Entity Framework Core - миграция создает все свойства из модели вместо желаемых - PullRequest
0 голосов
/ 23 октября 2019

это мои начала с EfCore (ранее я был в nHibernate и Dapper). У меня проблема с сопоставлениями.

Моя модель выглядит так:

public class Document
{
   public Guid Id {get;set;}
   public string Name {get;set;}

   public int ValueIDontWantToBeInDb {get; set;}
}

Мои сопоставления:

b.ToTable("documents");
b.Property(x => x.Id).ValueGeneratedOnAdd();
b.HasKey(x => x.Id);
b.Property(x => x.Name).IsRequired();

(где b - EntityTypeBuilder, полученный в реализации IEntityTypeConfiguration.

Как видите, я никогда не использую ValueIDontWantToBeInDb, но EfCore продолжает добавлять это в схему таблицы. Почему так и что нужно сделать, чтобы добавить только те свойства, которые мне нужны?

Я знаю, что есть метод Ignore, но тогда мне придется вызывать его для каждой модели каждого свойства, которое я не хочу добавлять в схему.

Я просто хочу показать EfCore - «Эй,сопоставить эти свойства примерно так "- как в nHibernate. Как это сделать?

1 Ответ

0 голосов
/ 24 октября 2019

Хорошо, я создаю какое-то решение для этого. Но я думаю, что это действительно возмутительно, что EfCore не имеет такого решения в стандарте. Итак, сначала создайте базовый класс для всех ваших отображений. Это действительно место со всей магией:

abstract class BaseMap<T>: IEntityTypeConfiguration<T> where T: class, IDbItem //IDbItem is my own constraint, just an interface that has Id property
{
    EntityTypeBuilder<T> theBuilder;
    List<string> mappedPropertyNames = new List<string>();

    protected PropertyBuilder<TProperty> Map<TProperty>(Expression<Func<T, TProperty>> x) //this will be called instead of Property()
    {
        mappedPropertyNames.Add(GetPropertyName(x)); 
        return theBuilder.Property(x);
    }

    protected ReferenceNavigationBuilder<T, TRelatedEntity> HasOne<TRelatedEntity>(Expression<Func<T, TRelatedEntity>> x)
        where TRelatedEntity: class
    {
        mappedPropertyNames.Add(GetPropertyName(x));
        return theBuilder.HasOne(x);
    }

    protected CollectionNavigationBuilder<T, TRelatedEntity> HasMany<TRelatedEntity>(Expression<Func<T, IEnumerable<TRelatedEntity>>> x)
        where TRelatedEntity: class
    {
        mappedPropertyNames.Add(GetPropertyName(x));
        return theBuilder.HasMany(x);
    }

    protected PropertyBuilder<TColumnType> Map<TColumnType>(string propName)
    {
        mappedPropertyNames.Add(propName);
        return theBuilder.Property<TColumnType>(propName);
    }

    protected abstract void CreateModel(EntityTypeBuilder<T> builder);

    public void Configure(EntityTypeBuilder<T> builder)
    {
        theBuilder = builder;

        Map(x => x.Id).ValueGeneratedOnAdd();
        builder.HasKey(x => x.Id);

        CreateModel(builder);

        IgnoreUnmappedProperties(builder);

    }

    void IgnoreUnmappedProperties(EntityTypeBuilder<T> builder)
    {
        PropertyInfo[] propsInModel = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
        foreach (var prop in propsInModel)
        {
            if (!mappedPropertyNames.Contains(prop.Name))
            {
                builder.Ignore(prop.Name);
            }
        }
    }

    string GetPropertyName<TProperty>(Expression<Func<T, TProperty>> memberExpression)
    {
        MemberExpression member = null;

        switch (memberExpression.Body)
        {
            case UnaryExpression ue when ue.Operand is MemberExpression:
                member = ue.Operand as MemberExpression;
                break;

            case MemberExpression me:
                member = me;
                break;

            default:
                throw new InvalidOperationException("You should pass property to the method, for example: x => x.MyProperty");

        }

        var pInfo = member.Member as PropertyInfo;
        if (pInfo == null)
            throw new InvalidOperationException("You should pass property to the method, for example: x => x.MyProperty");

        return pInfo.Name;
    }
}

Теперь пример класса, который наследуется от него и создает карту реальных объектов:

class DocumentMap : BaseMap<Document>
{
    protected override void CreateModel(EntityTypeBuilder<Document> b)
    {
        b.ToTable("documents");

        Map(x => x.Name).IsRequired();
        Map<Guid>("Owner_id").IsRequired();

        HasOne(x => x.Owner)
            .WithMany()
            .HasForeignKey("Owner_id")
            .HasConstraintName("FK_Users_Documents")
            .IsRequired()
            .OnDelete(DeleteBehavior.Cascade);

        HasMany(x => x.Periods)
            .WithOne(y => y.ParentDocument);

        HasMany(x => x.Loans)
            .WithOne(y => y.ParentDocument);
    }
}

Обратите внимание, что вместо использования методов изEntityTypeBuilder, я использую методы, полученные из базового класса. Честно говоря, мне даже не нужно передавать объект EntityTypeBuilder здесь.

И все это вызывается в DbContext следующим образом:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
    }
...