EF Core Mapping EntityTypeConfiguration

Her*_*man 107 c# entity-framework-core

在EF6中,我们通常能够使用这种方式配置实体.

public class AccountMap : EntityTypeConfiguration<Account>
{
    public AccountMap()
    {
        ToTable("Account");
        HasKey(a => a.Id);

        Property(a => a.Username).HasMaxLength(50);
        Property(a => a.Email).HasMaxLength(255);
        Property(a => a.Name).HasMaxLength(255);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们如何在EF Core中做,因为当我继承EntityTypeConfiguration类时无法找到该类.

我从GitHub下载EF Core原始源代码,我找不到它.有人可以帮忙吗?

Krz*_*cki 150

从EF Core 2.0开始IEntityTypeConfiguration<TEntity>.你可以像这样使用它:

class CustomerConfiguration : IEntityTypeConfiguration<Customer>
{
  public void Configure(EntityTypeBuilder<Customer> builder)
  {
     builder.HasKey(c => c.AlternateKey);
     builder.Property(c => c.Name).HasMaxLength(200);
   }
}

...
// OnModelCreating
builder.ApplyConfiguration(new CustomerConfiguration());
Run Code Online (Sandbox Code Playgroud)

有关此内容以及2.0中引入的其他新功能的更多信息,请参见此处.

  • 这是EF Core 2.0的最佳答案.谢谢! (8认同)
  • 如果您有手动自定义配置,只需输入 ```builder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);``` 它将应用所有自定义配置 (6认同)
  • 太好了 我一直在寻找分离流畅的API定义的方法。谢谢 (2认同)

dev*_*tal 51

您可以通过一些简单的其他类型实现此目的:

internal static class ModelBuilderExtensions
{
   public static void AddConfiguration<TEntity>(
     this ModelBuilder modelBuilder, 
     DbEntityConfiguration<TEntity> entityConfiguration) where TEntity : class
   {     
       modelBuilder.Entity<TEntity>(entityConfiguration.Configure);
   }
}

internal abstract class DbEntityConfiguration<TEntity> where TEntity : class
{     
    public abstract void Configure(EntityTypeBuilder<TEntity> entity);
}
Run Code Online (Sandbox Code Playgroud)

用法:

internal class UserConfiguration : DbEntityConfiguration<UserDto>
{
    public override void Configure(EntityTypeBuilder<UserDto> entity)
    {
        entity.ToTable("User");
        entity.HasKey(c => c.Id);
        entity.Property(c => c.Username).HasMaxLength(255).IsRequired();
        // etc.
    }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.AddConfiguration(new UserConfiguration());
}
Run Code Online (Sandbox Code Playgroud)

  • @Izzy - DbModelBuilder 是 Entity Framework 6.0,ModelBuilder 是 EF Core。它们是不同的程序集,在这种情况下,问题特定于 EF Core。 (2认同)

Avi*_*rry 28

在EF7中,您在正在实现的DbContext类上重写OnModelCreating.

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Account>()
            .ForRelational(builder => builder.Table("Account"))
            .Property(value => value.Username).MaxLength(50)
            .Property(value => value.Email).MaxLength(255)
            .Property(value => value.Name).MaxLength(255);
    }
Run Code Online (Sandbox Code Playgroud)

