C# EF代码对象列表中的第一个通用Add方法

C# EF代码对象列表中的第一个通用Add方法,c#,asp.net-mvc,entity-framework,generics,C#,Asp.net Mvc,Entity Framework,Generics,我有一个应用程序,它使用插件风格的方法()来填充我的EF代码优先数据库。在PreApplicationStartMethod期间,我从每个插件(dll)中提取一个对象列表,以加载到实体框架中。我试图使用这种通用方法(),但它说T是一个对象,而不是我在插件中指定的特定类型。结果,插入失败,因为我的上下文中没有对象dbset。如果手动将对象转换为其原始类型,也会发生同样的情况 IModule.cs(主项目) 公共接口模块 { 列表>实体{get;} } Module.cs(插件) 公共类模块:IMo

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

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

Module.cs(插件)

公共类模块:IModule
{
公开名单实体
{
得到
{
列表实体=新列表();
列表渲染=新列表();
添加(新的FieldRendering(1,“文本”,“字符串”));
添加(新的FieldRendering(2,“日期”);
添加(新的FieldRendering(3,“隐藏”、“自动”);
添加(渲染);
返回实体;
}
}
}
预应用单元(主项目)

[程序集:System.Web.PreApplicationStartMethod(typeof(Core.Modules.preapplicationnit),“InitializeModules”)]
公共类预应用单元
{
私有静态语境;
公共静态void InitializeModules()
{
//用于拉入模块的代码(工作正常,因此为简洁起见禁止使用)
上下文=新上下文();
foreach(模块实体中的列表部分)
{
foreach(节中的对象实体)
{
//检查此处的实体对象表示正确的类型(FieldRendering)
添加(实体);
}
SaveChanges();
}
}
私有静态void Add(T实体),其中T:class
{
//此处检查T表示类型(对象)不正确
//代码在此失败
context.Set().Add(实体);
}
}

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

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

var addMethod = typeof(PreApplicationInit)
        .GetMethod("Add", BindingFlags.Static | BindingFlags.NonPublic)
        .MakeGenericMethod(o.GetType());
addMethod.Invoke(null, new[] { entity  });
如果要多次调用,我会创建一个
Dictionary()
,以便在创建MethodInfo对象时将其存储在中,因为创建速度很慢


您还可以将
Add()
方法设置为非泛型,并在
Add()
中对
context.Set()
使用反射。此代码正在正常工作

当在对象列表中循环时,Add(entity)调用将“object”分配给T,因为它知道类型

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

上下文是否有Set(Type t)方法

如果是这样,您可以轻松地执行以下操作:-

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

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

foreach (object entity in section)
{
    context.Set(entity.GetType()).Add(entity);
}
泛型类型参数使用的类型在编译时确定。在这种情况下,实体的编译时类型是
对象
。您需要使用实体的运行时类型来获取要使用的数据库集,而不是使用泛型类型参数

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

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

    try
    {
        context.SaveChanges();
    }
    catch (Exception e)
    {
        //do nothing
    }
}

谢谢大家,@will和@aaronbregger方法的挑战在于
context.Set(entity.GetType())
返回一个DbSet而不是一个idset。这方面的问题是,当我尝试检查实体是否已经存在时,我无法使用
set.Contains(entity)
,因为这是IDbSet特有的,并且当我尝试使用反射来获取标记有键属性的属性时,我遇到了与反射相同的问题,反射查看类型对象而不是特定类型。今晚我来看看@TheevillPenguin的方法。这很有效!最终代码:
字典类型=新字典();foreach(节中的对象实体){if(!types.ContainsKey(entity.GetType()){MethodInfo addMethod=typeof(PreApplicationInit).GetMethod(“添加”,BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(entity.GetType());types.Add(entity.GetType(),addMethod);}types[entity.GetType()].Invoke(null,new[]{entity});
foreach (object entity in section)
{
    context.Set(entity.GetType()).Add(entity);
}
foreach (object entity in section)
{
    context.Set(entity.GetType()).Add(entity);

    try
    {
        context.SaveChanges();
    }
    catch (Exception e)
    {
        //do nothing
    }
}