Потеря наследующих типов на пути от ядра asp.net к Angular
Я занимаюсь разработкой приложения с asp.net core 2.2 и ef core 2.2 на сервере и Angular 7 на стороне клиента. Я не могу понять это:
У меня есть следующие (упрощенные) модели:
public abstract class LegalEntity
{
public int Id { get; set; }
public int Name { get; set; }
public Address FiscalAddress { get; set; }
}
public class Organisation : LegalEntity
{
public Organisation(){}
public Organisation(LegalEntityType legalEntityType, string name, Address fiscalAddress)
{
LegalEntityType = legalEntityType;
Name = name;
}
public LegalEntityType LegalEntityType { get; set; }
}
public class LegalEntityType
{
public int Id { get; set; }
public int Name { get; set; }
public LegalEntityType(){}
public LegalEntityType(string name)
{
Name = name;
}
}
public class Person : LegalEntity
{
public Gender Gender { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string Name => string.Format("{0} {1}", FirstName, LastName);
}
public class Gender
{
public int Id { get; set; }
public int Name { get; set; }
}
public class Customer
{
public int Id { get; set; }
public Customer(){}
public Customer(LegalEntity legalEntity)
{
LegalEntity = legalEntity;
}
public LegalEntity LegalEntity { get; set; }
}
Когда я возвращаю сущности клиента клиенту через API, иногда LegalEntity - это организация, а иногда и личность. После того, какой тип (Organization или Person) возвращается, я хочу, чтобы свойство LegalEntityType (в случае Organization) или свойство Gender (в случае Person) было отображено в коде JSON. Это моя первая проблема, следующее оставляет их обоих нулевыми:
.Include(o => o.Customer).ThenInclude(o => o.LegalEntity)
поскольку это не загружает свойства навигации, которые присутствуют только в наследующих объектах. Вот выдержка из строки JSON в случае
Человек:
...
"customer": {
"legalEntity": {
"gender": null,
"firstName": "Denis",
"lastName": "Testmann",
"name": "Denis Testmann",
"id": 9
},
...
Организация:
...
"customer": {
"legalEntity": {
"legalEntityType": null,
"name": "Companyname GmbH",
"id": 6
},
...
Вместо этого должно получиться следующее:
Человек:
...
"customer": {
"Person": {
"gender": null,
"firstName": "Denis",
"lastName": "Testmann",
"name": "Denis Testmann",
"id": 9
},
...
Организация:
...
"customer": {
"Organisation": {
"legalEntityType": null,
"name": "Companyname GmbH",
"id": 6
},
...
Подводя итог: клиент может быть физическим или юридическим лицом, оба юридического лица (организация и физическое лицо) наследуют от LegalEntity, и поэтому собственностью клиента "LegalEntity" иногда является физическое лицо, а иногда и организация. Когда я отрисовываю JSON, необходимо поддерживать определенный тип.
Надеюсь, я был достаточно ясен - извините за то, что я так долго, я хочу быть уверен, что проблема понятна.
2 ответа
Хорошо - нашел решение!
на стороне сервера вы должны сериализовать контент JSON с
SerializerSettings.TypeNameHandling = TypeNameHandling.Auto
что вы должны сделать в файле Startup.cs:
services.AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
opt.SerializerSettings.TypeNameHandling = TypeNameHandling.Auto;
})
а на стороне клиента вы можете использовать преобразователь классов ( https://github.com/typestack/class-transformer).
для моего примера класс клиента - где LegalEntity может иметь тип Person или Organization выглядит следующим образом:
public class Customer
{
public int Id { get; set; }
@Type( () => Object, {
discriminator: {
property: '$type',
subTypes: [
{value: Person, name: 'Your.Name.Space.Person, YourApp.Name'},
{value: Organisation, name: 'Your.Name.Space.Organisation, YourApp.Name'},
],
},
})
public LegalEntity LegalEntity { get; set; }
}
а затем вы создаете экземпляр класса customer из простого объекта javascript следующим образом:
import { plainToClass } from 'class-transformer';
export class SomeClassInYourAngularApp implements OnInit {
customerList: Customer[];
ngOnInit() {
this.customerList = new Array<Customer>();
let u: any;
// lets get the plain js object form somewhere, ex. from a resolver
this.route.data.subscribe(data => {
u = data['customerList'].result;
});
u = plainToClass(Customer, u);
Object.assign(this.customerList, u);
}
вот и все!
Новый ответ
Я собрал следующие Program.cs через dotnet new console
через дотнет ядро 2.1:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
class Program
{
static void Main(string[] args)
{
var list = new List<Customer>();
list.Add(new Customer(new Person { Gender = new Gender{ Name = "Male"}}));
list.Add(new Customer(new Organisation { LegalEntityType = new LegalEntityType{ Name = "GmbH"}}));
Console.WriteLine(JsonConvert.SerializeObject(list, Newtonsoft.Json.Formatting.Indented));
}
}
public abstract class LegalEntity
{
public int Id { get; set; }
public virtual string Name { get; set; }
public Address FiscalAddress { get; set; }
}
public class Organisation : LegalEntity
{
public Organisation(){}
public Organisation(LegalEntityType legalEntityType, string name, Address fiscalAddress)
{
LegalEntityType = legalEntityType;
Name = name;
}
public LegalEntityType LegalEntityType { get; set; }
}
public class LegalEntityType
{
public int Id { get; set; }
public string Name { get; set; }
public LegalEntityType(){}
public LegalEntityType(string name)
{
Name = name;
}
}
public class Person : LegalEntity
{
public Gender Gender { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public override string Name => string.Format("{0} {1}", FirstName, LastName);
}
public class Gender
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Customer
{
public int Id { get; set; }
public Customer(){}
public Customer(LegalEntity legalEntity)
{
LegalEntity = legalEntity;
}
public LegalEntity LegalEntity { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
}
Вывод выглядит так:
[
{
"Id": 0,
"LegalEntity": {
"Gender": {
"Id": 0,
"Name": "Male"
},
"FirstName": null,
"LastName": null,
"Name": " ",
"Id": 0,
"FiscalAddress": null
}
},
{
"Id": 0,
"LegalEntity": {
"LegalEntityType": {
"Id": 0,
"Name": "GmbH"
},
"Id": 0,
"Name": null,
"FiscalAddress": null
}
}
]
Это выглядит нормально? Возможно, проверьте, какие настройки сериализатора использует ваша привязка конечной точки API.
Старый ответ
Если LegalEntity является реальной таблицей в базе данных, вы можете использовать:
[ForeignKey("LegalEntity")]
public int LegalEntityId { get; set; }
в вашем Customer
определение.
Вам также необходимо установить ключ:
public abstract class LegalEntity
{
[Key]
public int Id { get; set; }
public int Name { get; set; }
public Address FiscalAddress { get; set; }
}
если LegalEntity не является реальной таблицей, добавьте отдельные переходы для Персона / Организация