EF Core: абстрактный класс обрабатывается как корневой объект - PullRequest
0 голосов
/ 09 июля 2019

Я использую ядро ​​EF для создания своих таблиц (сначала код) и сталкиваюсь со странным поведением с моими абстрактными базовыми классами для некоторых сущностей. У меня есть класс ConfigurableDiscount в качестве абстрактного базового класса и несколько классов, унаследованных от него, например: AfterSalesCoverage. Классы выглядят так:

public abstract class ConfigurableDiscount : TenantEntity, TraceChangesEntity
{
    public DealType DealType { get; set; }
    [Required]
    [Column(TypeName = "decimal(18,4)")]
    public decimal Discount { get; set; }
}


public class AfterSalesCoverage : ConfigurableDiscount
{
    public Guid Id { get; set; }
    [Required]
    public string Name { get; set; }
}

TenantEntity и TraceChangesEntity - это еще один абстрактный класс и пустой интерфейс соответственно.

public abstract class TenantEntity : BaseDeleteEntity
{
    public Guid BusinessUnitId { get; set; }
    public virtual BusinessUnit BusinessUnit { get; set; }
}
public abstract class BaseDeleteEntity : BaseEntity
{
    public DateTime? DeletedOn { get; set; }
}
public abstract class BaseEntity
{
    [Required]
    public DateTime CreatedOn { get; set; }
    public Guid CreatedById { get; set; }
    public virtual User CreatedBy { get; set; }
    public DateTime? ChangedOn { get; set; }
    public Guid? ChangedById { get; set; }
    public virtual User ChangedBy { get; set; }
}

Итак, как вы можете видеть, все они абстрактны, кроме AfterSalesCoverage. Если я пытаюсь построить или перенести, я получаю следующую ошибку:

Тип сущности 'ConfigurableDiscount' требует определения первичного ключа.

Если я сделаю это (определив фиктивный ключ внутри ConfigurableDiscount или переместив Id с AfterSalesCoverage на ConfigurableDiscount) и попытаюсь выполнить миграцию, я получу следующую ошибку:

Выражение фильтра 'entity => (entity.DeletedOn == null)' не может быть указано для типа объекта AfterSalesCoverage. Фильт Это может быть применено только к корневому типу сущности в иерархии.

Что заставляет меня задуматься, почему AfterSalesCoverage не является корневым объектом? Выражение фильтра определяется / настраивается в DBContext.

EDIT: Фильтр в DBContext вызывается внутри OnModelCreating(ModelBuilder builder) и выглядит следующим образом:

private void SetSoftDeleteFilterQuery(ModelBuilder builder)
    {
        System.Collections.Generic.IEnumerable<Type> q = from t in Assembly.GetExecutingAssembly().GetTypes()
                                                         where t.IsClass && t.IsSubclassOf(typeof(BaseDeleteEntity))
                                                         select t;
        foreach (Type type in q)
        {
            MethodInfo method = typeof(CPEDbContext).GetMethod("ApplySoftDeleteFilterQuery", BindingFlags.NonPublic | BindingFlags.Instance);
            MethodInfo generic = method.MakeGenericMethod(type);
            if (type.Name != "RequestWrapper" && type.Name != "TenantEntity" && type.Name != "BaseBenchmarkDiscount")
            {
                {
                    generic.Invoke(this, new[] { builder });
                }
            }
        }
    }

private void ApplySoftDeleteFilterQuery<Tdb>(ModelBuilder builder) where Tdb : BaseDeleteEntity
    {
        builder.Entity<Tdb>().HasQueryFilter(entity => entity.DeletedOn == null);
    }

Похоже, этот фильтр является центром интереса в этом случае ...

Вопрос: Зачем ConfigurableDiscount нужен первичный ключ? Это не должно быть реальной сущностью. Если наследование происходит непосредственно от TenantEntity, мне также не нужен идентификатор в TenantEntity ... На сущность ConfigurableDiscount ссылаются нигде, кроме как в наследующих классах. Так что в моем DBContext нет DBSet или чего-то подобного.

...