EF 代码从对象列表中的第一个泛型添加方法

本文关键字:第一个 泛型 添加 方法 代码 对象 列表 EF | 更新日期: 2023-09-27 18:30:26

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

IModule.cs(主项目) 公共接口接口 { List> Entities { get; } }

模块.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;
        }
    }
}

预申请初始化(主项目)

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

EF 代码从对象列表中的第一个泛型添加方法

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

如果要以这种方式执行操作,可以使用反射来调用具有正确泛型参数的方法:

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

如果要多次调用它,我会创建一个Dictionary<Type, MethodInfo>(),以便在创建 MethodInfo 对象时将它们存储在其中,因为创建速度很慢。

您还可以使Add()方法非泛型,并在Add()中使用对context.Set<T>()的反射。

这段代码正在正常工作。

当循环遍历对象列表时,Add(entity) 调用将"object"分配给 T,因为这是它所知道的类型。

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

上下文是否有 Set(类型 t) 方法?

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

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

使用接受类型对象而不是泛型类型参数的重载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
    }
}