[英]EF Core Mapping EntityTypeConfiguration
在 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);
}
}
我們如何在 EF Core 中做,因為當我繼承 EntityTypeConfiguration 時無法找到該類。
我從 GitHub 下載了 EF Core 原始源代碼,我找不到它。 有人可以幫忙嗎?
由於 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());
您可以通過一些簡單的附加類型來實現這一點:
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);
}
用法:
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());
}
在 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);
}
這是使用當前最新的 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);
}
}
然后在您的 DbContext 中:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
new AccountMap(modelBuilder.Entity<Account>());
}
從 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());
}
並且,正如在前面的答案中提到的,從 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);
}
}
您可以使用反射來做與它們在 EF6 中的工作方式非常相似的事情,每個實體都有一個單獨的映射類。 這適用於 RC1 決賽:
首先,為您的映射類型創建一個接口:
public interface IEntityTypeConfiguration<TEntityType> where TEntityType : class
{
void Map(EntityTypeBuilder<TEntityType> builder);
}
然后為每個實體創建一個映射類,例如為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);
}
}
現在,在您的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 });
}
}
這就是我在我目前正在進行的項目中所做的。
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;
}
}
用法:
在您的 Context 的 OnModelCreating 方法中:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder
.RegisterEntityMapping<Card, CardMapping>()
.RegisterEntityMapping<User, UserMapping>();
}
示例映射類:
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);
}
}
為了利用 Visual Studio 2015 的折疊行為,我喜歡做的另一件事是對於名為“User”的實體,您將映射文件命名為“User.Mapping.cs”,Visual Studio 將在解決方案資源管理器中折疊該文件以便它包含在實體類文件下。
我以這個解決方案結束:
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);
}
}
}
樣品用途:
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();
}
}
和
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
}
只需實現 IEntityTypeConfiguration
public abstract class EntityTypeConfiguration<TEntity> : IEntityTypeConfiguration<TEntity> where TEntity : class
{
public abstract void Configure(EntityTypeBuilder<TEntity> builder);
}
然后將其添加到您的實體 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; }
}
在 ef 核心中,我們必須使用 IEntityTypeConfiguration 而不是 EntityTypeConfiguration,在這種情況下,我們可以完全訪問 DbContext 模型構建器,並且可以使用 fluent api,但在 ef 核心中,此 api 與以前的版本略有不同。 您可以在上找到有關 ef 核心模型配置的更多詳細信息
https://www.learnentityframeworkcore.com/configuration/fluent-api
在實體框架核心 2.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 });
}
}
}
它在 DbContext 中是這樣使用的:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.AddEntityConfigurationsFromAssembly(GetType().Assembly);
}
這就是為實體創建實體類型配置的方式:
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
}
}
我對嗎?
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);
}
}
我可以通過配置:
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());
}
我遵循了與 Microsoft 實施 ForSqlServerToTable 的方式類似的方法
使用擴展方法...
如果要在多個文件中使用相同的類名,則需要部分標志
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;
}
}
然后在 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>();
}
這樣我們就遵循了其他構建器方法使用的相同模式。
你有什么事?
那么這里是 EF7 Github 存儲庫增強的問題: https : //github.com/aspnet/EntityFramework/issues/2805
您可以直接在那里跟蹤問題,盡管它仍然只是在沒有指定優先級的積壓中。
我有一個項目,允許您在DbContext.OnModelCreating
之外配置實體您在繼承自StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration
的單獨類中配置每個實體
首先,您需要創建一個繼承自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
}
}
然后在您的 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 )
);
}
還有一個選項可以使用提供程序添加類型配置。 該 repo 有關於如何使用它的完整文檔。
https://github.com/john-t-white/StaticDotNet.EntityFrameworkCore.ModelConfiguration
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.