Dansi
Dansi

Reputation: 1

Using reflection in EF6 OnModelCreating to rename all DbSet properties to lowercase

I'm working on a C# Winforms project (targeting .NET Framework 4.6.1) and I want to reflect this function for each DbSet:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .Property(e => e.Name)
        .HasColumnName("name");
}

I need to automate the configuration of my OnModelCreating method. This is necessary because, due to legacy reasons, I must create all database columns in lowercase in PostgreSQL. I haven't found a setting in PostgreSQL that allows me to query tables like SELECT * FROM User without using quotes unless the tables and columns are created in lowercase. Also using data annotations is not an option since I need this in Pascal case for another reason.

I have successfully converted table names to lowercase, but I'm struggling with dynamically configuring the property names (i.e., column names) using reflection.

Here's the approach I'm currently using:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // Dynamically iterate through all DbSet properties
    var dbSetProperties = this.GetType().GetProperties()
        .Where(p => p.PropertyType.IsGenericType &&
                    p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>));
   
    foreach (var property in dbSetProperties)
    {
        var entityType = property.PropertyType.GenericTypeArguments[0]; // Entity type
        var tableName = entityType.Name.ToLower(); // Convert table name to lowercase

        // Register the entity and set the table name
        var method = modelBuilder.GetType()
            .GetMethod("Entity", new Type[] { })
            .MakeGenericMethod(entityType); // Create generic method

        var entityConfiguration = method.Invoke(modelBuilder, null); // Call the Entity method

        // Set the table name
        var toTableMethod = entityConfiguration.GetType()
            .GetMethod("ToTable", new[] { typeof(string) });
        toTableMethod.Invoke(entityConfiguration, new object[] { tableName });
    }
}

However, I need to also set the property names (column names) to lowercase, but I'm not sure how to do that dynamically.

So far I reached this point, but I always fail on loading the property Method and use the lambda expression on it.

void LowerProperties(DbModelBuilder modelBuilder)
{
     var dbSetProperties = this.GetType()
         .GetProperties(BindingFlags.Public | BindingFlags.Instance)
         .Where(p => p.PropertyType.IsGenericType &&
                      p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>));

     foreach (var dbSetProperty in dbSetProperties)
     {
         Type entityType = dbSetProperty.PropertyType.GetGenericArguments()[0];

         var properties = entityType.GetProperties();

         foreach (var property in properties)
         {
             string propertyName = property.Name;

             MethodInfo entityMethod = modelBuilder.GetType()
                                                   .GetMethod("Entity")
                                                   .MakeGenericMethod(entityType);

             object entityConfig = entityMethod.Invoke(modelBuilder, null);

             ParameterExpression param = Expression.Parameter(entityType, "e");
             MemberExpression propertyAccess = Expression.Property(param, propertyName);
             LambdaExpression lambda = Expression.Lambda(propertyAccess, param);

             Type configType = typeof(StructuralTypeConfiguration<>).MakeGenericType(entityType);

             dynamic typ;

             if (property.PropertyType == typeof(int))
             {
                  typ = typeof(Func<,>).MakeGenericType(entityType, typeof(decimal));
             }
             else
             {
                  typ = typeof(Func<,>).MakeGenericType(entityType, property.PropertyType);
             }

             MethodInfo propertyMethod = configType.GetMethod("Property",
                 BindingFlags.Public | BindingFlags.Instance,
                 null,
                 new Type[] { typ },
                 null);
             
             object stringPropertyConfig = propertyMethod.Invoke(entityConfig, new object[] { lambda });

             MethodInfo hasColumnNameMethod = stringPropertyConfig.GetType()
                 .GetMethod("HasColumnName", new[] { typeof(string) });

             hasColumnNameMethod.Invoke(stringPropertyConfig, new object[] { propertyName.ToLower() });

         }
     }
}

Can anyone help me figure out how to achieve this?

Upvotes: 0

Views: 54

Answers (1)

David Browne - Microsoft
David Browne - Microsoft

Reputation: 88852

It took me a while to remember how to do this in EF6. You should really migrate to EF Core and .NET Core. It has Windows Forms. Anyway, like this:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Properties().Configure( p => p.HasColumnName(p.ClrPropertyInfo.Name.ToLower()) );
        base.OnModelCreating(modelBuilder);
    }

Upvotes: 0

Related Questions