保持事件类名称的事件向上转换

Dav*_*her 4 neventstore

NEventStore 3.2.0.0

据我所知,NEventStore需要事件类型必须保留以进行事件上转换.
为了使它们在将来正确反序列化,它们必须具有唯一的名称.建议称之为EventEVENT_VERSION.

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

Dav*_*her 5

在很久很久以前的问题中,答案遗失了......

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

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

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

namespace CurrentEvents
{
    [Versioned("EventSomethingHappened", 0)] // still version 0
    public class EventSomethingHappened
    {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

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

当需要此事件的另一个版本时,将当前版本复制到"旧版"程序集中或仅复制到另一个命名空间中并重命名(type-name)为"EventSomethingHappenedV0" - 但Versioned-attribute保持不变(在此副本中)

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

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

namespace CurrentEvents
{
    [Versioned("EventSomethingHappened", 1)] // new version 1
    public class EventSomethingHappened
    {
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

Json.NET支持将类型标识符映射到类型和返回的绑定器.这是一个生产就绪的粘合剂:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

......和 ​​- 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

最后使用这样的版本化绑定器

JsonSerializer.Create(new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple,
    Binder = new VersionedSerializationBinder()
});
Run Code Online (Sandbox Code Playgroud)

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