0

最近为了解决循环依赖,我们需要将枚举类移动到不同命名空间下的不同项目。有一些参与者和有状态服务将这个枚举值的实例保持在其可靠状态。

枚举类是这样的:

namespace com.libA
{
    public enum Foo
    {
        None = 0,
        Foo1 = 1,
        Foo2 = 2,
    }
}

我们想将其移动到另一个项目,其命名空间为com.libB. 这些枚举值存储在参与者和有状态服务中的可靠状态中,并按如下方式获取:

Foo foo = await this.StateManager.GetStateAsync<Foo>("FooKey").ConfigureAwait(false);

存储价值的演员服务之一Foo是非常长寿的演员。理论上,它可以在快乐的路径中存活到无穷大,并且永远不会从外部调用删除。我们尝试了简单的重构 > 移动并尝试了我们的非生产环境。这开始SerializationException在我们的非产品环境中引起。错误消息说: Expecting element 'Foo' from namespace 'http://schemas.datacontract.org/2004/07/com.libB'.. Encountered 'Element' with name 'Foo', namespace 'http://schemas.datacontract.org/2004/07/com.libA'.

这些异常发生在获取Foo旧演员的价值之前。

我的问题是:

  1. 我们如何移动Foo到命名空间com.libB?两阶段升级在这里会有帮助吗?
  2. 甚至可以在没有数据丢失/损坏的情况下这样做吗?
4

2 回答 2

1

您可以将DataContract带有命名空间的属性添加到您的类型。由于您已经在生产环境中运行,您可以使用错误中的命名空间来解决问题。

例子:

[DataContract(Name = "Foo", Namespace = "http://schemas.datacontract.org/2004/07/com.libA")]
public enum Foo
{
   // ...
}

更好的方法可能是制定升级计划。

  1. 让2种共存
  2. 检索状态时,有一个尝试机制,使用旧类型检索,如果失败并出现序列化异常,则使用新类型尝试。
  3. 持久化状态时,将其转换为新命名空间中的新类型,当它是旧类型时。(添加一些日志记录,以便您可以验证转换发生)
  4. 部署到测试,看看它是否工作,如果可以部署到生产环境
  5. 移除旧类型,移除转换代码
  6. 部署到测试,看看它是否有效,如果可以部署到生产环境。
于 2020-09-03T05:24:57.740 回答
1

一种选择是为所有使用的类型创建自定义序列化程序 包装 ,在反序列化期间修复/忽略命名空间。DataContractSerializerFoo

IReliableStateManager.TryAddStateSerializer 用于为给定类型 T 注册自定义序列化程序。此注册应发生在 StatefulServiceBase 的构造中,以确保在恢复开始之前,所有可靠集合都可以访问相关序列化程序以读取其持久数据。

  • IStateSerializer<OrderKey>.Read(BinaryReader reader)中,将序列化数据读取为 XML
  • 在需要的地方更改 XML 命名空间
  • 将 XML 提供DataContractSerializer给创建对象
  • 返回对象
于 2020-09-03T05:27:20.253 回答