EasyNetQ 不同框架序列化反序列化问题

 

当.net core 项目给.net framework 用easynetq发送信息的时候。出现异常,例如以string对象为例。抛出异常:“Could not load assembly 'System.Private.CoreLib'”

因为:.net core 的string 对象是在System.Private.CoreLib程序集下,System.String命名空间下;而.net framework的string对象是在mscorlib程序集下,System.String命名空间下

easynetq反序列化对象前,会拿到当前对象的类型,拿到类型之后会从对应的程序集去加载,而.net framework当然没有System.Private.CoreLib.dll,所以失败。

解决办法:替换ITypeNameSerializer组件  官方替换各种组件方法

源码地址

关键源码(版本6.3.1):

  private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
        {
            var assemblyName = typeNameKey.AssemblyName;
            var typeName = typeNameKey.TypeName;

            if (assemblyName != null)
            {
                var assembly = Assembly.Load(new AssemblyName(assemblyName));
                if (assembly == null)
                {
                    throw new EasyNetQException($"Could not load assembly '{assemblyName}'");
                }

                var type = assembly.GetType(typeName);
                if (type == null)
                {
                    // if generic type, try manually parsing the type arguments for the case of dynamically loaded assemblies
                    // example generic typeName format: System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
                    if (typeName.IndexOf('`') >= 0)
                    {
                        try
                        {
                            type = GetGenericTypeFromTypeName(typeName, assembly);
                        }
                        catch (Exception ex)
                        {
                            throw new EasyNetQException($"Could not find type '{typeName}' in assembly '{assembly.FullName}'", ex);
                        }
                    }

                    if (type == null)
                    {
                        throw new EasyNetQException($"Could not find type '{typeName}' in assembly '{assembly.FullName}'");
                    }
                }

                return type;
            }

            return Type.GetType(typeName);
        }
View Code

只要在源码中做兼容代码就行。

