简体   繁体   English

EF Core 映射实体类型配置

[英]EF Core Mapping EntityTypeConfiguration

In EF6 we usually able to use this way to configure the Entity.在 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);
    }
}

How we can do in EF Core, since when the class I Inherit EntityTypeConfiguration that unable to find the class.我们如何在 EF Core 中做,因为当我继承 EntityTypeConfiguration 时无法找到该类。

I download the EF Core raw source code from the GitHub, I can't find it.我从 GitHub 下载了 EF Core 原始源代码,我找不到它。 Can someone help on this?有人可以帮忙吗?

Since EF Core 2.0 there is IEntityTypeConfiguration<TEntity> .由于 EF Core 2.0 有IEntityTypeConfiguration<TEntity> You can use it like this:你可以这样使用它:

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());

More on this and other new features introduced in 2.0 can be found here .可以在此处找到有关此功能和 2.0 中引入的其他新功能的更多信息

You can achieve this through some simple additional types:您可以通过一些简单的附加类型来实现这一点:

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);
}

Usage:用法:

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());
}

In EF7, you override OnModelCreating on the DbContext class you're implementing.在 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);
    }

This is using current latest, beta 8. Try this:这是使用当前最新的 beta 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);
    }
}

Then in your DbContext:然后在您的 DbContext 中:

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

        new AccountMap(modelBuilder.Entity<Account>());
    }

Since EF Core 2.2 you can add all configs (classes, which implemented IEntityTypeConfiguration interface) in one line in the method OnModelCreating in class, which is inherited from DbContext class从 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());
}

And, as it was mentioned in the previous answer, since EF Core 2.0, you can implement the interface IEntityTypeConfiguration, setup mapping configuration with using FluentAPI in Configure method.并且,正如在前面的答案中提到的,从 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);
    }
}

You can use reflection to do things very similarly to how they work in EF6, with a separate mapping class for each entity.您可以使用反射来做与它们在 EF6 中的工作方式非常相似的事情,每个实体都有一个单独的映射类。 This works in RC1 final:这适用于 RC1 决赛:

First, create an interface for your mapping types:首先,为您的映射类型创建一个接口:

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

Then create a mapping class for each of your entities, eg for a Person class:然后为每个实体创建一个映射类,例如为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);
    }
}

Now, the reflection magic in OnModelCreating in your DbContext implementation:现在,在您的DbContext实现中OnModelCreating中的反射魔法:

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 });
    }
}

This is what I am doing in a project I'm currently working on.这就是我在我目前正在进行的项目中所做的。

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;
    }
}

Usage:用法:

In your Context's OnModelCreating method:在您的 Context 的 OnModelCreating 方法中:

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

        builder
            .RegisterEntityMapping<Card, CardMapping>()
            .RegisterEntityMapping<User, UserMapping>();
    }

Example mapping class:示例映射类:

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);
    }
}

One other thing I like to do to take advantage of the folding behavior of Visual Studio 2015 is for an Entity called 'User', you name your mapping file 'User.Mapping.cs', Visual Studio will fold the file in the solution explorer so that it is contained under the entity class file.为了利用 Visual Studio 2015 的折叠行为,我喜欢做的另一件事是对于名为“User”的实体,您将映射文件命名为“User.Mapping.cs”,Visual Studio 将在解决方案资源管理器中折叠该文件以便它包含在实体类文件下。

I ended with this solution:我以这个解决方案结束:

public interface IEntityMappingConfiguration
{
    void Map(ModelBuilder b);
}

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

public abstract class EntityMappingConfiguration<T> : IEntityMappingConfiguration<T> where T : class
{
    public abstract void Map(EntityTypeBuilder<T> b);

    public void Map(ModelBuilder b)
    {
        Map(b.Entity<T>());
    }
}

public static class ModelBuilderExtenions
{
    private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface)
    {
        return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
    }

    public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly)
    {
        var mappingTypes = assembly.GetMappingTypes(typeof (IEntityMappingConfiguration<>));
        foreach (var config in mappingTypes.Select(Activator.CreateInstance).Cast<IEntityMappingConfiguration>())
        {
            config.Map(modelBuilder);
        }
    }
}

Sample Use:样品用途:

