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字段指定的子类。(所以我最终可能会得到一个包含MySubClass1
或MySubClass2
对象的混合列表。)
然而,当我通过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的吗?
这是一个序列化问题。
可以自定义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应用程序需要有一个对您定义MyBaseClass
和MySubClass1
的项目的引用。
编辑
谢谢Anish,这几乎是排序。我可以在JSON中看到正确的$type数据,我只是一个笨蛋,我不确定如何获得WebApi响应作为jsonString?目前我正在做response。content。readasasync>().Result;自动反序列化数据。
作为对您的评论的回应,您可以像这样以字符串的形式读取内容:
var jsonString = response.Content.ReadAsStringAsync().Result;
编辑2
Anish,非常感谢你的输入。正如您所看到的,我现在已经设法获得JSON作为字符串,但即使使用JsonConvert。我仍然得到相同的问题。您是否认为(正如我在编辑2中提到的)与从服务返回的$类型不正确(作为EF代理类型)有关?
回应你的评论,是的,你将而不是能够将EF代理类型反序列化为你想要的类型。这个问题需要在WebApi端解决。
创建代理类是为了允许您延迟加载实体。代理通常用于表示引用/嵌套实体。这允许从数据库中获取被引用的实体被延迟到需要时,如果需要的话。 这里有一个关于lazy和eager用EF加载实体的文档链接。<解决方案/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();
客户端
我必须将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;
}