EF Core Mapping EntityTypeConfiguration

In EF6 solitamente usiamo questo modo per configurare l’entity framework.

public class AccountMap : EntityTypeConfiguration { 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); } } 

Come possiamo fare in EF Core, da quando la class I Inherit EntityTypeConfiguration non è in grado di trovare la class.

Ho scaricato il codice sorgente raw di EF Core da GitHub, non riesco a trovarlo. Qualcuno può aiutarci?

Dal momento che EF Core 2.0 contiene IEntityTypeConfiguration . Puoi usarlo in questo modo:

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

Maggiori informazioni su questa e altre nuove funzionalità introdotte nel 2.0 possono essere trovate qui .

È ansible ottenere questo attraverso alcuni semplici tipi aggiuntivi:

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

Uso:

 internal class UserConfiguration : DbEntityConfiguration { public override void Configure(EntityTypeBuilder 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, si esegue l’override di OnModelCreating nella class DbContext che si sta implementando.

 protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity() .ForRelational(builder => builder.Table("Account")) .Property(value => value.Username).MaxLength(50) .Property(value => value.Email).MaxLength(255) .Property(value => value.Name).MaxLength(255); } 

Questo sta usando la versione più recente, la beta 8. Prova questo:

 public class AccountMap { public AccountMap(EntityTypeBuilder entityBuilder) { entityBuilder.HasKey(x => x.AccountId); entityBuilder.Property(x => x.AccountId).IsRequired(); entityBuilder.Property(x => x.Username).IsRequired().HasMaxLength(50); } } 

Quindi nel tuo DbContext:

  protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); new AccountMap(modelBuilder.Entity()); } 

È ansible utilizzare la riflessione per fare le cose in modo molto simile a come funzionano in EF6, con una class di mapping separata per ciascuna entity framework. Funziona in RC1 finale:

Innanzitutto, crea un’interfaccia per i tuoi tipi di mapping:

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

Quindi crea una class di mapping per ciascuna delle tue quadro, ad esempio per una class Person :

 public class PersonMap : IEntityTypeConfiguration { public void Map(EntityTypeBuilder builder) { builder.HasKey(x => x.Id); builder.Property(x => x.Name).IsRequired().HasMaxLength(100); } } 

Ora, la magia di riflessione in OnModelCreating nella tua implementazione di DbContext :

 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 var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg); // Invoke builder.Entity 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 }); } } 

Questo è quello che sto facendo in un progetto al quale sto lavorando attualmente.

 public interface IEntityMappingConfiguration where T : class { void Map(EntityTypeBuilder builder); } public static class EntityMappingExtensions { public static ModelBuilder RegisterEntityMapping(this ModelBuilder builder) where TMapping : IEntityMappingConfiguration where TEntity : class { var mapper = (IEntityMappingConfiguration)Activator.CreateInstance(typeof (TMapping)); mapper.Map(builder.Entity()); return builder; } } 

Uso:

Nel metodo OnModelCreating del tuo contesto:

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

Esempio di class di mapping:

 public class UserMapping : IEntityMappingConfiguration { public void Map(EntityTypeBuilder 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); } } 

Un’altra cosa che mi piace fare per sfruttare il comportamento di piegatura di Visual Studio 2015 è per un’entity framework chiamata ‘Utente’, si nomina il file di mapping ‘User.Mapping.cs’, Visual Studio piegherà il file in Solution Explorer in modo che sia contenuto nel file di class dell’entity framework.

Ho finito con questa soluzione:

 public interface IEntityMappingConfiguration { void Map(ModelBuilder b); } public interface IEntityMappingConfiguration : IEntityMappingConfiguration where T : class { void Map(EntityTypeBuilder builder); } public abstract class EntityMappingConfiguration : IEntityMappingConfiguration where T : class { public abstract void Map(EntityTypeBuilder b); public void Map(ModelBuilder b) { Map(b.Entity()); } } public static class ModelBuilderExtenions { private static IEnumerable 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()) { config.Map(modelBuilder); } } } 

