【问题标题】:How to pass a System.Type into a generic method using reflection [duplicate]如何使用反射将 System.Type 传递给泛型方法
【发布时间】:2023-04-03 23:30:02
【问题描述】:

可能重复:
How to use reflection to call generic Method?

我正在尝试简化一些 EF Code First 配置。

不要像这样写代码:

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

我将每个类型声明的表包装到一个类中,并使用反射来调用模型构建器

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

由于编译时安全性,无法将类型添加为泛型参数。那么是否可以使用反射进行相同的调用?

目的是像调用builder一样

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    TablePerTypeBuilder<Asset> builder = new TablePerTypeBuilder<Asset>();
    builder.Build(modelBuilder);
}

【问题讨论】:

  • 看看MakeGenericMethod():msdn.microsoft.com/en-us/library/…
  • MakeGenericMethod 可以和参考参数一起使用吗?在这种情况下,我认为我们不想创建调用类的新实例。
  • 你是如何简化它的?看起来您必须使用您的解决方案编写更多代码。除了在编译时进行类型检查,这样做有什么好处?
  • 当我想在系统中引入更多资产时...

标签: c# entity-framework generics reflection


【解决方案1】:

按照建议,您可以使用MakeGenericMethod()。但它有很多这样丑陋的打字:

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

【讨论】:

  • 小更新 - 绑定标志应该是 BindingFlags.InvokeMethod
【解决方案2】:

您可以构建一个表达式并将其编译为委托:

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

执行时可以调用:

public void BuildInternal<T>(DbModelBuilder builder) where T : class
{
  builder.Entity<T>.ToTable(typeof(T).Name);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多