EF代码优先从对象列表添加方法

时间:2013-09-06 04:20:54

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

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

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

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

3 个答案:

答案 0 :(得分:1)

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

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

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

如果要多次调用它,我会创建一个Dictionary<Type, MethodInfo>()来存储MethodInfo对象,因为创建速度很慢。

您还可以使Add()方法非通用,并在context.Set<T>()内的Add()上使用反射。

答案 1 :(得分:1)

此代码正常运行。

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

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

Context是否有Set(Type t)方法?

如果是这样,你可以很容易地做到: -

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

答案 2 :(得分:0)

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