Esempio di utilizzo:

 public abstract class PersonConfiguration : EntityMappingConfiguration { public override void Map(EntityTypeBuilder 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(); } } 

e

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

Bene, ecco il problema per il miglioramento del repository Github EF7: https://github.com/aspnet/EntityFramework/issues/2805

È ansible tenere traccia del problema direttamente lì, anche se è ancora solo in arretrato senza priorità designata.

Basta implementare IEntityTypeConfiguration

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

e quindi aggiungilo al tuo Contesto quadro

 public class ProductContext : DbContext, IDbContext { public ProductContext(DbContextOptions options) : base((DbContextOptions)options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfiguration(new ProductMap()); } public DbSet Products { get; set; } } 

Ho ragione?

 public class SmartModelBuilder where T : class { private ModelBuilder _builder { get; set; } private Action> _entityAction { get; set; } public SmartModelBuilder(ModelBuilder builder, Action> entityAction) { this._builder = builder; this._entityAction = entityAction; this._builder.Entity(_entityAction); } } 

Posso passare 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(builder, entity => entity.Property(b => b.Url).Required()); } 

Ho seguito un approccio simile al modo in cui Microsoft ha implementato ForSqlServerToTable

usando il metodo di estensione …

il flag parziale è obbligatorio se si desidera utilizzare lo stesso nome di class in più file

 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 AddFluentMapping ( this EntityTypeBuilder entityTypeBuilder) where TEntity : ConsignorUser { entityTypeBuilder.HasKey(x => new { x.ConsignorId, x.UserId }); return entityTypeBuilder; } } 

Quindi nel DataContext OnModelCreating effettua la chiamata per ogni estensione …

  public class DataContext : IdentityDbContext { 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().AddFluentMapping(); builder.Entity().AddFluentMapping(); } 

In questo modo stiamo seguendo lo stesso schema usato dagli altri metodi di builder.

Che cosa fai?

In ef core dobbiamo impelementare IEntityTypeConfiguration invece di EntityTypeConfiguration in questo caso abbiamo pieno accesso a DbContext modelBuilder e possiamo usare fluenti api ma in ef core questa api è un po ‘diversa rispetto alle versioni precedenti. puoi trovare maggiori dettagli sulla configurazione del modello di base di ef su

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

In Entity Framework Core 2.0:

Ho preso la risposta di Cocowalla e l’ho adattata per la versione 2.0:

  public static class ModelBuilderExtenions { private static IEnumerable 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 var genericEntityMethod = entityMethod.MakeGenericMethod(genericTypeArg); // Invoke builder.Entity 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 }); } } } 

Ed è usato nel DbContext come questo:

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

E questo è il modo in cui crei una configurazione del tipo di quadro per un’ quadro:

  public class UserUserRoleEntityTypeConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("UserUserRole"); // compound PK builder.HasKey(p => new { p.UserId, p.UserRoleId }); } } 

DbContext.OnModelCreating un progetto che consente di configurare entity framework esterne a DbContext.OnModelCreating È ansible configurare ogni entity framework in una class separata che eredita da StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration

Per prima cosa è necessario creare una class che erediti da StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration dove TEntity è la class che si desidera configurare.

 using StaticDotNet.EntityFrameworkCore.ModelConfiguration; using Microsoft.EntityFrameworkCore.Metadata.Builders; public class ExampleEntityConfiguration : EntityTypeConfiguration { public override void Configure( EntityTypeBuilder builder ) { //Add configuration just like you do in DbContext.OnModelCreating } } 

Quindi nella tua class di avvio devi solo dire a Entity Framework dove trovare tutte le classi di configurazione quando configuri il tuo DbContext.

 using StaticDotNet.EntityFrameworkCore.ModelConfiguration; public void ConfigureServices(IServiceCollection services) { Assembly[] assemblies = new Assembly[] { // Add your assembiles here. }; services.AddDbContext( x => x .AddEntityTypeConfigurations( assemblies ) ); } 

C’è anche un’opzione per aggiungere configurazioni di tipo usando un provider. Il repository ha una documentazione completa su come usarlo.

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