我有一个类,我用它作为Rebus中的消息类型来发布/订阅,但是当我在LinqPad中尝试概念验证时遇到了一个障碍。我的应用程序收到的任何消息都以反序列化异常失败。我已经把问题缩小到牛顿软件公司了。并给出一个最小的示例来演示这个问题:
public class MyMessage
{
public string Name { get; set; } = "";
public int Number { get; set; }
}
void Main()
{
var message = new MyMessage { Name = "ABC", Number = 5 };
var defaultSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var serialized = JsonConvert.SerializeObject(message, defaultSettings);
Console.WriteLine(serialized);
try
{
var deserialized = JsonConvert.DeserializeObject(serialized, message.GetType(), defaultSettings);
Console.WriteLine(deserialized);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
如果我在Rider中编译它,这段代码作为控制台应用程序运行良好,并输出以下内容到控制台:
{"$type":"ConsoleApp1.MyMessage, ConsoleApp1","Name":"ABC","Number":5}
ConsoleApp1.MyMessage
同样的代码在LinqPad 6中失败,在具有JsonConvert.DeserializeObject
语句的行中出现以下错误消息:
{"$type":"UserQuery+MyMessage, LINQPadQuery","Name":"ABC","Number":5}
Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null'. Path '$type', line 1, position 44.
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ResolveTypeName(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, String qualifiedTypeName)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadMetadataProperties(JsonReader reader, Type& objectType, JsonContract& contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue, Object& newValue, String& id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonSerializer.Deserialize(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at UserQuery.Main() in C:UsersarcaaAppDataLocalTempLINQPad6_dwfnlfnztufqxfLINQPadQuery:line 19
异常消息:Type specified in JSON 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5, Culture=neutral, PublicKeyToken=null' is not compatible with 'UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61, Culture=neutral, PublicKeyToken=null
看起来好像序列化器试图将字符串反序列化为类型(UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.5
)的不同版本,而不是作为参数提供的类型(UserQuery+MyMessage, LINQPadQuery, Version=1.0.0.61
)。这里有趣的一点是,JsonConvert.DeserializeObject
方法中使用的类型与传递给序列化器的类型完全相同,并且该代码中只有一个类。
我不知道另一个类型的版本是从哪里来的。有人知道原因吗?如果可能的话,如何避免?
编辑:
我还注意到,如果我关闭LinqPad并再次打开它,代码工作正常。但是,如果我随后对代码进行更改(即使我只是添加一个空格),则会返回异常。
最有可能的是,在Newtonsoft中会有一些静态字典。缓存类型信息的Json库,键值为字符串,如"MyMessage"当您编辑查询并重新运行它时,LINQPad必须重新编译查询,因此在新的DLL中会有一个新版本的MyMessage
。然而,静态缓存在Newtonsoft。Json仍然会指向上次执行时使用的旧代码。
要解决这个问题,您不需要重新启动LINQPad;只需按Shift+F5
清除缓存的进程。或者,在查询中添加以下代码:
Util.NewProcess = true;
告诉LINQPad不要缓存进程以供后续使用。
在编辑|首选项>先进,永远不会回收过程。这会产生很小的性能成本;还有一些方法,比如LINQPad的Util。缓存-在执行之间缓存数据-将无法工作。