Reputation: 4954
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
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:
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.Extensions
.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.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