gpt4 book ai didi

neventstore - 保持事件类名称的事件上转换

转载 作者:行者123 更新时间:2023-12-05 01:08:07 25 4
gpt4 key购买 nike

NEventStore 3.2.0.0

据我发现,NEventStore 要求 旧的事件类型必须保留在 左右用于事件上转换。
为了让他们在 future 正确反序列化,他们 必须有唯一的名称 .建议使用 EventEVENT_VERSION .

有没有办法避免EventV1 , EventV2 ,..., EventVN 弄乱你的领域模型和继续使用Event ?
你的策略是什么?

最佳答案

在很久很久以前的一个问题中,缺少一个答案......

在评论中提到的讨论中,我想出了一个 - 我会说 - 优雅的解决方案:

不要保存类型名称,而是(版本化)标识符

标识符由类级别的属性设置,即

namespace CurrentEvents
{
[Versioned("EventSomethingHappened", 0)] // still version 0
public class EventSomethingHappened
{
...
}
}

此标识符应在有效负载中/旁边进行序列化。以序列化形式
“Some.Name.Space.EventSomethingHappened” -> “EventSomethingHappened|0”

当需要此事件的另一个版本时,当前版本将被复制到“旧”程序集或仅在另一个命名空间中并重命名(类型名称)为“EventSomethingHappenedV0” - 但 Versioned - 属性保持不变(在此副本中)

namespace LegacyEvents
{
[Versioned("EventSomethingHappened", 0)] // still version 0
public class EventSomethingHappenedV0
{
...
}
}

在新版本中(在同一个地方,在同一个名字下)只是属性的版本部分会增加。就是这样!

namespace CurrentEvents
{
[Versioned("EventSomethingHappened", 1)] // new version 1
public class EventSomethingHappened
{
...
}
}

Json.NET 支持将类型标识符映射到类型并返回的绑定(bind)器。这是一个生产就绪的活页夹:

public class VersionedSerializationBinder : DefaultSerializationBinder
{
private Dictionary<string, Type> _getImplementationLookup = new Dictionary<string, Type>();
private static Type[] _versionedEvents = null;

protected static Type[] VersionedEvents
{
get
{
if (_versionedEvents == null)
_versionedEvents = AppDomain.CurrentDomain.GetAssemblies()
.Where(x => x.IsDynamic == false)
.SelectMany(x => x.GetExportedTypes()
.Where(y => y.IsAbstract == false &&
y.IsInterface == false))
.Where(x => x.GetCustomAttributes(typeof(VersionedAttribute), false).Any())
.ToArray();

return _versionedEvents;
}
}

public VersionedSerializationBinder()
{

}

private VersionedAttribute GetVersionInformation(Type type)
{
var attr = type.GetCustomAttributes(typeof(VersionedAttribute), false).Cast<VersionedAttribute>().FirstOrDefault();

return attr;
}

public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
var versionInfo = GetVersionInformation(serializedType);
if (versionInfo != null)
{
var impl = GetImplementation(versionInfo);

typeName = versionInfo.Identifier + "|" + versionInfo.Revision;
}
else
{
base.BindToName(serializedType, out assemblyName, out typeName);
}

assemblyName = null;
}

private VersionedAttribute GetVersionInformation(string serializedInfo)
{
var strs = serializedInfo.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);

if (strs.Length != 2)
return null;

return new VersionedAttribute(strs[0], strs[1]);
}

public override Type BindToType(string assemblyName, string typeName)
{
if (typeName.Contains('|'))
{
var type = GetImplementation(GetVersionInformation(typeName));
if (type == null)
throw new InvalidOperationException(string.Format("VersionedEventSerializationBinder: No implementation found for type identifier '{0}'", typeName));
return type;
}
else
{
var versionInfo = GetVersionInformation(typeName + "|0");
if (versionInfo != null)
{
var type = GetImplementation(versionInfo);
if (type != null)
return type;
// else: continue as it is a normal serialized object...
}
}

// resolve assembly name if not in serialized info
if (string.IsNullOrEmpty(assemblyName))
{
Type type;
if (typeName.TryFindType(out type))
{
assemblyName = type.Assembly.GetName().Name;
}
}

return base.BindToType(assemblyName, typeName);
}

private Type GetImplementation(VersionedAttribute attribute)
{
Type eventType = null;

if (_getImplementationLookup.TryGetValue(attribute.Identifier + "|" + attribute.Revision, out eventType) == false)
{
var events = VersionedEvents
.Where(x =>
{
return x.GetCustomAttributes(typeof(VersionedAttribute), false)
.Cast<VersionedAttribute>()
.Where(y =>
y.Revision == attribute.Revision &&
y.Identifier == attribute.Identifier)
.Any();
})
.ToArray();

if (events.Length == 0)
{
eventType = null;
}
else if (events.Length == 1)
{
eventType = events[0];
}
else
{
throw new InvalidOperationException(
string.Format("VersionedEventSerializationBinder: Multiple types have the same VersionedEvent attribute '{0}|{1}':\n{2}",
attribute.Identifier,
attribute.Revision,
string.Join(", ", events.Select(x => x.FullName))));
}
_getImplementationLookup[attribute.Identifier + "|" + attribute.Revision] = eventType;
}
return eventType;
}
}

...和 ​​ Versioned -属性

[AttributeUsage(AttributeTargets.Class)]
public class VersionedAttribute : Attribute
{
public string Revision { get; set; }
public string Identifier { get; set; }

public VersionedAttribute(string identifier, string revision = "0")
{
this.Identifier = identifier;
this.Revision = revision;
}

public VersionedAttribute(string identifier, long revision)
{
this.Identifier = identifier;
this.Revision = revision.ToString();
}
}

最后像这样使用版本化的活页夹

JsonSerializer.Create(new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
Binder = new VersionedSerializationBinder()
});

有关完整的 Json.NET ISerialize 实现,请参见(有点过时的)要点:
https://gist.github.com/warappa/6388270

关于neventstore - 保持事件类名称的事件上转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17521153/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com