Question

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.

Was it helpful?

Solution

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 });

        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top