【问题标题】:EF Code First Generic Add Method From List of Objects来自对象列表的 EF Code First 通用添加方法
【发布时间】:2013-09-07 01:19:51
【问题描述】:

我有一个使用插件式方法 (Plug-in architecture for ASP.NET MVC) 的应用程序来填充我的 EF 代码优先数据库。在 PreApplicationStartMethod 期间,我从每个插件 (dll) 中提取对象列表以加载到实体框架中。我正在尝试使用这种通用方法(Generic method to insert record into DB),但它说 T 是一个对象,而不是我在插件中指定的特定类型。结果插入失败,因为我的上下文中没有对象数据库集。如果我手动将对象转换为其原始类型,也会发生同样的情况。

IModule.cs(主项目) 公共接口IModule { 列表>实体{获取; } }

Module.cs(插件)

public class Module : IModule
{
    public List<List<object>> Entities
    {
        get
        {
            List<List<object>> entities = new List<List<object>>();

            List<object> renderings = new List<object>();
            renderings.Add(new FieldRendering(1, "Text", "String"));
            renderings.Add(new FieldRendering(2, "Date", "Date"));
            renderings.Add(new FieldRendering(3, "Hidden", "Auto"));

            entities.Add(renderings);

            return entities;
        }
    }
}

PreApplicationInit(主项目)

[assembly: System.Web.PreApplicationStartMethod(typeof(Core.Modules.PreApplicationInit), "InitializeModules")]
public class PreApplicationInit
{
    private static Context context;

    public static void InitializeModules()
    {
        // code to pull in the modules (works, so suppressing for brevity)

        context = new Context();

        foreach (List<object> section in module.Entities)
        {
            foreach (object entity in section)
            {
                // Inspecting the entity object here indicates the correct type (FieldRendering)
                Add(entity);
            }

            context.SaveChanges();
        }
    }

    private static void Add<T>(T entity) where T : class
    {
        // Inspecting T here indicates the incorrect type (object)
        // Code fails here
        context.Set<T>().Add(entity);
    }
}

【问题讨论】:

  • 谢谢大家,@will 和@aaronbregger 方法的挑战在于context.Set(entity.GetType()) 返回的是DbSet 而不是IDbSet。问题是当我尝试检查实体是否已经存在时,我不能使用set.Contains(entity),因为它是特定于 IDbSet 的,当我尝试使用反射来获取标有 key 属性的属性时,我遇到了同样的问题查看类型对象而不是特定类型的反射问题。今晚我会看看@TheEvilPenguin 的做法。

标签: c# asp.net-mvc entity-framework generics


【解决方案1】:

在推断泛型参数时,编译器使用传递的变量的声明类型,而不是该变量在运行时包含的类型。

如果你想以这种方式做事,你可以使用反射来调用具有正确泛型参数的方法:

var addMethod = typeof(PreApplicationInit)
        .GetMethod("Add", BindingFlags.Static | BindingFlags.NonPublic)
        .MakeGenericMethod(o.GetType());
addMethod.Invoke(null, new[] { entity  });

如果要被多次调用,我会创建一个Dictionary&lt;Type, MethodInfo&gt;() 来在您创建 MethodInfo 对象时存储它们,因为创建速度很慢。

您还可以将Add() 方法设为非泛型,并在Add() 内对context.Set&lt;T&gt;() 使用反射。

【讨论】:

  • 这行得通!最终代码:Dictionary&lt;Type, MethodInfo&gt; types = new Dictionary&lt;Type, MethodInfo&gt;(); foreach (object entity in section) { if (!types.ContainsKey(entity.GetType())) { MethodInfo addMethod = typeof(PreApplicationInit) .GetMethod("Add",BindingFlags.Static | BindingFlags.NonPublic) .MakeGenericMethod(entity.GetType()); types.Add(entity.GetType(), addMethod); } types[entity.GetType()].Invoke(null, new[] { entity });
【解决方案2】:

此代码正在正常工作。

在遍历对象列表时,Add(entity) 调用将“对象”分配给 T,因为这是它所知道的类型。

也许这里最好的解决方案是让您的每个实体模块都从给定的接口派生,然后允许您将其强制转换为该接口以向 Context 注册 - 但我无法测试这是否有效。

Context 有 Set(Type t) 方法吗?

如果是这样,您可以轻松做到:-

context.Set(entity.GetType()).Add(entity);

【讨论】:

    【解决方案3】:

    使用接受类型对象而不是泛型类型参数的重载DbContext.Set 方法:

    foreach (object entity in section)
    {
        context.Set(entity.GetType()).Add(entity);
    }
    

    泛型类型参数使用的类型是在编译时确定的。在这种情况下,您的实体的编译时类型是object。您需要使用实体的运行时类型来获取要使用的 DbSet,而不是使用泛型类型参数。

    如果您需要不断地向集合中添加新实体,您可以捕获上下文保存每个实体的更改时引发的异常。

    foreach (object entity in section)
    {
        context.Set(entity.GetType()).Add(entity);
    
        try
        {
            context.SaveChanges();
        }
        catch (Exception e)
        {
            //do nothing
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2015-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多