John
John

Reputation: 961

Accessing / executing generic methods via reflection?

I'm trying to dynamically switch out my table annotations' schema values @ Runtime when using EF6.

So here's what I've got thus far:

var builder = new DbModelBuilder()
var dbSetProperties = typeof(T).GetProperties().Where(p => p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>));

foreach (PropertyInfo property in dbSetProperties)
{
    Type[] propTypes = property.PropertyType.GetGenericArguments();

    // Iterate the DbSets and set the correct schema
    foreach (Type dbSetType in propTypes)
    {
        // Get the TableAttribute
        var tableAttribute = Attribute.GetCustomAttribute(dbSetType, typeof(TableAttribute));

        MethodInfo dbModelMethodInfo = typeof(DbModelBuilder).GetMethod("Entity");
        MethodInfo entityTypeConfigMethodInfo = typeof(EntityTypeConfiguration<>).GetMethod("ToTable", new[] { typeof(String), typeof(String) });
        MethodInfo genericDbModelMethodInfo = dbModelMethodInfo.MakeGenericMethod(dbSetType);

        genericDbModelMethodInfo.Invoke(builder, null);
        entityTypeConfigMethodInfo.Invoke(genericDbModelMethodInfo, new Object[] { (tableAttribute as TableAttribute).Name, "NEW_SCHEMA_VALUE" });
    }
}

What I'm trying to accomplish is something like this (which doesn't work):

builder.Entity<dbSetType>().ToTable((tableAttribute as TableAttribute).Name, "NEW_SCHEMA_VALUE");

Basically, for T I want to pull the DbSets, determine the Class used in the Entity<> generic, get the TableAttribute, and set the Schema to a new value.

Currently, on entityTypeConfigMethodInfo.Invoke, I'm getting an error of "Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true".

What am I missing?

Upvotes: 1

Views: 188

Answers (3)

SunilA
SunilA

Reputation: 583

Since EntityTypeConfiguration is a generic Type , could you specify the type of Generic parameter being used in your object for EntityTypeConfiguration. Ex :

 Type type= typeof (GenericClass<>).MakeGenericType(typeof(int));

Here 'GenericClass' is a generic class with generic 'T' parameter. I have specified int type while getting the type information of it. Then Invoke the method on this type will work fine.

I have written a sample code which does the same, but minimally what you are trying to do and may help you solve your error.

 class Program
{
    static void Main()
    {

        Type type= typeof (GenericClass<>).MakeGenericType(typeof(int));

        var method = type.GetMethod("TestMethod");

        var instance = Activator.CreateInstance(type);


        method.Invoke(instance, null); 

    }
}

public class GenericClass<T> where T : struct // These parameters can be anything
{
    public T TestMethod()
    {
        T a = new T();

        return a; 
    }
}

Upvotes: 0

thehennyy
thehennyy

Reputation: 4218

To get the right closed generic methodinfo for the second call you rewrite your code to:

MethodInfo genericDbModelMethodInfo = dbModelMethodInfo.MakeGenericMethod(dbSetType);
MethodInfo entityTypeConfigMethodInfo = genericDbModelMethodInfo.ReturnType.GetMethod("ToTable", new[] { typeof(String), typeof(String) });

Then you have to use the return value of the genericDbModelMethodInfo.Invoke call as the first parameter in the second call.

var obj = genericDbModelMethodInfo.Invoke(builder, null);
entityTypeConfigMethodInfo.Invoke(obj, new Object[] { (tableAttribute as TableAttribute).Name, "NEW_SCHEMA_VALUE" });

That is because the first parameter in MethodInfo.Invoke is the object you want to invoke the method on.

Upvotes: 1

Lasse V. Karlsen
Lasse V. Karlsen

Reputation: 391346

Well, yes, what did you expect?

You say you get the error on this line:

entityTypeConfigMethodInfo.Invoke(genericDbModelMethodInfo, new Object[] { (tableAttribute as TableAttribute).Name, "NEW_SCHEMA_VALUE" });

The problem is not with this line, but with this line:

MethodInfo entityTypeConfigMethodInfo = typeof(EntityTypeConfiguration<>).GetMethod("ToTable", new[] { typeof(String), typeof(String) });

You're getting the method ToTable of which type?

The type containing that method is still an open generic type. You must specify the type arguments to make it a specific type, then you can get the method to invoke.

In other words, you need this:

MethodInfo entityTypeConfigMethodInfo =
    typeof(EntityTypeConfiguration<>).MakeGenericType(...).GetMethod("ToTable", new[] { typeof(String), typeof(String) });
                                     ^-------------------^

Upvotes: 3

Related Questions