Share via


回溯相容性指導方針

撰寫回溯相容程式碼可能很難且難以測試。 本文討論在 .NET Orleans 中撰寫回溯相容程式碼的指導方針。 本文涵蓋 VersionAttributeObsoleteAttribute 的使用方式。

永不變更現有方法的簽章

由於 Orleans 序列化程式的運作方式,您絕對不應變更現有方法的簽章。

正確範例如下:

[Version(1)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // First method
    Task MyMethod(int arg);
}
[Version(2)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // Method inherited from V1
    Task MyMethod(int arg);

    // New method added in V2
    Task MyNewMethod(int arg, obj o);
}

這是不正確的:

[Version(1)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // First method
    Task MyMethod(int arg);
}
[Version(2)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // Method inherited from V1
    Task MyMethod(int arg, obj o);
}

重要

您不應該在程式碼中進行這項變更,因為這是會導致不良副作用的不良做法範例。 這是當您只重新命名參數名稱時,可能發生的情況範例:假設我們在叢集中部署了下列兩個介面版本:

[Version(1)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // return a - b
    Task<int> Subtract(int a, int b);
}
[Version(2)]
public interface IMyGrain : IGrainWithIntegerKey
{
    // return a - b
    Task<int> Subtract(int b, int a);
}

此方法看起來完全相同。 但是,如果使用 V1 呼叫用戶端,且要求是由 V2 啟用處理:

var grain = client.GetGrain<IMyGrain>(0);
var result = await grain.Subtract(5, 4); // Will return "-1" instead of expected "1"

這是因為內部 Orleans 序列化程式的運作方式。

避免變更現有的方法邏輯

這似乎顯而易見,但在變更現有方法的本文時,您應該要非常小心。 除非您正在修正錯誤 (bug),否則若有修改程式碼的需求,您最好直接新增方法。

範例:

// V1
public interface MyGrain : IMyGrain
{
    // First method
    Task MyMethod(int arg)
    {
        SomeSubRoutine(arg);
    }
}
// V2
public interface MyGrain : IMyGrain
{
    // Method inherited from V1
    // Do not change the body
    Task MyMethod(int arg)
    {
        SomeSubRoutine(arg);
    }

    // New method added in V2
    Task MyNewMethod(int arg)
    {
        SomeSubRoutine(arg);
        NewRoutineAdded(arg);
    }
}

請勿從 Grain 介面中移除方法

除非您確定不再使用這些方法,否則您不應該從 Grain 介面中移除方法。 如果您想要移除方法,這應該會在 2 個步驟中完成:

  1. 部署 V2 Grain,並將 V1 方法標示為 Obsolete

    [Version(1)]
    public interface IMyGrain : IGrainWithIntegerKey
    {
        // First method
        Task MyMethod(int arg);
    }
    
    [Version(2)]
    public interface IMyGrain : IGrainWithIntegerKey
    {
        // Method inherited from V1
        [Obsolete]
        Task MyMethod(int arg);
    
        // New method added in V2
        Task MyNewMethod(int arg, obj o);
    }
    
  2. 當您確定沒有任何 V1 呼叫時 (V1 實際上不會再部署在執行中的叢集中),請部署 V3 並移除 V1 方法

    [Version(3)]
    public interface IMyGrain : IGrainWithIntegerKey
    {
        // New method added in V2
        Task MyNewMethod(int arg, obj o);
    }