public abstract class PersonConfiguration : EntityMappingConfiguration<Person>
{
    public override void Map(EntityTypeBuilder<Person> b)
    {
        b.ToTable("Person", "HumanResources")
            .HasKey(p => p.PersonID);

        b.Property(p => p.FirstName).HasMaxLength(50).IsRequired();
        b.Property(p => p.MiddleName).HasMaxLength(50);
        b.Property(p => p.LastName).HasMaxLength(50).IsRequired();
    }
}

and

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
}

Just implement the IEntityTypeConfiguration只需实现 IEntityTypeConfiguration

public abstract class EntityTypeConfiguration<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : class
{
    public abstract void Configure(EntityTypeBuilder<TEntity> builder);
}

and then add it to your entity Context然后将其添加到您的实体 Context

public class ProductContext : DbContext, IDbContext
{
    public ProductContext(DbContextOptions<ProductContext> options)
        : base((DbContextOptions)options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfiguration(new ProductMap());
    }

    public DbSet<Entities.Product> Products { get; set; }
}

In ef core we have to impelement IEntityTypeConfiguration instead of EntityTypeConfiguration in this case we have full access to DbContext modelBuilder and we can use fluent api but in ef core this api is a litle bit diferent from previous versions.在 ef 核心中,我们必须使用 IEntityTypeConfiguration 而不是 EntityTypeConfiguration,在这种情况下,我们可以完全访问 DbContext 模型构建器,并且可以使用 fluent api,但在 ef 核心中,此 api 与以前的版本略有不同。 you can find more details on ef core model configuration on您可以在上找到有关 ef 核心模型配置的更多详细信息

https://www.learnentityframeworkcore.com/configuration/fluent-api https://www.learnentityframeworkcore.com/configuration/fluent-api

In Entity Framework Core 2.0:在实体框架核心 2.0 中:

I took Cocowalla's answer and adapted it for v2.0:我接受了 Cocowalla 的回答并将其改编为 v2.0:

    public static class ModelBuilderExtenions
    {
        private static IEnumerable<Type> GetMappingTypes(this Assembly assembly, Type mappingInterface)
        {
            return assembly.GetTypes().Where(x => !x.IsAbstract && x.GetInterfaces().Any(y => y.GetTypeInfo().IsGenericType && y.GetGenericTypeDefinition() == mappingInterface));
        }

        public static void AddEntityConfigurationsFromAssembly(this ModelBuilder modelBuilder, Assembly assembly)
        {
            // Types that do entity mapping
            var mappingTypes = assembly.GetMappingTypes(typeof(IEntityTypeConfiguration<>));

            // 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(modelBuilder, null);

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


    }

And it's used in the DbContext like this:它在 DbContext 中是这样使用的:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
    }

And this is how you create an entity type configuration for an entity:这就是为实体创建实体类型配置的方式:

    public class UserUserRoleEntityTypeConfiguration : IEntityTypeConfiguration<UserUserRole>
    {
        public void Configure(EntityTypeBuilder<UserUserRole> builder)
        {
            builder.ToTable("UserUserRole");
            // compound PK
            builder.HasKey(p => new { p.UserId, p.UserRoleId });
        }
    }
      public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int RollNumber { get; set; }
        public string Address { get; set; }
    }
    
    public class StudentMapper : IEntityTypeConfiguration<Student>
    {
        public void Configure(EntityTypeBuilder<Student> builder)
        {
            builder.ToTable("tbl_Students");
            builder.HasKey(x => x.Id);
            builder.Property(x => x.Address)
                        .HasMaxLength(250);
            builder.Property(x => x.Name)
                     .HasMaxLength(50);

            builder.Property(x => x.Name).HasColumnName("varchar_name");
        }

        
    }
    
     public class DataContext : DbContext
    {
        public DataContext(DbContextOptions options): base(options)
        {
                
        }
        public DbSet<Student> Students { get; set; }
        //DbSets
        protected override void OnModelCreating(ModelBuilder modelBuilder) {
            //for fluent API approach and also we can create a separate mapper for each entity
            #region student
                        
          
            modelBuilder.ApplyConfiguration(new StudentMapper());
            #endregion
        }
    }

Am I right?我对吗?

public class SmartModelBuilder<T> where T : class         {

    private ModelBuilder _builder { get; set; }
    private Action<EntityTypeBuilder<T>> _entityAction { get; set; }

