2014-11-06 68 views
0

从数据库中获取数据后,我将其序列化为XML。Web Api 2返回数据而不格式化异步

然后,我将该XML作为字符串写入Redis缓存实例。

我希望端点检查数据是否存在于缓存中,并根据结果从缓存中返回数据,或者击中数据库,缓存数据然后返回该数据。如果一切顺利的香蕉试图使它异步时

工作同步码

[HttpGet] 
public IHttpActionResult Test(int userId) 
{ 
    var response = new HttpResponseMessage(HttpStatusCode.OK); 
    var data = Cache.StringGet("Cart:" + userId); 

    if (string.IsNullOrEmpty(data)) 
    { 
    // Grab data from DB and format as XML 
    Cache.StringSet("Cart:" + userId, data, TimeSpan.FromHours(2)); 
    } 

    response.Content = new StringContent(data); 
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml"); 
    return new ResponseMessageResult(response); 
} 

:当同步执行

我的代码工作就好了。

破碎异步代码 (我包括必要的代码量最小重现该问题)

[HttpGet] 
public async Task<HttpResponseMessage> TestAsync(int userId) 
{ 
    var data = await Cache.StringGetAsync("Cart:" + userId); 
    var response = Request.CreateResponse(HttpStatusCode.OK); 
    response.Content = new StringContent("<test>Test</test>"); 
    response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/xml"); 
    return response; 
} 

注意,在这个上面的例子我还没有访问异步加载数据。如果我将等待线注释掉,事情就会重新开始。它只有在代码中有await时才会失败。

发生的问题是,50%的时间,请求端点只是...失速,永远不会完成。附上的Fiddler屏幕截图突出了这个问题。

Fiddler Grab

最后,如果跳过媒体格式和内容协商一个更简单的方法,我会很乐意改变我的做法了。

我应该补充说,将使用此端点的服务仅支持XML,并且对每个请求进行反序列化和重新串行化都没有意义。

+0

你有没有试过'等待Cache.StringGetAsync(“Cart:”+ userId).ConfigureAwait(false)'而不是?这听起来像你可能正在经历一个典型的ASP.Net死锁问题(它的组织方式,它喜欢与异步/等待死锁)。 – 2014-11-06 03:40:44

+0

在破损的代码中,你为什么要访问Cache.StringGetAsync,当你在调用Cache.StringGet的工作代码中时,不是已经是Async的TestAsync调用,现在它将等待调用Cache.StringGet的结果。你会检查这是否解决了问题 – 2014-11-06 04:12:35

+0

@MrinalKamboj我的印象是,正确的异步/等待设计是为了使链中的每个方法都是异步的,以避免阻塞错误。例如。异步控制器调用异步服务调用异步数据库存储库,它使用检索数据的异步方法。删除await可以解决问题,但也可以使控制器在HttpContext线程上运行时同步。 Cache.StringGetAsync是对redis实例的远程调用,如果可能的话应该是异步的。 – Bio2hazard 2014-11-06 04:31:15

回答

0

关于与媒体格式和内容协商相关的部分,以下是我的Global.asax.cs文件的Application_Start方法代码,用于生成默认为Json。发布这些设置后,我无需进行任何内容协商或设置媒体类型,Web API负责将对象序列化为Json并对传入的Json进行反序列化。事实上,你只需要一个子集,因为根据我的理解,Web API的默认值是XML,如果你有一个特殊的序列化器,那么只需设置该设置,并且你可以明确地设置媒体类型来强制执行,否则它依赖于传入的请求,这也是大多数浏览器的XML

 JsonSerializerSettings newtonSoftJsonSerializerSettings = new JsonSerializerSettings() 
     { 
      ReferenceLoopHandling = ReferenceLoopHandling.Ignore, // Ignore the Self Reference looping 
      PreserveReferencesHandling = PreserveReferencesHandling.None, // Do not Preserve the Reference Handling 
      ContractResolver = new CamelCasePropertyNamesContractResolver() // Make All properties Camel Case 
     }; 

     // Setting the Formatting for the Json output to be indented 
     newtonSoftJsonSerializerSettings.Formatting = Formatting.Indented; // Make the final output indented 

     HttpConfiguration jsonHttpconfig = GlobalConfiguration.Configuration; 

     // Add the settings modified Json Serializer to the HttpConfiguration object 
     jsonHttpconfig.Formatters.JsonFormatter.SerializerSettings = newtonSoftJsonSerializerSettings; 

     // This line ensures Json for all clients, without this line it generates Json only for clients which request, for browsers default is XML 
     jsonHttpconfig.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html")); 
+0

这不会帮助我解决我的问题,我不这么认为。我没有任何问题让我的对象被序列化为XML或JSON,我的问题是我有一个已经序列化的字符串,我想避免反序列化 - 然后在每次请求时重新序列化它。但是我不能用普通的方法从控制器中返回它,因为它会被视为一个字符串(将其包含在引号中,或者创建包装实际内容的XML字符串元素)。 – Bio2hazard 2014-11-06 04:41:10

1

问题解决了!

它最终成为Azure Application Insights

我想它并不完全支持异步或与异步手动创建HttpResponseMessages的问题。

谢谢大家的回复。