我已成功设置了一些使用TPH EF inheritance,MyBaseClass
,MySubClass1
,MySubClass2
等的课程。
使用Linq context.MyBaseClasses.Where(...)
查询时,返回的对象都正确使用数据库中Discriminator字段指定的子类。 (所以我最终可能会得到一个包含MySubClass1
或MySubClass2
的混合对象的列表。)
但是,当我通过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}成功了?
答案 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 应用程序需要引用您定义MyBaseClass
和MySubClass1
的项目。
修改强>
感谢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;
}