在Web-API OData中手动反序列化对象内容

时间:2013-06-27 18:31:18

标签: asp.net-mvc-4 asp.net-web-api odata

使用WCF数据服务客户端与我的OData API进行通信时,我偶尔需要使用 System.Data.Services.Client.DataServiceContext的AddRelatedObject方法

这会为端点生成POST,如下所示:

http://base_url/odata/Entity(key)/NavigationProperty

http请求的内容是序列化的NavigationProperty实体。

由于此URL的帖子在默认的odata控制器中未映射,我编写了一个新的EntitySetRoutingConvention类:

Public Class AddRelatedObjectRoutingConvention
    Inherits EntitySetRoutingConvention

    Public Overrides Function SelectAction(odataPath As Http.OData.Routing.ODataPath, controllerContext As Http.Controllers.HttpControllerContext, actionMap As ILookup(Of String, Http.Controllers.HttpActionDescriptor)) As String
        If controllerContext.Request.Method = Net.Http.HttpMethod.Post Then
            If odataPath.PathTemplate = "~/entityset/key/navigation" Then
                If actionMap.Contains("AddRelation") Then
                    Return "AddRelation"
                End If
            End If
        End If
        Return Nothing
    End Function
End Class

我将此添加到默认路由约定列表并将其传递给MapODATARoute例程。

在我的基本控制器中,我实现了AddRelation,并且它被称为完美。

从此我可以获得odatapath,并解析它以确定所需的类型和密钥。

我遇到的问题是,一旦我知道我的父实体是Tenant,密钥为1NavigationPropertyUsers in { Tenant实体是User实体,我无法弄清楚如何手动调用odatamediatypeformatter将http内容反序列化为User实体,以便我可以添加它是Tenants Users集合。

我已经查看了OData源代码,以了解如何对Web-API管道调用和反序列化实体,但是无法找到OData库获取http内容并将其转换为实体的点

如果有人有想法指出我正确的方向,如果我想出更多的话,我会继续研究和更新。

更新06/28/2013 感谢RaghuRam,我已经能够靠近了。我看到的问题是request.getconfiguration()。formatters或odatamediatypeformatters.create似乎都创建了特定于类型的反序列化器。

当我调用ReadAsAsync时,我收到错误: 没有MediaTypeFormatter可用于从媒体类型为“application / atom + xml”的内容中读取“User”类型的对象。

如上所示,控制器的上下文是租户,因此我认为格式化程序是输入到租户的,因此无法反序列化用户。

我尝试手动创建格式化程序,_childDefinition是odatapath导航属性中的EDM类型引用。在这种情况下是用户。

Dim dser As New Formatter.Deserialization.ODataEntityDeserializer(_childDefinition, New Formatter.Deserialization.DefaultODataDeserializerProvider)

Dim ser As New Formatter.Serialization.ODataEntityTypeSerializer(_childDefinition, New Formatter.Serialization.DefaultODataSerializerProvider)

Dim dprovider As New Formatter.Deserialization.DefaultODataDeserializerProvider
dprovider.SetEdmTypeDeserializer(_childDefinition, dser)

Dim sprovider As New Formatter.Serialization.DefaultODataSerializerProvider
sprovider.SetEdmTypeSerializer(_childDefinition, ser)

Dim fmt As New Formatter.ODataMediaTypeFormatter(dprovider, sprovider, New List(Of Microsoft.Data.OData.ODataPayloadKind) From {Microsoft.Data.OData.ODataPayloadKind.Entry})
fmt.SupportedEncodings.Add(System.Text.Encoding.UTF8)
fmt.SupportedEncodings.Add(System.Text.Encoding.Unicode)
fmt.SupportedMediaTypes.Add(Headers.MediaTypeHeaderValue.Parse("application/atom+xml;type=entry"))
fmt.SupportedMediaTypes.Add(New Headers.MediaTypeHeaderValue("application/atom+xml"))

然后我尝试了:

request.content.ReadAsAsync(of User)(new List(of odatamediatypeformatter) from {fmt})
request.content.ReadAsAsync(of User)(request.getConfiguration().formatters)
request.content.ReadAsAsync(of User)(odatamediatypeformatters.create)

所有人都给出了同样的错误,似乎我错过了一些明显的东西。

谢谢!

史蒂夫

1 个答案:

答案 0 :(得分:1)

只需在您的AddRelation操作中添加User类型的参数即可。 Web API会自动调用OData格式化程序从请求正文中读取用户并将其绑定到您的操作参数。

您可以使用此帮助程序方法读取OData请求正文

private static Task<T> ReadODataContentAsAsync<T>(HttpRequestMessage request)
{
    var formatters =
        ODataMediaTypeFormatters.Create()
        .Select(formatter => formatter.GetPerRequestFormatterInstance(typeof(T), request, request.Content.Headers.ContentType));
    return request.Content.ReadAsAsync<T>(formatters);
}
像这样,

Customer addedCustomer = ReadODataContentAsAsync<Customer>(Request).Result;