簡體   English   中英

客戶端兼容性檢查管理

[英]Client compatibility check management

我正在開發必須支持向后兼容性的客戶端-服務器應用程序(.NET 4、WCF)。 換句話說,舊客戶端應該與新服務器兼容,反之亦然。 結果,我們的客戶端代碼中充斥着如下語句:

if (_serverVersion > new Version(2, 1, 3))
{
    //show/hide something or call method Foo()...
}
else
{
   //show/hide something or call method Foo2()...
}

顯然,這有點像維護的噩夢。 幸運的是,我們被允許打破與每個次要版本的向后兼容性。 當我們到達可以破壞兼容性的地步時,我想清理上面示例形式的代碼。

我的問題:

(1) 當這些代碼塊不再“有效”時,有沒有辦法輕松識別它們? 我最初的想法是以某種方式有條件地根據程序集的版本應用過時的屬性。 當我們進入一個新的次要版本時,Obsolete 屬性會“啟動”,突然間我們會有幾個編譯器警告指向這些代碼塊......有人做過這樣的事嗎? 或者有更好的方法來管理這個嗎?

(2) 每當我看到諸如new Version(2, 1, 3)之類的硬編碼版本時,我都會感到畏縮。 更糟糕的是,在開發過程中,我們並不知道發布的最終版本,因此當開發人員添加檢查時,版本檢查基於當前內部版本號 + 1。 雖然這有效,但它不是很干凈。 關於如何改進的任何想法?

謝謝!

我建議至少創建一個方法,您可以在其中執行如下邏輯:

public static class ServerUtilities
{
    public static bool IsValidToRun(Version desiredVersion)
    {
        if (_serverVersion >= desiredVersion)
            return true;
        else if (/* your other logic to determine if they're in some acceptable range */)
            return true;

        return false;
    }
}

然后,像這樣使用:

if (ServerUtilities.IsValidToRun(new Version(2, 1, 3)))
{
    // Do new logic
}
else
{
    // Do old logic
}

如果你需要集中版本,有一個 static 功能庫到版本映射,這樣你就可以調用:

if (ServerUtilities.IsValidToRun(ServerFeatures.FancyFeatureRequiredVersion))
{
    ...
}

public static class ServerFeatures
{
    public static Version FancyFeatureRequiredVersion
    {
        get { return new Version(2, 1, 3); }
    }
}

另一種方法是實施服務合同的版本控制:此時您可以利用 WCF 自己的功能來忽略不會破壞客戶端的微小更改,如版本控制策略頁面中所列。

在圖 1 中,您可以看到在向操作簽名添加新參數、從操作簽名中刪除參數以及添加新操作時,客戶端不受影響。

如果仍然存在重大更改或者您的客戶端必須支持兩個版本(如果我錯了請糾正我,因為我不知道您的部署策略),您可以在不同的端點上提供不同版本的服務並擁有 WCF客戶端代碼中的客戶端工廠,然后可以將其配置為返回適當端點的客戶端。

此時,您已經隔離了不同客戶端中的不同實現,這可能比現在更干凈,也更少維護噩夢。


用於澄清問題的非常基本的示例實現:假設我們有兩個不同的服務合同,一個舊合同和一個新合同。

[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/03")]
public interface IServiceOld
{
    [OperationContract]
    void DoWork();
}

[ServiceContract(Name = "Service", Namespace = "http://stackoverflow.com/2012/04")]
public interface IServiceNew
{
    [OperationContract]
    void DoWork();

    [OperationContract]
    void DoAdditionalWork();
}

請注意這兩個服務如何具有相同的名稱但不同的名稱空間。

讓我們來處理客戶端必須能夠同時支持擴展服務和新服務以及舊服務的問題。 假設我們想要在之前調用 DoWork 時調用 DoAdditionalWork 方法,並且我們想要在客戶端處理這種情況,因為假設 DoAdditionalWork 可能需要來自客戶端的一些額外的 arguments。 然后服務的配置可能是這樣的:

<service name="ConsoleApplication1.Service">
    <endpoint address="http://localhost:8732/test/new" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceNew" />
    <endpoint address="http://localhost:8732/test/old" binding="wsHttpBinding" contract="ConsoleApplication1.IServiceOld" />
    ...
</service>

好的,我們有服務端,現在是有趣的部分:我們想使用相同的接口與服務進行通信。 在這種情況下,我將使用舊的,但您可能需要在兩者之間放置一個適配器。 理想情況下,在我們的客戶端代碼中,我們會做這樣的事情:

IServiceOld client = *Magic*

client.DoWork();

在這種情況下,神奇的是一個像這樣的簡單工廠:

internal class ClientFactory
{
    public IServiceOld GetClient()
    {
        string service = ConfigurationManager.AppSettings["Service"];
        if(service == "Old")
            return new ClientOld();
        else if(service == "New")
            return new ClientNew();

        throw  new NotImplementedException();
    }
}

我將使用哪個客戶端的決定委托給了 app.config,但您可以在此處插入版本檢查。 ClientOld 的實現只是 IServiceOld 的常規 WCF 客戶端:

public class ClientOld : IServiceOld
{
    private IServiceOld m_Client;

    public ClientOld()
    {
        var factory = new ChannelFactory<IServiceOld>(new WSHttpBinding(), "http://localhost:8732/test/old");
        m_Client = factory.CreateChannel();
    }

    public void DoWork()
    {
        m_Client.DoWork();
    }

    ...
}

ClientNew 反而實現了我們希望的行為,即調用 DoAdditionalWork 操作:

public class ClientNew : IServiceOld
{
    private IServiceNew m_Client;

    public ClientNew()
    {
        var factory = new ChannelFactory<IServiceNew>(new WSHttpBinding(), "http://localhost:8732/test/new");
        m_Client = factory.CreateChannel();
    }

    public void DoWork()
    {
        m_Client.DoWork();
        m_Client.DoAdditionalWork();
    }
    ...
}

就是這樣,現在我們的客戶端可以像下面的例子一樣使用:

var client = new ClientFactory().GetClient();
client.DoWork();

我們取得了什么成就? 使用客戶端的代碼是從實際 WCF 客戶端必須執行的額外工作中抽象出來的,並且關於使用哪個客戶端的決定被委托給工廠。 我希望這個示例的一些變化/擴展適合您的需要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM