Приведение коллекции BaseClass к нескольким DerivedClasses - EF Core - PullRequest
1 голос
/ 21 января 2020

У меня проблемы с приведением коллекции дочерних сущностей базового класса ко всем производным классам. Позвольте мне привести вам пример:

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;}
}

Проблема возникает, когда я пытаюсь выбрать сущность класса «Домашнее хозяйство» с дочерней коллекцией «Персоны», когда я хочу привести детей к их определенным c производным классы.

Я безуспешно пробовал следующие запросы:

var household = Context.Households.Where(h => h.Id = id)
                .Include(hp => hp.Persons).OfType<Parent>().OfType<Child>()
                .FirstOrDefault();

(выдает ошибку, что дочерний класс не содержит правильных определений).

var household = Context.Households.Where(h => h.Id = id)
                .Include(hp => hp.Persons).OfType<Parent>()
                .Include(hpp => hpp.Persons).OfType<Child>()
                .FirstOrDefault();

(генерирует ошибку, что класс Parent не содержит определения для Person)

Я хотел бы иметь коллекцию обоих производных классов в сущности Household, а не только базового класса.

1 Ответ

0 голосов
/ 21 января 2020

Итак, я обнаружил, что EF Core работает просто отлично, именно контроллер потерял свойства polymorphi c дочерних элементов во время вызова API. Чтобы получить производные классы, я просто использовал:

var household = Context.Households.Where(h => h.Id = id)
                .Include(hp => hp.Persons).FirstOrDefault();

Чтобы решить проблему с контроллером, я использовал следующие два источника в качестве руководства: Do tNet Сериализация и Polymorphi c десериализация .

Я основывался на реализации JsonConverter на нем и в итоге получил следующий код:

Редактируемый класс:

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());
});
...