  • 所以,如果我有20个实体类型配置,我将它们放在一个巨大的方法中? (23认同)
  • 默认情况下,似乎如此.您可以创建自己的FooMapper/FooModelBuilder类来扩展基类,并且有一个方法可以传递类型化的EntityBuilder <Foo>.你甚至可以使用新的依赖注入和IConfiguration接口让它们自动发现/调用,如果你想要的话! (6认同)
  • 尝试新的依赖注入工具?使用`void MapEntity(ModelBuilder,Type)`签名和`bool IsFor(Type)`创建一个`IEntityMapperStrategy`接口.根据需要多次或多次实现接口(这样你可以创建可以映射多个实体的类),然后创建另一个类(一个策略提供者)来注入所有的IEnumerable` `IEntityMapperStrategies`.请参阅"特殊类型"下的[此处](https://david-driscoll.github.io/2015/04/03/dependency-injection-with-dnx-aka-aspnet5/).将其注入您的上下文中. (4认同)

Joh*_*ohn 21

这是使用当前最新的测试版8.试试这个:

public class AccountMap
{
    public AccountMap(EntityTypeBuilder<Account> entityBuilder)
    {
        entityBuilder.HasKey(x => x.AccountId);

        entityBuilder.Property(x => x.AccountId).IsRequired();
        entityBuilder.Property(x => x.Username).IsRequired().HasMaxLength(50);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的DbContext中:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        new AccountMap(modelBuilder.Entity<Account>());
    }
Run Code Online (Sandbox Code Playgroud)

  • 我最后做了类似的事情.我决定使用静态方法而不是构造函数. (3认同)

小智 16

从 EF Core 2.2 开始,您可以在类中的 OnModelCreating 方法中的一行中添加所有配置(类,实现了 IEntityTypeConfiguration 接口),该类继承自 DbContext 类

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    //this will apply configs from separate classes which implemented IEntityTypeConfiguration<T>
    modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
Run Code Online (Sandbox Code Playgroud)

而且,正如前面的回答中提到的,从 EF Core 2.0 开始,您可以实现接口 IEntityTypeConfiguration,在 Configure 方法中使用 FluentAPI 设置映射配置。

public class QuestionAnswerConfig : IEntityTypeConfiguration<QuestionAnswer>
{
    public void Configure(EntityTypeBuilder<QuestionAnswer> builder)
    {
      builder
        .HasKey(bc => new { bc.QuestionId, bc.AnswerId });
      builder
        .HasOne(bc => bc.Question)
        .WithMany(b => b.QuestionAnswers)
        .HasForeignKey(bc => bc.QuestionId);
      builder
        .HasOne(bc => bc.Answer)
        .WithMany(c => c.QuestionAnswers)
        .HasForeignKey(bc => bc.AnswerId);
    }
}
Run Code Online (Sandbox Code Playgroud)


Coc*_*lla 12

您可以使用反射来完成与它们在EF6中的工作方式非常相似的操作,并为每个实体使用单独的映射类.这适用于RC1决赛:

首先,为您的映射类型创建一个接口:

public interface IEntityTypeConfiguration<TEntityType> where TEntityType : class
{
    void Map(EntityTypeBuilder<TEntityType> builder);
}
Run Code Online (Sandbox Code Playgroud)

然后为每个实体创建一个映射类,例如Person:

public class PersonMap : IEntityTypeConfiguration<Person>
{
    public void Map(EntityTypeBuilder<Person> builder)
    {
        builder.HasKey(x => x.Id);
        builder.Property(x => x.Name).IsRequired().HasMaxLength(100);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,OnModelCreatingDbContext实现中的反射魔法:

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    // Interface that all of our Entity maps implement
    var mappingInterface = typeof(IEntityTypeConfiguration<>);

    // Types that do entity mapping
    var mappingTypes = typeof(DataContext).GetTypeInfo().Assembly.GetTypes()
        .Where(x => x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));

    // Get the generic Entity method of the ModelBuilder type
    var entityMethod = typeof(ModelBuilder).GetMethods()
        .Single(x => x.Name == "Entity" && 
                x.IsGenericMethod && 
                x.ReturnType.Name == "EntityTypeBuilder`1");

    foreach (var mappingType in mappingTypes)
    {
        // Get the type of entity to be mapped
        var genericTypeArg = mappingType.GetInterfaces().Single().GenericTypeArguments.Single();

        // Get the method builder.Entity<TEntity>
        var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg);

        // Invoke builder.Entity<TEntity> to get a builder for the entity to be mapped
        var entityBuilder = genericEntityMethod.Invoke(builder, null);

        // Create the mapping type and do the mapping
        var mapper = Activator.CreateInstance(mappingType);
        mapper.GetType().GetMethod("Map").Invoke(mapper, new[] { entityBuilder });
    }
}
Run Code Online (Sandbox Code Playgroud)


Jam*_*uld 5

这就是我正在进行的项目中所做的工作.

public interface IEntityMappingConfiguration<T> where T : class
{
    void Map(EntityTypeBuilder<T> builder);
}

public static class EntityMappingExtensions
{
     public static ModelBuilder RegisterEntityMapping<TEntity, TMapping>(this ModelBuilder builder) 
        where TMapping : IEntityMappingConfiguration<TEntity> 
        where TEntity : class
    {
        var mapper = (IEntityMappingConfiguration<TEntity>)Activator.CreateInstance(typeof (TMapping));
        mapper.Map(builder.Entity<TEntity>());
        return builder;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

在Context的OnModelCreating方法中:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder
            .RegisterEntityMapping<Card, CardMapping>()
            .RegisterEntityMapping<User, UserMapping>();
    }
Run Code Online (Sandbox Code Playgroud)

示例映射类:

public class UserMapping : IEntityMappingConfiguration<User>
{
    public void Map(EntityTypeBuilder<User> builder)
    {
        builder.ToTable("User");
        builder.HasKey(m => m.Id);
        builder.Property(m => m.Id).HasColumnName("UserId");
        builder.Property(m => m.FirstName).IsRequired().HasMaxLength(64);
        builder.Property(m => m.LastName).IsRequired().HasMaxLength(64);
        builder.Property(m => m.DateOfBirth);
        builder.Property(m => m.MobileNumber).IsRequired(false);
    }
}
Run Code Online (Sandbox Code Playgroud)

我喜欢利用Visual Studio 2015的折叠行为来做的另一件事是对于名为"User"的实体,将映射文件命名为"User.Mapping.cs",Visual Studio将折叠解决方案资源管理器中的文件这样它就包含在实体类文件中.