Как сохранять свойство типа 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 не умеет работать с абстракциями, а только с конкретными типами, то как быть в такой ситуации?