3

Possible Duplicate:
How to use reflection to call generic Method?

I'm trying to simplify some EF Code First configuration.

Instead of writing code like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Asset>().ToTable("Assets");
    modelBuilder.Entity<VideoAsset>().ToTable("VideoAssets");
    modelBuilder.Entity<ImageAsset>().ToTable("ImageAssets");
    ...
}

I'm wrapping the table per type declaration into a class and using reflection to call the modelBuilder

public class TablePerTypeBuilder<TBase> where TBase : class
{
    public void Build(DbModelBuilder modelBuilder)
    {
        //modelBuilder.Entity<Asset>().ToTable("Assets");
        modelBuilder.Entity<TBase>().ToTable(typeof(TBase).Name);

        //modelBuilder.Entity<VideoAsset>().ToTable("VideoAssets");
        //modelBuilder.Entity<ImageAsset>().ToTable("ImageAssets");
        var types = from a in AppDomain.CurrentDomain.GetAssemblies()
                    from t in a.GetTypes()
                    where typeof(TBase).IsAssignableFrom(t)
                    select t;

        foreach (Type type in types)
        {           
            modelBuilder.Entity<type>().ToTable(type.Name); 
            //Error - The type or namespace name 'type' could not be found (are you missing a using directive or an assembly reference?)
        }
    }
}

It's not possible to add a Type as a generic parameter due to compile time safety. So is it possible to make same call using refection?

The aim is to call the builder like

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    TablePerTypeBuilder<Asset> builder = new TablePerTypeBuilder<Asset>();
    builder.Build(modelBuilder);
}
Community
  • 1
  • 1
Fixer
  • 5,985
  • 8
  • 40
  • 58
  • Take a look at `MakeGenericMethod()`: http://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.makegenericmethod%28v=VS.100%29.aspx – dlev Sep 20 '11 at 16:26
  • Can MakeGenericMethod be used with reference parameters? In this case i don't think we want to create a new instance of the calling class. – Fixer Sep 20 '11 at 16:30
  • How are you simplifying it? It looks like you would have to write more code with your solution. Other then type checking at compile time, what would be the benefit of doing it this way? – Dmitry Samuylov Sep 20 '11 at 16:30
  • When i want to introduce more Assets into the system... – Fixer Sep 20 '11 at 16:33

2 Answers2

3

As suggested, you can use MakeGenericMethod(). But its a lot of ugly typing like that:

var method = modelBuilder.GetType().GetMethod("Entity");
var genericMethod = method.MakeGenericMethod(type);
var entTypConfig = genericMethod.Invoke(modelBuilder, null);
entTypConfig.GetType()
    .InvokeMember("ToTable", BindingFlags.InvokeMethod, null, entTypConfig, 
                  new object[] {type.Name});
Jan
  • 15,802
  • 5
  • 35
  • 59
1

You could build an expression and compile it down into a delegate:

public void Build(DbModelBuilder builder) 
{
  // Stuff

  var param = Expression.Parameter(typeof(DbModelBuilder));
  foreach (var type in types)
  {
    var method = Expression.Call(
      Expression.Constant(this),      // Call to self.
      "BuildInternal",                // The method to call.
      new[] { type },                 // The generic arguments.
      param);                        // The parameters.

    Expression.Lambda(method, param).Compile().DynamicInvoke(builder);
  }
}

Which when executed could call:

public void BuildInternal<T>(DbModelBuilder builder) where T : class
{
  builder.Entity<T>.ToTable(typeof(T).Name);
}
Matthew Abbott
  • 60,571
  • 9
  • 104
  • 129