如何为二进制格式化程序创建一个 SerializationBinder 来处理类型从一个程序集和命名空间到另一个程序集的移动 [英] How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another

查看:16
本文介绍了如何为二进制格式化程序创建一个 SerializationBinder 来处理类型从一个程序集和命名空间到另一个程序集的移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上下文如下

  1. 我想通过将代码移动到不同的项目来重构代码
  2. 其中一些代码由可序列化的 DTO 组成,这些 DTO 用于跨多个端点发送和接收数据
  3. 如果我移动代码,序列化会中断(因此它不是向后兼容我的应用程序的旧版本)

这个问题的一个解决方案是 SerializationBinder,它允许我从某种意义上重定向"到另一种类型.

A solution to this problem is the SerializationBinder which allows me to "redirect" in a sense from one type to another.

因此我想创建一个 SerializationBinder 来满足这个需求.但是它必须满足以下要求

I therefore want to create a SerializationBinder to meet this need. However it must do so by meeting the following requirements

  1. SerializationBinder 的输入应该是旧类型的列表到新的类型映射.映射应包括旧程序集名称(无版本,无公钥令牌)和旧的全名类型(命名空间和名称)以及新的程序集名称和新类型的全名
  2. 对于输入中的类型,应忽略程序集的版本号
  3. 如果我的类型碰巧是泛型,它应该处理泛型(列表、字典等)而无需在输入中包含泛型
  4. 对于任何不在输入中的东西(即没有移动或 .NET 类型(例如数据集)它应该默认为使用二进制序列化器的开箱即用算法

这是可能的还是我在做梦?有什么东西已经做到了?我认为这是一个常见问题.

Is this possible or am I dreaming? Is there something out there that already does this? I would assume this is a common problem.

到目前为止,我认为没有简单的方法可以做 3,也没有办法做 4.

So far I see no easy way of doing 3 and no way at all of doing 4.

这是一个尝试

public class SmartDeserializationBinder : SerializationBinder
{
    /// <summary>
    /// Private class to handle storing type mappings
    /// </summary>
    private class TypeMapping
    {
        public string OldAssemblyName { get; set; }
        public string OldTypeName { get; set; }
        public string NewAssemblyName { get; set; }
        public string NewTypeName { get; set; }
    }

    List<TypeMapping> typeMappings;

    public SmartDeserializationBinder()
    {
        typeMappings = new List<TypeMapping>();
    }

    public void AddTypeMapping(string oldAssemblyName, string oldTypeName, string newAssemblyName, string newTypeName)
    {
        typeMappings.Add(new TypeMapping()
        {
            OldAssemblyName = oldAssemblyName,
            OldTypeName = oldTypeName,
            NewAssemblyName = newAssemblyName,
            NewTypeName = newTypeName
        });
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        //Need to handle the fact that assemblyName will come in with version while input type mapping may not
        //Need to handle the fact that generics come in as mscorlib assembly as opposed to the assembly where the type is defined.
        //Need to handle the fact that some types won't even be defined by mapping. In this case we should revert to normal Binding... how do you do that?

        string alternateAssembly = null;
        string alternateTypeName = null;
        bool needToMap = false;
        foreach (TypeMapping mapping in typeMappings)
        {
            if (typeName.Contains(mapping.OldTypeName))
            {
                alternateAssembly = mapping.NewAssemblyName;
                alternateTypeName = mapping.NewTypeName;
                needToMap = true;
                break;
            }
        }

        if (needToMap)
        {
            bool isList = false;
            if (typeName.Contains("List`1"))
                isList = true;
            // other generics need to go here

            if (isList)
                return Type.GetType(String.Format("System.Collections.Generic.List`1[[{0}, {1}]]", alternateTypeName, alternateAssembly));
            else
                return Type.GetType(String.Format("{0}, {1}", alternateTypeName, alternateAssembly));
        }
        else
            return null; // this seems to do the trick for binary serialization, but i'm not sure if it is supposed to work
    }
}

推荐答案

这可以工作(而不是您的覆盖).

This could work (instead of your override).

public override Type BindToType(string assemblyName, string typeName)
        {
            var m = Regex.Match(typeName, @"^(?<gen>[^[]+)[[(?<type>[^]]*)](,[(?<type>[^]]*)])*]$");
            if (m.Success)
            { // generic type
                var gen = GetFlatTypeMapping(m.Groups["gen"].Value);
                var genArgs = m.Groups["type"]
                    .Captures
                    .Cast<Capture>()
                    .Select(c =>
                        {
                            var m2 = Regex.Match(c.Value, @"^(?<tname>.*)(?<aname>(,[^,]+){4})$");
                            return BindToType(m2.Groups["aname"].Value.Substring(1).Trim(), m2.Groups["tname"].Value.Trim());
                        })
                    .ToArray();
                return gen.MakeGenericType(genArgs);
            }
            return GetFlatTypeMapping(assemblyName,typeName);
        }

然后你只需要按照你的方式实现函数 GetFlatTypeMapping(不用担心泛型参数).

Then you just have to implement your way the function GetFlatTypeMapping (not worrying of about generic arguments).

您需要做的是返回 typeof(List<>)typeof(Dictionary<,>)(或您想要的任何其他泛型使用)当被问到时.

What you will have to do is to return typeof(List<>) and typeof(Dictionary<,>) (or any other generic you would like to use) when asked.

nb:我说的是 typeof(List<>) !不是 typeof(List) ... 这很重要.

nb: I said typeof(List<>) ! not typeof(List<something>) ... that's important.

免责声明:由于正则表达式(?[^]]*)",这个片段不支持嵌套的泛型类型,如List> ...你必须稍微调整一下才能支持它!

disclaimer: because of the regex "(?[^]]*)", this snipped does not support nested generic types like List<List<string>> ... you will have to tweak it a bit to support it !

这篇关于如何为二进制格式化程序创建一个 SerializationBinder 来处理类型从一个程序集和命名空间到另一个程序集的移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