Web Api JSON中丢失了EF TPH继承

时间:2015-07-16 12:52:27

标签: c# linq entity-framework inheritance asp.net-web-api

我已成功设置了一些使用TPH EF inheritanceMyBaseClassMySubClass1MySubClass2等的课程。

使用Linq context.MyBaseClasses.Where(...)查询时,返回的对象都正确使用数据库中Discriminator字段指定的子类。 (所以我最终可能会得到一个包含MySubClass1MySubClass2的混合对象的列表。)

但是,当我通过JSON Web Api调用将这些对象传递给WPF应用程序时,收到的对象都是MyBaseClass,而不是他们开始的正确子类。

它们返回的对象属性是public virtual List<MyBaseClass> MyThings类型,所以我想它们最终都是这种类型,但我希望为每个对象保留正确的子类类型。

我如何实现这一目标?我是否需要强制将EF Discriminator字段与所有其他数据一起发送?

修改

1

在客户端,我现在正试图反序列化JSON(包括$类型数据),如此(没有运气,项目仍然转换回基类)

HttpResponseMessage response = GetClient().GetAsync(url).Result;

if (response.IsSuccessStatusCode)
{
    string jsonMessage;
    using (Stream responseStream = response.Content.ReadAsStreamAsync().Result)
    {
        jsonMessage = new StreamReader(responseStream).ReadToEnd();
    }


    List<My.Domain.Models.MyBaseClass> thingsToReturn;

    //new method    
    thingsToReturn = JsonConvert.DeserializeObject<List<My.Domain.Models.MyBaseClass>>(jsonMessage);

    //previous method
    //thingsToReturn = response.Content.ReadAsAsync<List<My.Domain.Models.MyBaseClass>>().Result;

    return thingsToReturn;
}

2。

按照Anish的建议重新SerializerSettings.TypeNameHandling,我现在已经在我的JSON中出现了$ type信息,但是这是System.Data.Entity.DynamicProxies.SubClass1_5E07A4CE2F037430DC7BFA00593....类型,这样客户端可以反序列化回{{1}成功了?

2 个答案:

答案 0 :(得分:3)

这是序列化问题。

您可以自定义 Json.Net的序列化程序设置,以包含json对象的类型信息。

将此添加到您的HttpConfiguration

config.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;

config是用于配置和初始化 Asp.Net WebApi HttpConfiguration实例。

这将告诉 Json.Net 向每个具有类型歧义的json对象添加一些类型信息。这样的对象看起来像这样:

{
    "$type":"MyProjectContainingMyTypes.MySubClass1, MyProjectContainingMyTypes",
    "Name": "Tyrion Lannister",
    "DisplayName": "The Imp",
    "Traits": ["funny", "awesome", "clever"]
}

Json.Net 将知道在 WPF 方面反序列化时如何处理此问题。

这应该可以在你的 WPF 应用中使用:

var things = JsonConvert.DeserializeObject<List<MyBaseClass>>(jsonString);

然后,您可以将things列表中的对象强制转换为各自的派生类型。

当然,您的 WPF 应用程序需要引用您定义MyBaseClassMySubClass1的项目。

修改

  

感谢Anish,这几乎已经分类了。我可以在JSON中看到正确的$ type数据,我只是一个笨蛋,我不知道如何将WebApi响应作为jsonString?我正在做响应.Content.ReadAsAsync&gt;()。结果;自动反序列化数据。

在回复您的评论时,您可以将内容读取为如下字符串:

var jsonString = response.Content.ReadAsStringAsync().Result;

修改2

   Anish,非常感谢您的投入。正如您所看到的,我现在已经设法将JSON作为字符串,但即使使用JsonConvert.DeserializeObject,我仍然遇到同样的问题。你是否认为(正如我在编辑2中提到的那样)不正确地从服务返回的$类型(作为EF代理类型)?

在回复您的评论时,是的能够将EF代理类型反序列化为您想要的类型。需要在 WebApi 方面解决此问题。

创建代理类以允许您延迟加载实体。代理通常用于表示引用/嵌套实体。这允许从数据库中提取被引用的实体,直到需要时,如果需要的话。

以下是 lazy 渴望加载 EF 的实体的一些文档的link

<强>解决方案

您希望水合 WebApi 控制器操作中的对象列表并将其返回,这将告诉 EF 从中加载实体数据库和类的新实例。

你有几个选择:

选项1

在查询上调用ToList()来保护集合:

var result = (from t in dbContext.Things select t).ToList();

var result = dbContext.Things.ToList();

当然,您不希望返回无界结果集,因此请添加范围:

var result = (from t in dbContext.Things select t).Skip(0).Take(10).ToList();

var result = dbContext.Things.Skip(0).Take(10).ToList();

请记住,使用方法,您必须明确地水合嵌套对象,如下所示:

var result = dbContext
             .Things
             .Include(t => t.SomePropertyThatRepresentsSomeNestedObject) 
             .Skip(0)
             .Take(10)
             .ToList();

选项2

关闭DbContext的延迟加载

就个人而言,我会选择选项1 ,我认为最好知道你的实体,并控制你何时以及你保湿什么。

答案 1 :(得分:1)

很多,非常感谢Anish ,他的回答让我在正确的轨道上与this excellent article一起。

我必须采取的步骤来获取要通过web api服务传递的继承对象的类型,如下所示:

服务器端

关键是要将JsonFormatter Serializer TypeNameHandling设置为TypeNameHandling.Auto。这可以在WebApiConfig.Register()中完成,但它显然会为您的Web服务调用返回的所有JSON对象添加$ type属性,或者,您可以简单地装饰您需要$ type的对象的属性。

WebApiConfig方法

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto;        

物业装修方法

[Newtonsoft.Json.JsonProperty(ItemTypeNameHandling = Newtonsoft.Json.TypeNameHandling.Auto)]
public virtual List<MyBaseClass> Things { get; set; }

要在JSON中的$ type属性中获取正确的值,而不是EF代理类名,我在执行基于MyBaseClass返回对象的Linq查询之前关闭了ProxyCreationEnabled属性。

dbContext.Configuration.ProxyCreationEnabled = false;
List<MyBaseClass> things = dbContext.MyBaseClasses.Include("This").Include("That").ToList();

客户端

我必须在JsonMediaTypeFormatter调用中添加SerializerSettings = { TypeNameHandling = TypeNameHandling.Auto } ReadAsAsync(),然后快速地将(正确键入的)对象映射到其子类。

HttpResponseMessage response = GetClient().GetAsync(url).Result;

if (response.IsSuccessStatusCode)
{
    //this formatter responds to the $type parameter passed in the JSON to allow us to correctly map object types
    //https://kirmir.wordpress.com/2014/05/16/polymorphic-serialization-using-newton-json-net-in-httpcontent/
    var formatter = new JsonMediaTypeFormatter
    {
        SerializerSettings = { TypeNameHandling = TypeNameHandling.Auto }
    };

    List<MyBaseClass> thingsToReturn;
    thingsToReturn = response.Content.ReadAsAsync<List<MyBaseClass>>(new List<MediaTypeFormatter> { formatter }).Result;
    return productTestsToReturn;
}