Ok, je me sers donc d’une structure d’entité avec les migrations en premier point et en code. Ce n'est pas un problème en tant que tel, je me demandais simplement si quelqu'un avait trouvé un meilleur moyen de le faire.
Actuellement, j'ai de nombreuses configurations de type d'entité comme
public class ExampleEntityConfiguration : IEntityTypeConfiguration<ExampleEntity>
{
public void Configure(EntityTypeBuilder<ExampleEntity> builder)
{
builder.Property(p => p.Id).ValueGeneratedNever();
// more options here
}
}
et je les enregistre dans mon dbcontext comme si
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfiguration(new ExampleEntityConfiguration());
// lot's more configurations here
}
quelqu'un a-t-il rencontré ou connaît-il un moyen d'enregistrer toutes les interfaces IEntityTypeConfiguration
?
Cela ressemble à beaucoup de code répétitif qui pourrait être résolu en obtenant une liste des configurations, en les parcourant et en les appliquant dans le contexte. Je ne sais tout simplement pas par où commencer pour obtenir une liste de classes IEntityTypeConfiguration
qui existent dans un espace de noms particulier.
Toute aide/suggestion serait génial.
Cela peut être fait avec une réflexion comme celle-ci:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// get ApplyConfiguration method with reflection
var applyGenericMethod = typeof(ModelBuilder).GetMethod("ApplyConfiguration", BindingFlags.Instance | BindingFlags.Public);
// replace GetExecutingAssembly with Assembly where your configurations are if necessary
foreach (var type in Assembly.GetExecutingAssembly().GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters))
{
// use type.Namespace to filter by namespace if necessary
foreach (var iface in type.GetInterfaces()) {
// if type implements interface IEntityTypeConfiguration<SomeEntity>
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) {
// make concrete ApplyConfiguration<SomeEntity> method
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
// and invoke that with fresh instance of your configuration type
applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
break;
}
}
}
}
Le travail de Nice de @Evk peut être encapsulé dans une méthode d'extension réutilisable:
public static class ModelBuilderExtensions
{
public static void ApplyAllConfigurationsFromCurrentAssembly(this ModelBuilder modelBuilder, Assembly assembly, string configNamespace = "")
{
var applyGenericMethods = typeof(ModelBuilder).GetMethods( BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy);
var applyGenericApplyConfigurationMethods = applyGenericMethods.Where(m => m.IsGenericMethod && m.Name.Equals("ApplyConfiguration", StringComparison.OrdinalIgnoreCase));
var applyGenericMethod = applyGenericApplyConfigurationMethods.Where(m=>m.GetParameters().FirstOrDefault().ParameterType.Name== "IEntityTypeConfiguration`1").FirstOrDefault();
var applicableTypes = Assembly
.GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters);
if (!string.IsNullOrEmpty(configNamespace))
{
applicableTypes = applicableTypes.Where(c => c.Namespace == configNamespace);
}
foreach (var type in applicableTypes)
{
foreach (var iface in type.GetInterfaces())
{
// if type implements interface IEntityTypeConfiguration<SomeEntity>
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
{
// make concrete ApplyConfiguration<SomeEntity> method
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
// and invoke that with fresh instance of your configuration type
applyConcreteMethod.Invoke(modelBuilder, new object[] { Activator.CreateInstance(type) });
Console.WriteLine("applied model " + type.Name);
break;
}
}
}
}
}
Et peut être appelé comme tel:
public class ApiDbContext : DbContext
{
public DbSet<Customer> Customers { get; set; }
public ApiDbContext(DbContextOptions<ApiDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyAllConfigurationsFromCurrentAssembly("MyRoot.Api.Entities.Configuration");
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
base.OnConfiguring(optionsBuilder);
}
}
La réponse de @Evk est un peu dépassée et ne fonctionne plus, la réponse de @paul van bladel est celle qui fonctionne, même si elle contient des chaînes codées en dur, que je me suis le plus chiché. Si quelqu'un partage mon sentiment, j'aimerais proposer ma solution sous la forme d'une méthode d'extension statique:
public static ModelBuilder ApplyAllConfigurationsFromAssembly(
this ModelBuilder modelBuilder, Assembly assembly)
{
var applyGenericMethod = typeof(ModelBuilder)
.GetMethods(BindingFlags.Instance | BindingFlags.Public)
.Single(m => m.Name == nameof(ModelBuilder.ApplyConfiguration)
&& m.GetParameters().Count() == 1
&& m.GetParameters().Single().ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
foreach (var type in Assembly.GetTypes()
.Where(c => c.IsClass && !c.IsAbstract && !c.ContainsGenericParameters))
{
foreach (var iface in type.GetInterfaces())
{
if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>))
{
var applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]);
applyConcreteMethod.Invoke(modelBuilder, new object[] {Activator.CreateInstance(type)});
break;
}
}
}
}
Et, si vos classes de configuration sont stockées dans le même assemblage que votre DbContext, vous utilisez cette méthode comme suit:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyAllConfigurationsFromAssembly(GetType().Assembly);
base.OnModelCreating(modelBuilder);
}
En utilisant EF Core 2.2+, c'est devenu beaucoup plus simple:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
Assembly assemblyWithConfigurations = GetType().Assembly; //get whatever Assembly you want
modelBuilder.ApplyConfigurationsFromAssembly(assemblyWithConfigurations);
}
J'aime beaucoup la réponse fournie par @paul van bladel pour déplacer le code vers une méthode d'extension. J'ai également besoin d'appeler cela depuis d'autres assemblys. J'ai donc créé une énumération et modifié les types applicablesTypes afin qu'ils soient définis différemment.
IEnumerable<Type> assemblyTypeList;
switch (pAssemblyMethodType)
{
case AssemblyMethodType.CallingAssembly:
assemblyTypeList = Assembly.GetCallingAssembly()
.GetTypes()
.Where(c => c.IsClass
&& !c.IsAbstract
&& !c.ContainsGenericParameters);
break;
case AssemblyMethodType.ExecutingAssembly:
assemblyTypeList = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(c => c.IsClass
&& !c.IsAbstract
&& !c.ContainsGenericParameters);
break;
default:
throw new ArgumentOutOfRangeException(nameof(pAssemblyMethodType), pAssemblyMethodType, null);
}