向后兼容性指南
编写向后兼容的代码可能很难,并且难以测试。
永远不要更改现有方法的签名
由于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> Substract(int a, int b);
}
[Version(2)]
public interface IMyGrain : IGrainWithIntegerKey
{
// return y - x
Task<int> Substract(int y, int x);
}
这种方法似乎相同。但是如果使用V1调用客户端,并且请求由V2的激活体来处理:
var grain = client.GetGrain<IMyGrain>(0);
var result = await grain.Substract(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接口中删除方法。如果要删除方法,则应分两步完成:
-
部署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); }
-
如果您确定没有进行V1调用(实际上V1不再部署在正在运行的集群中),在删除v1方法的情况下部署v3。
[Version(3)] public interface IMyGrain : IGrainWithIntegerKey { // New method added in V2 Task MyNewMethod(int arg, obj o); }