EF TPH继承丢失在Web Api JSON中

本文关键字:Web Api JSON TPH 继承 EF | 更新日期: 2023-09-27 18:03:06

我已经成功设置了一些使用TPH EF继承的类,MyBaseClass, MySubClass1, MySubClass2

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

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

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

我如何做到这一点?我是否需要强制EF Discriminator字段以某种方式与所有其他数据一起发送?

编辑

1。

在客户端,我现在尝试像这样反序列化JSON(包括$type数据)(运气不好,这些项仍然被转换回它们的基类)

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中,但是这是System.Data.Entity.DynamicProxies.SubClass1_5E07A4CE2F037430DC7BFA00593....类型的,这对于客户端反序列化回SubClass1成功是OK的吗?

EF TPH继承丢失在Web Api JSON中

这是一个序列化问题。

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

添加到你的HttpConfiguration:

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

其中config是用于配置和初始化Asp的HttpConfiguration实例。净WebApi .

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

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

Json。Net将知道如何在WPF端对其进行反序列化时处理此问题。

在你的WPF app:

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

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

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

编辑

谢谢Anish,这几乎是排序。我可以在JSON中看到正确的$type数据,我只是一个笨蛋,我不确定如何获得WebApi响应作为jsonString?目前我正在做response。content。readasasync>().Result;自动反序列化数据。

作为对您的评论的回应,您可以像这样以字符串的形式读取内容:

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

编辑2

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

回应你的评论,是的,你将而不是能够将EF代理类型反序列化为你想要的类型。这个问题需要在WebApi端解决。

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

这里有一个关于lazyeagerEF加载实体的文档链接。

<

解决方案/strong>

你想在你的WebApi控制器动作中生成对象列表并返回它,这将告诉EF从数据库中加载实体和新的类实例。

这里有几个选项:

选项1

在查询中调用ToList()来填充集合:

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

var result = dbContext.Things.ToList();

当然,您不希望返回无界的结果集,因此添加一个range:

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,我认为更好的是了解你的实体,并控制你何时和什么。

非常非常感谢Anish,他的回答和这篇优秀的文章让我走上了正确的道路。

我必须采取的步骤,以获得继承对象的类型通过web api服务如下:

服务器端

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

<

WebApiConfig方法/em>

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代理类名,我在执行Linq查询之前关闭了ProxyCreationEnabled属性,该查询返回基于MyBaseClass的对象。

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

我必须将JsonMediaTypeFormatterSerializerSettings = { 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;
}