Asp.net mvc 4 在Web API OData中手动反序列化对象内容

Asp.net mvc 4 在Web API OData中手动反序列化对象内容,asp.net-mvc-4,asp.net-web-api,odata,Asp.net Mvc 4,Asp.net Web Api,Odata,当使用WCF数据服务客户端与我的ODataAPI通信时,我偶尔需要使用 System.Data.Services.Client.DataServiceContext的AddRelatedObject方法 这将生成一个到端点的POST,如下所示: http://base_url/odata/Entity(键)/导航属性 http请求的内容是序列化的NavigationProperty实体 由于默认odata控制器中未映射到此URL的帖子,因此我编写了一个新的EntitySetRoutingConv

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

这将生成一个到端点的
POST
,如下所示:

http://base_url/odata/Entity(键)/导航属性

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

由于默认odata控制器中未映射到此URL的帖子,因此我编写了一个新的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
,并对其进行解析以确定所需的类型和键

我遇到的问题是,一旦我知道我的父实体是一个键为
1
租户
,并且
租户
实体中
用户
导航属性
是一个
用户
实体,我不知道如何手动调用
odatamediatypeformatter
将http内容反序列化到
用户
实体中,以便将其添加到
租户
用户集合中

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

如果有人能给我指出正确的方向,我会继续研究并更新,如果我发现更多

更新日期2013年6月28日 多亏了拉古拉姆,我才得以靠近。我看到的问题是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)
所有人都犯了同样的错误,似乎我遗漏了一些明显的东西

谢谢


Steve

只需将User类型的参数添加到您的
AddRelation
操作中,您就应该很好了。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);
}
private静态任务readodatacontentasync(HttpRequestMessage请求)
{
变量格式化程序=
ODataMediaTypeFormatters.Create()
.Select(formatter=>formatter.GetPerRequestFormatterInstance(typeof(T),request,request.Content.Headers.ContentType));
返回request.Content.ReadAsAsync(格式化程序);
}
像这样,

Customer addedCustomer = ReadODataContentAsAsync<Customer>(Request).Result;
Customer addedCustomer=ReadODataContentAsAsync(请求)。结果;

Hi RhaghuRam,这对我来说是可行的,除了我的基本控制器是泛型的,我的所有EntitySetControllers都从中继承。这意味着NavigationProperty类型在设计时未知。此外,在本例中,虽然我的示例用于添加用户,但我还可以使用相同的方法将相关组和部门添加到租户。因为直到运行时我才知道NavigationProperty是什么类型,所以我需要手动反序列化它。谢谢!这让我非常接近,但我现在得到了一个错误:没有MediaTypeFormatter可以从媒体类型为“application/atom+xml”的内容中读取“User”类型的对象。我尝试过手动创建格式化程序,但仍然没有成功。我将用我尝试过的内容更新我的原始问题。我忘记添加GetPerRequestFormatterInstance技巧,我们在将请求注入OData格式化程序时使用了该技巧。我现在更新了答案。非常有效!谢谢你的帮助!