这些是我从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中一样。怎么做?
好吧,我为此创建了一些解决方案。但是我认为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());
}