问题
I'm trying to make a class which binds all classes which inherit a certain interface to be executed in an event fired by a custom entity framework model builder.
What I have:
1) A generic interface IOverride which has a method Configure(EntityTypeConfiguration entity)
2) Several classes that implement the interface.
public class ItemOverride : IOverride<Item>
{
public void Configure(EntityTypeConfiguration<Item> entity)
{
// Do something with entity
}
}
3) A method which gathers all classes which define the interface and combines them into a list of IOverride's like this:
var list = (from x in assembly.GetTypes()
from z in x.GetInterfaces()
let y = x.BaseType
where
(y != null && y.IsGenericType &&
typeof (IOverride<>).IsAssignableFrom(y.GetGenericTypeDefinition())) ||
(z.IsGenericType &&
typeof (IOverride<>).IsAssignableFrom(z.GetGenericTypeDefinition()))
select z).ToList();
4) I try to follow that up with invoking the method Configure in an event...
var method = typeof (IOverride<>).GetMethod("Configure")
var entityMethod = typeof (DbModelBuilder).GetMethod("Entity");
foreach (var item in list)
{
var target = item.GetGenericArguments().Single();
var invoked = entityMethod.MakeGenericMethod(target).Invoke(args.ModelBuilder, new object[] {});
var func = method.MakeGenericMethod(item);
ModelCreating += (o, eventArgs) => func.Invoke(invoked, new object[] {});
}
However, when running the actual event, I'm getting a
Additional information: Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.
Exception on the func.Invoke() line. I'm clearly doing something wrong, but I'm not 100% sure what it is. I'm thinking maybe Reflection is confusing me when I'm feeling under the weather or maybe I just did something incredibly stupid. Anyway a nudge in the right direction could suffice.
回答1:
The problem is that you are getting method from IOverride<> and not from real type. So it hasn't references to inheritor. And also when you call func.Invoke you should pass instance of inheritor of IOverride<>, and now you are passing EntityTypeConfiguration<T> that you get in invoked variable. But this variable should go as parameter.
Here is working example.
var entityMethod = typeof(DbModelBuilder).GetMethod("Entity");
var builder = args.ModelBuilder;
foreach (Type x in assembly.GetTypes())
{
foreach (Type z in x.GetInterfaces())
{
Type y = x.BaseType;
if ((y != null && y.IsGenericType && typeof(IOverride<>).IsAssignableFrom(y.GetGenericTypeDefinition())) ||
(z.IsGenericType && typeof(IOverride<>).IsAssignableFrom(z.GetGenericTypeDefinition())))
{
// first difference - we get Configure method from real object, and not from IOverride
var method = x.GetMethod("Configure");
var target = z.GetGenericArguments().Single();
var invoked = entityMethod.MakeGenericMethod(target).Invoke(builder, new object[] { });
var func = method.MakeGenericMethod(typeof(string));
// this one block can go to ModelCreating event as in your example,
// but locally i didn't use it
// second difference - we create instance of original type
var obj = Activator.CreateInstance(x);
// third one difference - we pass instance as `this` and pass invoked as parameter
func.Invoke(obj, new[] { invoked });
}
}
}
来源:https://stackoverflow.com/questions/21701054/invoke-a-method-from-a-generic-interface-with-a-parameter-of-a-result-of-another