Как сохранять свойство типа interfaces в EfCore?

реализовываю DDD подход на проекте. Поэтому все ValueObject, не имея Id сохраняются как часть Entity в который они входят, т.е. заменяем реляционные связи на плоскую структуру. Entity - одна таблица, куда входит вся информация про агрегат.

public class MarketingRowData : Entity<MarketingRowDataId>
{
    public MarketingNumber Number { get; private set; }
    public Photo Photo { get; private set; }
    public List<Photo> ApartmentPhotos { get; private set; }
    public IValidator Validator { get; private set; }  //Добавил новое св-во, которое тоже нужно сохранять в БД.

    private MarketingRowData(MarketingNumber number, Photo photo, List<Photo> apartmentPhotos)
    {
        Id = new MarketingRowDataId(Guid.NewGuid());
        Number = number;
        Photo = photo;
        ApartmentPhotos = apartmentPhotos;
    }
    // To satisfy EF Core
    protected MarketingRowData()
    {
    }

    public static Result<MarketingRowData, ErrorAll> Create(MarketingNumber number, Photo photo, List<Photo> apartmentPhotos)
    {
        var instance = new MarketingRowData(number, photo, apartmentPhotos);
        var validator = new Validator();
        var valRes = validator.Validate(instance);
        return valRes.IsValid ?
            ResultExt.Success(instance) :
            ResultExt.Failure<MarketingRowData>(valRes.Errors);
    }

    private class Validator : AbstractValidator<MarketingRowData>
    {
        public Validator()
        {
            RuleFor(x => x.Number)
                .NotNull().WithMessage("{PropertyName} не может быть Null");

            RuleFor(x => x.Photo)
                .NotNull().WithMessage("{PropertyName} не может быть Null");

            RuleFor(x => x.ApartmentPhotos)
                .NotNull().WithMessage("{PropertyName} не может быть Null")
                .Must(list =>list.Count > 0).WithMessage("{PropertyName} не может быть пустой коллекцией");
        }
    }
}

public class MarketingRowDataId : Base.ValueObject<MarketingRowDataId>
{
    public Guid Value { get; }

    public MarketingRowDataId(Guid value)
    {
        Value = value;
    }

    protected MarketingRowDataId()
    {
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Value;
    }
}

Приведу только код класса MarketingRowData, который является Entity и его строго типизированный Id- MarketingRowDataId Number, Photo, ApartmentPhotos, Validator - это ValueObject и их свойства превращаются в колонки в таблице MarketingRowData.

Настройка EfCore для хранения MarketingRowData

 public void Configure(EntityTypeBuilder<MarketingRowData> builder)
    {
        //Id-----------------------------------------------------------------------       
        builder.HasKey(p => p.Id);
        builder
            .Property(l => l.Id)
            .HasConversion(x => x.Value, x => new MarketingRowDataId(x));
        
        //Number--------------------------------------------------------------------               
        builder
            .Property(l => l.Number)
            .HasConversion(x => x.Number, x => MarketingNumber.Create(x).Value);
        
        //Photo--------------------------------------------------------------------               
        builder
            .OwnsOne(
                l => l.Photo,
                n =>
                {
                    n.Property(p => p.Name).HasColumnName("PhotoName");
                    n.Property(p => p.CreateDate).HasColumnName("PhotoCreateDate");
                    n.Property(p => p.Image).HasColumnName("PhotoImage");
                });

        //ApartmentPhotos--------------------------------------------------------------
        //для OwnsMany связи понадобиться релеационная зависимость. EF создаст таблицу(Ons-Many) "MarketingRowData_ApartmentPhotos" и для каждого элемента 2 обязательных поля "MarketingRowDataId" - "FK на Owner"  "Id" - номер внутри коллекции
        builder
            .OwnsMany(
                l => l.ApartmentPhotos, 
                n =>
                {
                    n.Property(p => p.Name).HasColumnName("ApartmentPhotos_PhotoName");
                    n.Property(p => p.CreateDate).HasColumnName("ApartmentPhotos_PhotoCreateDate");
                    n.Property(p => p.Image).HasColumnName("ApartmentPhotos_PhotoImage");
                }); 
        
        
        //Validator --------------------------------------------------------------------               
        //ЧТО делать с интерфейсом?
    }

Проблема возникает с хранением абстракции Validator (IValidator) - т.к. непонятно какой наследник там будет хранится и какие у него поля. Может ли EfCore работать с интерфейсами? NHibertnate - вроде умеет, попадались такие статьи на глаза.

public class Validator1 : IValidator
{
    public int propInt {get; set;}
    
}

public class Validator2 : IValidator
{
    public string propString1 {get; set;}
    public string propString2 {get; set;}
}

SQL скрипт получившихся таблиц в результате миграции.

CREATE TABLE public."MarketingRowData"
(
    "Id" uuid NOT NULL,
    "Number" text COLLATE pg_catalog."default",
    "PhotoName" text COLLATE pg_catalog."default",
    "PhotoImage" bytea,
    "PhotoCreateDate" timestamp without time zone,
    CONSTRAINT "PK_MarketingRowData" PRIMARY KEY ("Id")
)

CREATE TABLE public."MarketingRowData_ApartmentPhotos"
(
    "MarketingRowDataId" uuid NOT NULL,
    "Id" integer NOT NULL GENERATED BY DEFAULT AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
    "ApartmentPhotos_PhotoName" text COLLATE pg_catalog."default",
    "ApartmentPhotos_PhotoImage" bytea,
    "ApartmentPhotos_PhotoCreateDate" timestamp without time zone NOT NULL,
    CONSTRAINT "PK_MarketingRowData_ApartmentPhotos" PRIMARY KEY ("MarketingRowDataId", "Id"),
    CONSTRAINT "FK_MarketingRowData_ApartmentPhotos_MarketingRowData_Marketing~" FOREIGN KEY ("MarketingRowDataId")
        REFERENCES public."MarketingRowData" ("Id") MATCH SIMPLE
        ON UPDATE NO ACTION
        ON DELETE CASCADE
)

по идее в таблицу MarketingRowData нужно добавить все поля из всех потомков IValidator

  Validator1:
   "propInt" integer
  Validator2:
   "propString1" text COLLATE pg_catalog."default"
   "propString2" text COLLATE pg_catalog."default"

Если храним Validator1, то работаем только с "propInt1" колонкой. Если храним Validator2, то работаем только с "propString1" и "propString2" колонкой.

Какой-то такой алгоритм должен быть в ORM.

Если EfCore не умеет работать с абстракциями, а только с конкретными типами, то как быть в такой ситуации?


Ответы (0 шт):