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

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

问题描述

上下文是如下

  1. 我想通过移动到不同的项目来重构code
  2. 在其中的一些code包含了用于序列化的DTO的 发送和接收跨多个端点的数据
  3. 如果我将code左右,系列化中断(因此不 向下兼容旧版本的我的应用程序)
  4. 兼容

一个解决这个问题是SerializationBinder,让我在一定意义上从一种类型重定向到另一个地方。

因此​​,我想创建一个SerializationBinder,以满足这一需求。然而,它必须通过满足以下要求这样做

  1. 的投入到SerializationBinder应该是老机型的列表 新的类型映射。映射应该包括老总成 名字(没有版本,没有公钥标记),以及在旧的全名 类型(名称空间和名称),以及新的程序集的名称和新 该类型的全名
  2. 对于在输入的类型,组件的版本号应该被忽略
  3. 应该处理的仿制药,如果我的类型刚好在仿制药 (列表,字典等),而不需要包括仿制药在输入
  4. 对于任何不在输入(即有没有类型 移动或.NET类型,如数据集为例)应该默认为 采用了二进制序列的盒子算法

这是可能的还是我在做梦?有什么在那里,已经这样做了?我认为这是一个常见的​​问题。

到目前为止,我看到做3没有简单的方法,也没有办法都没有做4。

下面是一个尝试

 公共类SmartDeserializationBinder:SerializationBinder
{
    ///<总结>
    ///专用类来处理存储类型映射
    ///< /总结>
    私有类TypeMapping
    {
        公共字符串OldAssemblyName {获得;组; }
        公共字符串OldTypeName {获得;组; }
        公共字符串NewAssemblyName {获得;组; }
        公共字符串NewTypeName {获得;组; }
    }

    名单< TypeMapping> typeMappings;

    公共SmartDeserializationBinder()
    {
        typeMappings =新的名单,其中,TypeMapping>();
    }

    公共无效AddTypeMapping(字符串oldAssemblyName,串oldTypeName,串newAssemblyName,串newTypeName)
    {
        typeMappings.Add(新TypeMapping()
        {
            OldAssemblyName = oldAssemblyName,
            OldTypeName = oldTypeName,
            NewAssemblyName = newAssemblyName,
            NewTypeName = newTypeName
        });
    }

    公众覆盖类型BindToType(字符串的AssemblyName,字符串的typeName)
    {
        //需要处理的事实的AssemblyName将要与版本,而输入类型的映射可能不
        //需要处理的事实,而不是其中类型被定义的汇编泛型进来作为mscorlib程序组件。
        //需要处理某些类型甚至不会被映射定义的事实。在这种情况下,我们应恢复正常绑定...你怎么做呢?

        字符串alternateAssembly = NULL;
        字符串alternateTypeName = NULL;
        布尔needToMap = FALSE;
        的foreach(在typeMappings TypeMapping映射)
        {
            如果(typeName.Contains(mapping.OldTypeName))
            {
                alternateAssembly = mapping.NewAssemblyName;
                alternateTypeName = mapping.NewTypeName;
                needToMap = TRUE;
                打破;
            }
        }

        如果(needToMap)
        {
            布尔isList = FALSE;
            如果(typeName.Contains(List`1))
                isList = TRUE;
            //其他仿制药需要去这里

            如果(isList)
                返回Type.GetType(的String.Format(System.Collections.Generic.List`1 [[{0},{1}]],alternateTypeName,alternateAssembly));
            其他
                返回Type.GetType(的String.Format({0},{1},alternateTypeName,alternateAssembly));
        }
        其他
            返回null; //这似乎这样的伎俩二进制序列化,但我不知道是否应该工作
    }
}
 

解决方案

这可以工作(而不是你的覆盖)。

 公众覆盖类型BindToType(字符串的AssemblyName,字符串的typeName)
        {
            变种米= Regex.Match(typeName的,@^(小于?根> [^ \ [] +)\ [\ [(小于α型> [^ \]] *)\](,\ [(? <类型> [^ \] *)\])* \] $);
            如果(m.Success)
            {//泛型类型
                VAR根= GetFlatTypeMapping(m.Groups [根]值。);
                VAR genArgs = m.Groups [型]
                    .Captures
                    .Cast<捕获>()
                    。选择(C =>
                        {
                            变种平方米= Regex.Match(c.Value,@^(小于?TNAME> *。)(小于?aname>([^,] +){4})$);
                            返回BindToType(m2.Groups [aname] Value.Substring(1).Trim(),m2.Groups [TNAME] Value.Trim());
                        })
                    .ToArray();
                返回gen.MakeGenericType(genArgs);
            }
            返回GetFlatTypeMapping(的AssemblyName,typeName的);
        }
 

然后你只需要实现自己的方式在功能GetFlatTypeMapping(不令人担忧的有关通用参数)。

你将要做的就是回到的typeof(名单<>) typeof运算(字典<,>)(或任何其他一般你想使用),当记者问。

注:我说的typeof(名单<>)!没有的typeof(名单<东西>)。 ...这是很重要

免责声明:;列表<字符串>&GT,因为正则表达式(?[^] *),该文档片断不支持嵌套泛型类型,如名单,其中的; ...你将不得不调整它有点以支持它!

The context is as follows

  1. I want to refactor code by moving it around to different projects
  2. Some of this code comprises of serializable DTOs that are used to send and receive data across multiple endpoints
  3. If I move the code around, serialization breaks (therefore it is not backward compatible with older versions of my application)

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

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

  1. The inputs to the SerializationBinder should be a list of old type to new type mappings. The mapping should include the old assembly name (no version, no public key token) and the old full name of the type (namespace and name) as well as the new assembly name and new full name of the type
  2. For the types that are in the inputs, version numbers of assemblies should be ignored
  3. It should handle generics if my types happen to be in generics (List, Dictionary, etc) without needing to include the generics in the inputs
  4. For anything that is not in the inputs (i.e. types that have not moved or .NET types like dataset for example) it should default to using the out of the box algorithm of the binary serializer

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

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

Here is an attempt

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);
        }

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

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: I said typeof(List<>) ! not typeof(List<something>) ... that's important.

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天全站免登陆