onmodelcreating framework c# entity-framework-core

c# - framework - EF Core Mapping EntityTypeConfiguration



entity framework entity configuration (14)

¿Estoy en lo cierto?

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

Puedo pasar la configuración:

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

En EF6 usualmente podemos usar esta forma para configurar la Entidad.

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

Cómo podemos hacer en EF Core, ya que cuando la clase I Hereda EntityTypeConfiguration que no puede encontrar la clase.

Descargo el código fuente bruto de EF Core del GitHub, no puedo encontrarlo. ¿Alguien puede ayudar en esto?



En EF Core 2.0 hay IEntityTypeConfiguration<TEntity> . Puedes usarlo así:

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

Más sobre esto y otras características nuevas en 2.0 se pueden encontrar here .


En EF7, anulas OnModelCreating en la clase DbContext que estás implementando.

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


En Entity Framework Core 2.0:

Tomé la respuesta de Cocowalla y la adapté para 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 }); } } }

Y se usa en el DbContext así:

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

Y así es como se crea una configuración de tipo de entidad para una entidad:

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


En ef core tenemos que impelentar IEntityTypeConfiguration en lugar de EntityTypeConfiguration, en este caso tenemos acceso completo a DbContext modelBuilder y podemos usar una API fluida pero en ef core esta API es un poco diferente de las versiones anteriores. Puede encontrar más detalles sobre la configuración del modelo de núcleo ef en

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


Esto es lo que estoy haciendo en un proyecto en el que estoy trabajando actualmente.

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

Uso:

En el método OnModelCreating de su Contexto:

protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); builder .RegisterEntityMapping<Card, CardMapping>() .RegisterEntityMapping<User, UserMapping>(); }

Ejemplo de clase de mapeo:

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

Otra cosa que me gusta hacer para aprovechar el comportamiento de plegado de Visual Studio 2015 es para una Entidad llamada ''Usuario'', nombre su archivo de mapeo ''User.Mapping.cs'', Visual Studio doblará el archivo en el explorador de soluciones para que esté contenido en el archivo de clase de entidad.


Esto está usando la última versión actual, beta 8. Prueba esto:

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

Luego en tu DbContext:

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


Puede lograr esto a través de algunos tipos adicionales simples:

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

Uso:

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


Puede usar la reflexión para hacer las cosas de manera muy similar a cómo funcionan en EF6, con una clase de mapeo separada para cada entidad. Esto funciona en RC1 final:

Primero, crea una interfaz para tus tipos de mapeo:

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

Luego crea una clase de mapeo para cada una de tus entidades, por ejemplo, para una clase 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); } }

Ahora, la magia de reflexión en OnModelCreating en su implementación de 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<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 }); } }


Seguí un enfoque similar a la forma en que Microsoft implementó ForSqlServerToTable

usando el método de extensión ...

la marca parcial es obligatoria si desea utilizar el mismo nombre de clase en varios archivos

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

Luego, en DataContext OnModelCreating realiza tu llamada para cada extensión ...

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

De esta forma, seguimos el mismo patrón utilizado por los otros métodos de compilación.

¿Qué piensas?


Simplemente implemente IEntityTypeConfiguration

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

y luego agrégalo a tu entidad. Contexto

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


Tengo un proyecto que le permite configurar entidades fuera de DbContext.OnModelCreating Usted configura cada entidad en una clase separada que hereda de StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration

Primero necesita crear una clase que herede de StaticDotNet.EntityFrameworkCore.ModelConfiguration.EntityTypeConfiguration<TEntity> donde TEntity es la clase que desea configurar.

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

Luego, en su clase de Inicio, solo necesita decirle a Entity Framework dónde encontrar todas sus clases de configuración cuando configure su DbContext.

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

También hay una opción para agregar configuraciones de tipo usando un proveedor. El repositorio tiene documentación completa sobre cómo usarlo.

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


Terminé con esta solución:

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

Uso de muestra:

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

y

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