将 BaseClass 的集合转换为多个 DerivedClasses - EF Core
Cast collection of BaseClass to several DerivedClasses - EF Core
我在将基础 class 的子实体集合转换为所有派生的 class 时遇到问题。
让我举个例子:
public class Household {
public int Id {get; set;}
public virtual ICollection<Person> Persons {get; set;}
}
public abstract class Person {
public int Id {get; set;}
}
public class Parent : Person {
public int Income {get; set;}
}
public class Child : Person {
public string School {get; set;}
}
当我试图将子集合 Persons select 与子集合 Persons 一起 select 时,问题就出现了。
我试过以下查询但没有成功:
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).OfType<Parent>().OfType<Child>()
.FirstOrDefault();
(生成 Class 子项不包含正确定义的错误)。
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).OfType<Parent>()
.Include(hpp => hpp.Persons).OfType<Child>()
.FirstOrDefault();
(生成 class Parent 不包含 Person 的定义的错误)
我想要的是在 Household 实体上收集两个派生的 classes,而不仅仅是基础 class.
因此,我发现 EF Core 工作正常,是控制器在 API 调用期间丢失了子项的多态属性。
为了获得派生的 classes,我简单地使用了:
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).FirstOrDefault();
为了解决控制器的问题,我使用以下两个来源作为指导,DotNet Serialization and 。
我基于它实现了一个 JsonConverter,并得到了以下代码:
已编辑 class:
public class Household {
public int Id {get; set;}
public virtual ICollection<Person> Persons {get; set;}
}
public abstract class Person {
public int Id {get; set;}
public string Discriminator {get; private set;}
// The discriminator actually exists in the inherited parent table,
// I just want to attach it to my entities so I can identify to which subclass it belongs to
}
public class Parent : Person {
public int Income {get; set;}
}
public class Child : Person {
public string School {get; set;}
}
JsonConverter:
public class PersonConverter: JsonConverter<Person>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(Person).IsAssignableFrom(typeToConvert);
public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
Person person;
using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{
// Using camelCase properties in my front-end application, therefore ToLower()
if (!jsonDocument.RootElement.TryGetProperty(nameof(Person.Discriminator).ToLower(), out var typeProperty))
{
throw new JsonException();
}
var type = typeProperty.GetString();
var jsonObject = jsonDocument.RootElement.GetRawText();
switch (type)
{
case nameof(Parent):
person = (Parent)JsonSerializer.Deserialize(jsonObject, typeof(Parent));
break;
case nameof(Child):
person = (Child)JsonSerializer.Deserialize(jsonObject, typeof(Child));
break;
default:
throw new JsonException();
}
}
return person;
}
public override void Write(Utf8JsonWriter writer, Person value, JsonSerializerOptions options)
{
if (value is Parent parent)
{
// Using camelCase properties in my front-end application
JsonSerializer.Serialize(writer, parent, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
else if (value is Child child)
{
// Using camelCase properties in my front-end application
JsonSerializer.Serialize(writer, child, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
else
{
throw new NotSupportedException();
}
}
}
然后我在启动期间将它连接到我的控制器。
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new PersonConverter());
});
我在将基础 class 的子实体集合转换为所有派生的 class 时遇到问题。 让我举个例子:
public class Household {
public int Id {get; set;}
public virtual ICollection<Person> Persons {get; set;}
}
public abstract class Person {
public int Id {get; set;}
}
public class Parent : Person {
public int Income {get; set;}
}
public class Child : Person {
public string School {get; set;}
}
当我试图将子集合 Persons select 与子集合 Persons 一起 select 时,问题就出现了。
我试过以下查询但没有成功:
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).OfType<Parent>().OfType<Child>()
.FirstOrDefault();
(生成 Class 子项不包含正确定义的错误)。
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).OfType<Parent>()
.Include(hpp => hpp.Persons).OfType<Child>()
.FirstOrDefault();
(生成 class Parent 不包含 Person 的定义的错误)
我想要的是在 Household 实体上收集两个派生的 classes,而不仅仅是基础 class.
因此,我发现 EF Core 工作正常,是控制器在 API 调用期间丢失了子项的多态属性。 为了获得派生的 classes,我简单地使用了:
var household = Context.Households.Where(h => h.Id = id)
.Include(hp => hp.Persons).FirstOrDefault();
为了解决控制器的问题,我使用以下两个来源作为指导,DotNet Serialization and
我基于它实现了一个 JsonConverter,并得到了以下代码:
已编辑 class:
public class Household {
public int Id {get; set;}
public virtual ICollection<Person> Persons {get; set;}
}
public abstract class Person {
public int Id {get; set;}
public string Discriminator {get; private set;}
// The discriminator actually exists in the inherited parent table,
// I just want to attach it to my entities so I can identify to which subclass it belongs to
}
public class Parent : Person {
public int Income {get; set;}
}
public class Child : Person {
public string School {get; set;}
}
JsonConverter:
public class PersonConverter: JsonConverter<Person>
{
public override bool CanConvert(Type typeToConvert) =>
typeof(Person).IsAssignableFrom(typeToConvert);
public override Person Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}
Person person;
using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{
// Using camelCase properties in my front-end application, therefore ToLower()
if (!jsonDocument.RootElement.TryGetProperty(nameof(Person.Discriminator).ToLower(), out var typeProperty))
{
throw new JsonException();
}
var type = typeProperty.GetString();
var jsonObject = jsonDocument.RootElement.GetRawText();
switch (type)
{
case nameof(Parent):
person = (Parent)JsonSerializer.Deserialize(jsonObject, typeof(Parent));
break;
case nameof(Child):
person = (Child)JsonSerializer.Deserialize(jsonObject, typeof(Child));
break;
default:
throw new JsonException();
}
}
return person;
}
public override void Write(Utf8JsonWriter writer, Person value, JsonSerializerOptions options)
{
if (value is Parent parent)
{
// Using camelCase properties in my front-end application
JsonSerializer.Serialize(writer, parent, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
else if (value is Child child)
{
// Using camelCase properties in my front-end application
JsonSerializer.Serialize(writer, child, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
}
else
{
throw new NotSupportedException();
}
}
}
然后我在启动期间将它连接到我的控制器。
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new PersonConverter());
});