rbasniak
rbasniak

Reputation: 4954

Pass parameter to function via reflection

I have an extesion method with the following signature

public static void AddConfiguration<TEntity>(this ModelBuilder builder, EntityTypeConfiguration<TEntity> configuration)
    where TEntity : class
{
    ...
}

I want to pass the parameter via Reflection and tried this method:

var ctors = type.GetConstructors(BindingFlags.Public);
modelBuilder.AddConfiguration(ctors[0].Invoke(new object[] { }));

And this way:

modelBuilder.AddConfiguration(Activator.CreateInstance(type));

The classes I'm getting from reflection are like these:

public class LessonMapping : EntityTypeConfiguration<Lesson> 
public class ChapterMapping : EntityTypeConfiguration<Chapter> 
public class TrainingMapping : EntityTypeConfiguration<Training> 

Both return an object so the method does not accept them. Is there any way to do that?

Upvotes: 1

Views: 1339

Answers (1)

strongbutgood
strongbutgood

Reputation: 675

Activator has a generic method for creating typed instances that have a default constructor. By the looks of it you have that, so use the following:

var obj = Activator.CreateInstance<LessonMapping>();
modelBuilder.AddConfiguration(obj);

If that doesn't work for you or you need to pass arguments to your constructors:

var obj = (LessonMapping)Activator.CreateInstance(typeof(LessonMapping));
modelBuilder.AddConfiguration(obj);

Finally if you are putting this code inside another generic method you would replace LessonMapping etc with a generic:

void DoSomethingGeneric<TEntity>(ModelBuilder builder)
{
    var obj = Activator.CreateInstance<EntityTypeConfiguration<TEntity>>();
    modelBuilder.AddConfiguration(obj);
}

EDIT add reflection of AddConfiguration method

For a non-generic usage of your AddConfiguration method you will have to use reflection to invoke it:

void DoSomethingNonGeneric(ModelBuilder builder, Type entityType)
{
    // make the generic type for the given entity type using the generic definition
    var entityConfigType = typeof(EntityTypeConfiguration<>).MakeGenericType(new Type[] { entityType });

    // instantiate the entity type configuration
    var obj = Activator.CreateInstance(entityConfigType);

    // get and make a generic method to invoke with the object above. Check the binding flags if it doesn't find the method
    var methodDef = typeof(Extensions).GetMethod("AddConfiguration", BindingFlags.Static | BindingFlags.Public);
    var method = methodDef.MakeGenericMethod(new Type[] { entityConfigType });

    // and finally the end goal
    method.Invoke(null, new object[] { builder, obj });
}

My assumptions at this point are:

  1. That you want to pass the type of the entities (as opposed to the configuration). If you are passing the type of the configuration classes then change the parameter name entityType to entityConfigType and remove the first line which makes the generic type. See below for type checking code that should replace that first line.
  2. That your extension method is in a class called Extensions.
  3. That you are checking the type conforms to the requirements of AddConfiguration(...) so you don't get invocation exceptions due to bad types being used. Again see below for an example of the type check I would typically add to such a non-generic method.
  4. That the line which sets methodDef works as expected because I don't have means to test it at the moment.

If you are passing the entityConfigType directly and want to check the type then add this in the first few lines:

if (!CheckDerivesFrom(typeof(EntityTypeConfiguration<>), entityConfigType)
    throw new ArgumentException("entityConfigType");

And create the check method somewhere. I'm sure there must be an easier way to test if a type derives from a given generic definition but I use this all the time until I get shown the easier way.

bool CheckDerivesFrom(Type baseType, Type typeToCheck)
{
    if (!baseType.IsGenericDefinition)
        return baseType.IsAssignableFrom(typeToCheck);

    // assume typeToCheck is never a generic definition
    while (typeToCheck != null && typeToCheck != typeof(object))
    {
        if (typeToCheck.IsGenericType && baseType.IsAssignableFrom(typeToCheck.GetGenericDefinition()))
            return true;
        typeToCheck = typeToCheck.BaseType;
    }
    return false;
}

Upvotes: 2

Related Questions