web-dev-qa-db-fra.com

Boucle / réflexion dans toutes les propriétés de tous les modèles EF pour définir le type de colonne

Mon client a une norme de stockage des décimales SQL Server avec une spécification décimale (13,4). En conséquence, dans un schéma très grand et toujours croissant, j'ai près d'une centaine de déclarations comme celles-ci:

builder.Entity<MyObject>()
    .Property(x => x.MyField1)
    .ForSqlServerHasColumnType("decimal(13,4)");
builder.Entity<MyObject>()
    .Property(x => x.MyField2)
    .ForSqlServerHasColumnType("decimal(13,4)");
builder.Entity<MyObject2>()
    .Property(x => x.MyField1)
    .ForSqlServerHasColumnType("decimal(13,4)");

S'il y a une fonctionnalité où je peux dire à EF directement que toutes les décimales doivent être décimales (13,4) par défaut, je voudrais l'utiliser. Sinon, puis-je utiliser la réflexion pour parcourir tous les objets/propriétés du modèle afin de pouvoir le faire en quelques instructions?

Quelque chose comme:

foreach(var efObj in EntityFrameWorkObjects)
{
    foreach (var objProperty in efObj)
    {
        if (objProperty is decimal || objProperty is decimal?)
        {
            builder.Entity<efObj>()
                .Property(x => x.efObj)
                .ForSqlServerHasColumnType("decimal(13,4)");
        }
    }
}

La réflexion semble être un excellent chemin à parcourir, car je peux alors implémenter certaines de nos autres conventions où, si un objet a un nom et une description, le nom est requis et limité à 256 caractères.

pdate: J'ai suivi le lien dans le commentaire d'Ivan et l'ai adapté à cela, ce qui fonctionne pour moi:

foreach (var p in builder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => 
        p.ClrType == typeof(decimal) ||
        p.ClrType == typeof(decimal?)))
{
    p.SqlServer().ColumnType = "decimal(13,4)";
}

Peu de temps après, il a fourni une réponse complète, que j'ai légèrement modifiée pour travailler à la fois avec la décimale et la décimale nullable:

foreach (var pb in builder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => 
        p.ClrType == typeof(decimal) ||
        p.ClrType == typeof(decimal?))
    .Select(p => 
        builder.Entity(p.DeclaringEntityType.ClrType)
            .Property(p.Name)))
{
    pb.ForSqlServerHasColumnType("decimal(13,4)");
}

Les deux approches fonctionnent!

pdate 2: J'ai dû faire déclarer mes objets comme DbSet <> dans le contexte pour que ce qui précède fonctionne. Cela ne semblait pas être nécessaire lorsque je définissais les propriétés ligne par ligne.

18
Chris

Dans EF Core v1.1.0, vous pouvez utiliser quelque chose comme ceci:

foreach (var pb in modelBuilder.Model
    .GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?))
    .Select(p => modelBuilder.Entity(p.DeclaringEntityType.ClrType).Property(p.Name)))
{
    pb.ForSqlServerHasColumnType("decimal(13,4)");
}

pdate: À partir d'EF Core 2.0, le modèle est construit séparément pour chaque fournisseur de base de données, donc les méthodes HasAbcXyz sont remplacées par les HasXyz communes. Le code mis à jour (qui ignore également les propriétés configurées explicitement) ressemble à ceci:

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
{
    if (property.Relational().ColumnType == null)
        property.Relational().ColumnType = "decimal(13,4)";
}
30
Ivan Stoev