我已经编写了一个小的异步库包装器来使用NuGet中的Json.Net v5.06,并在更新了一些Telerik包后,我的UnitTests开始出现MissingMethodException错误。
我创建了一个虚拟的非异步方法,它可以正常工作,所以我很困惑错误出在哪里。
项目目标平台:
.Net 4.5
x86 CPU
异步操作
我创建了一个虚拟的非异步方法,它可以正常工作,所以我很困惑错误出在哪里。
项目目标平台:
.Net 4.5
x86 CPU
异步操作
public class JsonSerialiser : ISerialiser
{
[InjectionConstructor]
public JsonSerialiser(IStringCompression streamCompressor, ILog logger)
{
if (streamCompressor == null) throw new ArgumentNullException("streamCompressor");
if (logger == null) throw new ArgumentNullException("logger");
XmlConfigurator.Configure();
this.streamCompressor = streamCompressor;
this.logger = logger;
}
public async Task<string> SerialiseAsync<T>(T serialseObject) where T : class
{
if (serialseObject == null) throw new ArgumentNullException("serialseObject");
try
{
return await JsonConvert.SerializeObjectAsync(serialseObject);
}
catch (JsonSerializationException ex)
{
logger.Error(ex);
throw new SerialisationException("Could Not Serialse The Object", ex);
}
}
}
异步示例
现在这段代码只是为了测试基本的序列化,其中我跳过了类构造函数中的空值检查。
private async void button1_Click(object sender, EventArgs e)
{
List<Part> parts = new List<Part> { new Part() { AbstractType = typeof(IOpcController), ConcreteType = typeof(OpcController) },
new Part() { AbstractType = typeof(ISerialiser), ConcreteType = typeof(JsonSerialiser) },
new Part() { AbstractType = typeof(IStringCompression), ConcreteType = typeof(StringGZipCompression)}};
string serialisedResult = string.Empty;
JsonSerialiser serialiser = new JsonSerialiser(null, null);
serialisedResult = await serialiser.SerialiseAsync<List<Part>>(parts);
}
异步结果
这里出现了一个MissingMethodException错误。
Method not found: 'System.Threading.Tasks.Task`1<System.String> Newtonsoft.Json.JsonConvert.SerializeObjectAsync(System.Object)'.
at Helper.Core.Serialisation.Json.JsonSerialiser.<SerialiseAsync>d__0`1.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
at System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
at Helper.Core.Serialisation.Json.JsonSerialiser.SerialiseAsync[T](T serialseObject)
at OpcTester.Form1.<button1_Click>d__9.MoveNext() in c:\Users\phil.murray\Desktop\tmp\OpcTester\Form1.cs:line 44
非异步操作
只是为了检查是否方法的异步部分导致了问题,我编写了一个非异步实现。
public string Serialise<T>(T serialseObject) where T : class
{
if (serialseObject == null) throw new ArgumentNullException("serialseObject");
try
{
return JsonConvert.SerializeObject(serialseObject);
}
catch (JsonSerializationException ex)
{
logger.Error(ex);
throw new SerialisationException("Could Not Serialse The Object", ex);
}
}
非异步实现
private async void button1_Click(object sender, EventArgs e)
{
List<Part> parts = new List<Part> { new Part() { AbstractType = typeof(IOpcController), ConcreteType = typeof(OpcController) },
new Part() { AbstractType = typeof(ISerialiser), ConcreteType = typeof(JsonSerialiser) },
new Part() { AbstractType = typeof(IStringCompression), ConcreteType = typeof(StringGZipCompression)}};
string serialisedResult = string.Empty;
JsonSerialiser serialiser = new JsonSerialiser(null, null);
serialisedResult = serialiser.Serialise<List<Part>>(parts);
}
无异步结果
该方法完成并将List序列化为字符串。
失败的测试示例
Test Name: SerialiserSerialiseObjectExists
Test FullName: Helper.Tests.SerialiserTests.SerialiserSerialiseObjectExists
Test Source: c:\Perforce\Development\SharedAPIs\Helper.Core\Helper.Tests\SerialiserTests.cs : line 38
Test Outcome: Failed
Test Duration: 0:00:00.0116216
Result Message:
Test method Helper.Tests.SerialiserTests.SerialiserSerialiseObjectExists threw exception:
System.MissingMethodException: Method not found: 'System.Threading.Tasks.Task`1<System.String> Newtonsoft.Json.JsonConvert.SerializeObjectAsync(System.Object)'.
Result StackTrace:
at Helper.Core.Serialisation.Json.JsonSerialiser.<SerialiseAsync>d__0`1.MoveNext()
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[TStateMachine](TStateMachine& stateMachine)
a
t System.Runtime.CompilerServices.AsyncTaskMethodBuilder`1.Start[TStateMachine](TStateMachine& stateMachine)
at Helper.Core.Serialisation.Json.JsonSerialiser.SerialiseAsync[T](T serialseObject)
at Helper.Tests.SerialiserTests.<SerialiserSerialiseObjectExists>d__3.MoveNext() in c:\Perforce\Development\SharedAPIs\Helper.Core\Helper.Tests\SerialiserTests.cs:line 40
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
不使用Json.Net进行异步测试
只是为了论证,我用一个虚拟任务替换了Json.Net调用,结果它可以工作,因此看起来问题出在使用Await\Async调用Json.Net上。奇怪的是,这之前一直可以工作,且版本没有更新。
public async Task<string> SerialiseAsync<T>(T serialseObject) where T : class
{
if (serialseObject == null) throw new ArgumentNullException("serialseObject");
try
{
//return await JsonConvert.SerializeObjectAsync(serialseObject);
return await Task.Run(() => string.Empty);
}
catch (JsonSerializationException ex)
{
logger.Error(ex);
throw new SerialisationException("Could Not Serialse The Object", ex);
}
}
问题
在我更新Telerik控件套件之前,异步方法UnitTests之前是可以工作的,并且我在许多实际情况下测试了该操作。我并不是说Telerik更新引起了问题,因为这可能只是巧合。当测试其他类(与Json.Net无关)时,一些其他异步测试用例通过。
有没有想法关于Async方法出了什么问题,以及如何解决它?
可能的解决方案
随着我继续调查问题,我意识到问题可能在Json.Net库中的Async调用中,因此我将非异步调用包装在一个Task中,如下所示,这起作用了。
public async Task<string> SerialiseAsync<T>(T serialseObject) where T : class
{
if (serialseObject == null) throw new ArgumentNullException("serialseObject");
try
{
//return await JsonConvert.SerializeObjectAsync(serialseObject);
return await Task.Run<string>(() => JsonConvert.SerializeObject(serialseObject));
}
catch (JsonSerializationException ex)
{
logger.Error(ex);
throw new SerialisationException("Could Not Serialse The Object", ex);
}
}
出于兴趣,我下载了Json.Net的源代码并查看了JsonConvert.SerializeObjectAsync调用,发现它也在做同样的事情,所以我不确定潜在的问题是什么。
public static Task<string> SerializeObjectAsync(object value, Formatting formatting, JsonSerializerSettings settings)
{
return Task.Factory.StartNew(() => SerializeObject(value, formatting, settings));
}