下面是兼容代码整个类(其实只是增加了一个if判断,暂时可以这样写,以后类型多了不好这样写),以及easynetq组件替换方式。

    public class MyTypeNameSerializer : ITypeNameSerializer
    {
        private readonly ConcurrentDictionary<Type, string> serializedTypes = new ConcurrentDictionary<Type, string>();
        private readonly ConcurrentDictionary<string, Type> deSerializedTypes = new ConcurrentDictionary<string, Type>();

        /// <inheritdoc />
        public string Serialize(Type type)
        {

            return serializedTypes.GetOrAdd(type, t =>
            {
                var typeName = RemoveAssemblyDetails(t.AssemblyQualifiedName);
                if (typeName.Length > 255)
                {
                    throw new EasyNetQException($"The serialized name of type '{t.Name}' exceeds the AMQP maximum short string length of 255 characters");
                }
                return typeName;
            });
        }

        /// <inheritdoc />
        public Type DeSerialize(string typeName)
        {
            return deSerializedTypes.GetOrAdd(typeName, t =>
            {
                var typeNameKey = SplitFullyQualifiedTypeName(t);
                return GetTypeFromTypeNameKey(typeNameKey);
            });
        }

        private static string RemoveAssemblyDetails(string fullyQualifiedTypeName)
        {
            var builder = new StringBuilder(fullyQualifiedTypeName.Length);

            // loop through the type name and filter out qualified assembly details from nested type names
            var writingAssemblyName = false;
            var skippingAssemblyDetails = false;
            foreach (var character in fullyQualifiedTypeName)
            {
                switch (character)
                {
                    case '[':
                        writingAssemblyName = false;
                        skippingAssemblyDetails = false;
                        builder.Append(character);
                        break;
                    case ']':
                        writingAssemblyName = false;
                        skippingAssemblyDetails = false;
                        builder.Append(character);
                        break;
                    case ',':
                        if (!writingAssemblyName)
                        {
                            writingAssemblyName = true;
                            builder.Append(character);
                        }
                        else
                        {
                            skippingAssemblyDetails = true;
                        }
                        break;
                    default:
                        if (!skippingAssemblyDetails)
                        {
                            builder.Append(character);
                        }
                        break;
                }
            }

            return builder.ToString();
        }

        private static TypeNameKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
        {
            var assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);

            string typeName;
            string assemblyName;

            if (assemblyDelimiterIndex != null)
            {
                typeName = fullyQualifiedTypeName.Trim(0, assemblyDelimiterIndex.GetValueOrDefault());
                assemblyName = fullyQualifiedTypeName.Trim(assemblyDelimiterIndex.GetValueOrDefault() + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.GetValueOrDefault() - 1);
            }
            else
            {
                typeName = fullyQualifiedTypeName;
                assemblyName = null;
            }

            return new TypeNameKey(assemblyName, typeName);
        }

        private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
        {
            var assemblyName = typeNameKey.AssemblyName;
            var typeName = typeNameKey.TypeName;

            if (assemblyName == "System.Private.CoreLib" && typeName == "System.String")
            {
                return typeof(string);
            }

            if (assemblyName != null)
            {
                //#if NETFX
                //                // look, I don't like using obsolete methods as much as you do but this is the only way
                //                // Assembly.Load won't check the GAC for a partial name
                //#pragma warning disable 618,612
                var assembly = Assembly.LoadWithPartialName(assemblyName);
                //#pragma warning restore 618,612
                //#else
                //                var assembly = Assembly.Load(new AssemblyName(assemblyName));
                //#endif

                //#if NETFX
                if (assembly == null)
                {
                    // will find assemblies loaded with Assembly.LoadFile outside of the main directory
                    var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
                    foreach (var a in loadedAssemblies)
                    {
                        // check for both full name or partial name match
                        if (a.FullName == assemblyName || a.GetName().Name == assemblyName)
                        {
                            assembly = a;
                            break;
                        }
                    }
                }
                //#endif
                if (assembly == null)
                {
                    throw new EasyNetQException($"Could not load assembly '{assemblyName}'");
                }

                var type = assembly.GetType(typeName);
                if (type == null)
                {
                    // if generic type, try manually parsing the type arguments for the case of dynamically loaded assemblies
                    // example generic typeName format: System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
                    if (typeName.IndexOf('`') >= 0)
                    {
                        try
                        {
                            type = GetGenericTypeFromTypeName(typeName, assembly);
                        }
                        catch (Exception ex)
                        {
                            throw new EasyNetQException($"Could not find type '{typeName}' in assembly '{assembly.FullName}'", ex);
                        }
                    }

                    if (type == null)
                    {
                        throw new EasyNetQException($"Could not find type '{typeName}' in assembly '{assembly.FullName}'");
                    }
                }

                return type;
            }

            return Type.GetType(typeName);
        }

        private static Type GetGenericTypeFromTypeName(string typeName, Assembly assembly)
        {
            Type type = null;
            var openBracketIndex = typeName.IndexOf('[');
            if (openBracketIndex >= 0)
            {
                var genericTypeDefName = typeName.Substring(0, openBracketIndex);
                var genericTypeDef = assembly.GetType(genericTypeDefName);
                if (genericTypeDef != null)
                {
                    var genericTypeArguments = new List<Type>();
                    var scope = 0;
                    var typeArgStartIndex = 0;
                    var endIndex = typeName.Length - 1;
                    for (var i = openBracketIndex + 1; i < endIndex; ++i)
                    {
                        var current = typeName[i];
                        switch (current)
                        {
                            case '[':
                                if (scope == 0)
                                {
                                    typeArgStartIndex = i + 1;
                                }
                                ++scope;
                                break;
                            case ']':
                                --scope;
                                if (scope == 0)
                                {
                                    var typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex);
                                    var typeNameKey = SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName);
                                    genericTypeArguments.Add(GetTypeFromTypeNameKey(typeNameKey));
                                }
                                break;
                        }
                    }

                    type = genericTypeDef.MakeGenericType(genericTypeArguments.ToArray());
                }
            }

            return type;
        }

        private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
        {
            // we need to get the first comma following all surrounded in brackets because of generic types
            // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
            var scope = 0;
            for (var i = 0; i < fullyQualifiedTypeName.Length; i++)
            {
                var current = fullyQualifiedTypeName[i];
                switch (current)
                {
                    case '[':
                        scope++;
                        break;
                    case ']':
                        scope--;
                        break;
                    case ',':
                        if (scope == 0)
                        {
                            return i;
                        }
                        break;
                }
            }

            return null;
        }

        private readonly struct TypeNameKey
        {
            public string AssemblyName { get; }
            public string TypeName { get; }

            public TypeNameKey(string assemblyName, string typeName)
            {
                AssemblyName = assemblyName;
                TypeName = typeName;
            }
        }
    }
View Code
 var bus = RabbitHutch.CreateBus(connectString, serviceRegister => serviceRegister.Register<ITypeNameSerializer, MyTypeNameSerializer>());

这样就结束了。

posted @ 2021-04-07 15:23  TeemoHQ  阅读(311)  评论(0编辑  收藏  举报