    public SmartModelBuilder(ModelBuilder builder, Action<EntityTypeBuilder<T>> entityAction)
    {
        this._builder = builder;
        this._entityAction = entityAction;

        this._builder.Entity<T>(_entityAction);
    }
}   

I can Pass config:我可以通过配置:

 protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);



        new SmartModelBuilder<Blog>(builder, entity => entity.Property(b => b.Url).Required());

    } 

I followed a similar approach to the way Microsoft implemented ForSqlServerToTable我遵循了与 Microsoft 实施 ForSqlServerToTable 的方式类似的方法

using extension method...使用扩展方法...

the partial flag is required if you want to use the same class name in multiple files如果要在多个文件中使用相同的类名,则需要部分标志

public class ConsignorUser
{
    public int ConsignorId { get; set; }

    public string UserId { get; set; }

    public virtual Consignor Consignor { get; set; }
    public virtual User User { get; set; }

}

public static partial class Entity_FluentMappings
{
    public static EntityTypeBuilder<ConsignorUser> AddFluentMapping<TEntity> (
        this EntityTypeBuilder<ConsignorUser> entityTypeBuilder) 
        where TEntity : ConsignorUser
    {
       entityTypeBuilder.HasKey(x => new { x.ConsignorId, x.UserId });
       return entityTypeBuilder;
    }      
}

Then in the DataContext OnModelCreating make your call for each extension...然后在 DataContext OnModelCreating 中调用每个扩展...

 public class DataContext : IdentityDbContext<User>
{

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        // Customize the ASP.NET Identity model and override the defaults if needed.
        // For example, you can rename the ASP.NET Identity table names and more.
        // Add your customizations after calling base.OnModelCreating(builder);

        builder.Entity<ConsignorUser>().AddFluentMapping<ConsignorUser>();
        builder.Entity<DealerUser>().AddFluentMapping<DealerUser>();           

    }

This way we are following the same pattern used by the other builder methods.这样我们就遵循了其他构建器方法使用相同模式。

What do you thing?你有什么事?

Well here is the issue for the enhancement on the EF7 Github repo: https://github.com/aspnet/EntityFramework/issues/2805那么这里是 EF7 Github 存储库增强的问题: https : //github.com/aspnet/EntityFramework/issues/2805

You can track the issue directly there, altough its is still only in backlog without designated priority.您可以直接在那里跟踪问题,尽管它仍然只是在没有指定优先级的积压中。

I have a project that allows you to configure entities outside of the DbContext.OnModelCreating You configure each entity in a seperate class which inherits from StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration我有一个项目,允许您在DbContext.OnModelCreating之外配置实体您在继承自StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration的单独类中配置每个实体

First you need to create a class which inherits from StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration<TEntity> where TEntity is the class you want to configure.首先,您需要创建一个继承自StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration<TEntity>的类,其中TEntity是您要配置的类。

using StaticDotNet.EntityFrameworkCore.ModelConfiguration;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class ExampleEntityConfiguration
    : EntityTypeConfiguration<ExampleEntity>
{
    public override void Configure( EntityTypeBuilder<ExampleEntity> builder )
    {
        //Add configuration just like you do in DbContext.OnModelCreating
    }
}

Then in your Startup class you just need to tell Entity Framework where to find all of your configuration classes when you are configuring your DbContext.然后在您的 Startup 类中,您只需要在配置 DbContext 时告诉 Entity Framework 在哪里可以找到所有配置类。

using StaticDotNet.EntityFrameworkCore.ModelConfiguration;

public void ConfigureServices(IServiceCollection services)
{
    Assembly[] assemblies = new Assembly[]
    {
        // Add your assembiles here.
    };

    services.AddDbContext<ExampleDbContext>( x => x
        .AddEntityTypeConfigurations( assemblies )
    );
}

There is also an option for adding type configurations using a provider.还有一个选项可以使用提供程序添加类型配置。 The repo has complete documentation on how to use it.该 repo 有关于如何使用它的完整文档。

https://github.com/john-t-white/StaticDotNet.EntityFrameworkCore.ModelConfiguration https://github.com/john-t-white/StaticDotNet.EntityFrameworkCore.ModelConfiguration

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM