服务结构:将枚举类移动到不同的项目



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

枚举类是这样的:

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

我们想将其转移到另一个名称空间为com.libB的项目中。这些枚举值存储在actor和有状态服务中的可靠状态中,并且如下所示获取:

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

存储Foo的值的actor服务之一是非常长寿命的actor。理论上,它可以在快乐的道路上活到无穷大,如果删除永远不会从外部调用的话。我们尝试了简单的重构>在我们的非生产环境中移动并尝试。这开始在我们的非生产环境中导致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. 是否有可能在没有数据丢失/损坏的情况下做到这一点

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

示例:

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

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

  1. 让这两种类型共存
  2. 检索状态时,请使用try机制,使用旧类型检索,如果由于序列化异常而失败,请使用新类型尝试
  3. 当持久化状态时,当它是旧类型时,将其转换为新命名空间中的新类型。(添加一些日志记录,以便验证是否发生了转换(
  4. 部署到测试,看看它是否有效,如果可以,部署到生产
  5. 删除旧类型,删除转换代码
  6. 部署到测试,看看它是否工作,如果可以,部署到生产

一个选项是为所有使用Foo的类型创建一个封装DataContractSerializer的自定义序列化程序,在反序列化过程中修复/忽略命名空间。

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

  • IStateSerializer<OrderKey>.Read(BinaryReader reader)中,将序列化的数据读取为XML
  • 根据需要更改XML命名空间
  • 将XML提供给DataContractSerializer以创建对象
  • 返回对象

最新更新