.Net Standard 4.7.1 在序列化期间无法加载 System.Private.CoreLib

.Net Standard 4.7.1 Could not load System.Private.CoreLib during serialization

我正在努力将我的大部分项目迁移到 .Net Standard 2.0。

.net 标准 4.7.1 中项目的最后一部分是 WCF 前端。 此可执行文件通过 ClientServiceReceptionist 的库 Akka.net 与 .net 核心应用程序通信。

应用程序之间的网络通信由 akka.net 工作。 除非它尝试序列化 ReadOnlyCollection。

在这种情况下,系统尝试加载 "System.Private.CoreLib.dll" 不可用。

我读到了许多关于 .net 4.6 使用 .net 标准 2.0 库的问题,这些问题必须在 4.7.1 中得到纠正。我从 package.config 传递到 PackageReference.

我尝试使用 NewtonSoft 作为序列化程序来代替 Hyperion,但没有任何进展。

有没有人有想法,解决方案?

编辑:2018 年 7 月 5 日

当我通过 ClusterClientReceptionist 发送一个 ClusterClient.Send 对象时,问题出现在 WCF 入口点中。

发送的对象仅包含布尔值、字符串、Guid 和字符串属性数组。

编辑 08-05-2018

发送的对象如下所示:

{
  (string) "Path": "/user/ProcessOrderWorkflow",
  "Message": {
    "Order": {
      "Data": {
        (string)"Sentence": "i love my name",
        "Options": {
            (boolean)"Simplify" : true,
            (IReadOnlyCollection<string>) LanguageAffinity : [ "FR", "EN" ]
        }
      },
      "OrderQuery": {
        "Verb": {
          (string)"Verb": "Extract"
        },
        "Arguments": [
          {
            (string)"Argument": "Sentence"
          },
          {
            (string)"Argument": "Meaning"
          }
        ]
      },
      (Guid)"ProcessId": "0bfef4a5-c8a4-4816-81d1-6f7bf1477f65"
    },
    (string)"ReturnTypeFullName": "Viki.Api.Server.Data.SentenceMeaningMsg,, Viki.Api.Server.Data, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  },
  (boolean)"LocalAffinity": false
}

层次结构中使用的每个对象都是使用构造函数构建的。 所有属性都是只读的。

我尝试在发送之前在 WCF 部件上对结果进行序列化和反序列化,结果成功了。

var serializer = this._system.Serialization.FindSerializerFor(result);
var bytes = serializer.ToBinary(result);
var data = serializer.FromBinary(bytes, result.GetType());

奇怪的是,它试图在发送对象的 WCF 部分反序列化对象,而不是在应该接收元素并将其传输到代理进行处理的 LightHouse 中反序列化。

所以这就是我认为的问题......问题是使用 JSON.NET 或 Hyperion 等多态序列化程序在 .NET Core 应用程序和 .NET Framework 应用程序之间传输序列化内容是至少在 Akka.NET 中不受支持的操作,但在用户使用 CLR 类型作为消息的有线格式的任何其他运行时中也是如此。

关于why we don't support this in Akka.NET, from the v1.3.0 release notes的简要说明:

As a side note: Akka.NET on .NET 4.5 is not wire compatible with Akka.NET on .NET Core; this is due to fundamental changes made to the base types in the CLR on .NET Core. It's a common problem facing many different serializers and networking libraries in .NET at the moment. You can use a X-plat serializer we've developed here: #2947 - please comment on that thread if you're considering building hybrid .NET and .NET Core clusters.

在这种情况下,"fundamental changes" 是 类型的命名空间,如 string 在 .NET 4.* 和 .NET Core 上是不同的,因此不考虑这些差异的多态序列化程序将抛出此类异常。

如果您想使用,我们确实有可用的解决方法:

https://github.com/akkadotnet/akka.net/pull/2947

您需要在 Lighthouse 和您的 .NET 4.7.1 应用程序中使用 JSON.NET 注册该序列化器兼容层。

这是请求“无法加载 System.Private.CoreLib”的最佳结果,因此我 post ASP.NET Core APIs 和 .NET 客户端的解决方法。

如果 json 序列化器类型处理设置为自动

settings.TypeNameHandling = TypeNameHandling.Auto;

序列化器将包含多态类型的类型信息。迁移到 .NET Core 后,一些客户端报告异常,响应正文包含以下类型描述符:

"$type":"System.String[], System.Private.CoreLib"

在 API 模型中,属性 类型被定义为 IEnumerable<string>,它强制序列化程序包含数组的实际类型。解决方案是将 IEnumerable<string> 替换为 string[] 或任何其他允许序列化程序省略类型描述符的具体类型。

如果上述方法不起作用,例如当您使用 Dictionary<string, object> 时,您可以实现自定义 SerializationBinder:

public class NetCoreSerializationBinder : DefaultSerializationBinder
{
    private static readonly Regex regex = new Regex(
        @"System\.Private\.CoreLib(, Version=[\d\.]+)?(, Culture=[\w-]+)(, PublicKeyToken=[\w\d]+)?");

    private static readonly ConcurrentDictionary<Type, (string assembly, string type)> cache =
        new ConcurrentDictionary<Type, (string, string)>();

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        base.BindToName(serializedType, out assemblyName, out typeName);

        if (cache.TryGetValue(serializedType, out var name))
        {
            assemblyName = name.assembly;
            typeName = name.type;
        }
        else
        {
        if (assemblyName.AsSpan().Contains("System.Private.CoreLib".AsSpan(), StringComparison.OrdinalIgnoreCase))
            assemblyName = regex.Replace(assemblyName, "mscorlib");

        if (typeName.AsSpan().Contains("System.Private.CoreLib".AsSpan(), StringComparison.OrdinalIgnoreCase))
            typeName = regex.Replace(typeName, "mscorlib");    
            cache.TryAdd(serializedType, (assemblyName, typeName));
        }
    }
}

并在JsonSerializerSettings中注册:

settings.SerializationBinder = new NetCoreSerializationBinder();

注意:添加了 .AsSpan() 用于字符串比较是为了向后兼容 .NET Framework。它没有坏处,但 .NET Core 3.1+ 不需要,请随意丢弃它。

对于基本类型尝试使用

serializer.TypeNameHandling = TypeNameHandling.Auto

可以忽略 Json

中的 System.Private.CoreLib 命名空间

如果在 .NET Framework 应用程序中反序列化期间发生此问题,并且您无法对收到的 JSON 执行任何操作,那么您可以扩展 DefaultSerializationBinder 并在 [=13= 中使用它] 反序列化 .NET Core 应用程序生成的 JSON:

internal sealed class DotNetCompatibleSerializationBinder : DefaultSerializationBinder
{
    private const string CoreLibAssembly = "System.Private.CoreLib";
    private const string MscorlibAssembly = "mscorlib";

    public override Type BindToType(string assemblyName, string typeName)
    {
        if (assemblyName == CoreLibAssembly)
        {
            assemblyName = MscorlibAssembly;
            typeName = typeName.Replace(CoreLibAssembly, MscorlibAssembly);
        }

        return base.BindToType(assemblyName, typeName);
    }
}

然后:

var settings = new JsonSerializerSettings()
{
    SerializationBinder = new DotNetCompatibleSerializationBinder()
};