【问题标题】:Entity Framework Core - migration creates all properties from model instead of desired onesEntity Framework Core - 迁移从模型创建所有属性,而不是所需的
【发布时间】:2019-10-23 13:10:00
【问题描述】:

这些是我使用 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 是 IEntityTypeConfiguration 实现中收到的 EntityTypeBuilder。

如您所见,我从不使用 ValueIDontWantToBeInDb,但 EfCore 不断将其添加到表架构中。为什么会这样?如何让它只添加我想要的那些属性?

我知道有一个忽略方法。但随后我将不得不在我不想添加到架构中的每个属性的每个模型上调用它。

我只想向 EfCore 展示 - “嘿,像这样映射这些属性” - 就像在 nHibernate 中一样。如何做到这一点?

【问题讨论】:

    标签: entity-framework-core mapping ef-core-2.0 columnmappings


    【解决方案1】:

    好的,我为此创建了一些解决方案。但我认为 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());
        }
    

    【讨论】:

      猜你喜欢
      • 2016-08-21
      • 2019-02-12
      • 1970-01-01
      • 2020-06-06
      • 2020-03-09
      • 1970-01-01
      • 2017-06-01
      • 2016-04-12
      • 2017-11-05
      相关资源
      最近更新 更多