简体   繁体   English

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

[英]EF Code First Generic Add Method From List of Objects

I have an application that uses a plugin style methodology ( Plug-in architecture for ASP.NET MVC ), to populate my EF code first database. 我有一个使用插件样式方法( ASP.NET MVC的插件架构 )的应用程序来填充我的EF代码第一个数据库。 During the PreApplicationStartMethod I am pulling a list of objects from each plugin (dll) to load into entity framework. 在PreApplicationStartMethod期间,我从每个插件(dll)中提取对象列表以加载到实体框架中。 I'm trying to use this generic approach ( Generic method to insert record into DB ) but it says that T is an object rather than the specific type that I have specified in the plugins. 我正在尝试使用这种通用方法( 将记录插入数据库的通用方法 ),但它说T是一个对象而不是我在插件中指定的特定类型。 As a result the insert fails because I don't have a object dbset in my context. 因此插入失败,因为我的上下文中没有对象dbset。 The same happens if I manually convert the objects to their original type. 如果我手动将对象转换为其原始类型,也会发生同样的情况。

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

Module.cs (plugin) 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 (main project) 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);
    }
}

When inferring generic arguments the compiler uses the declared type of the variables passed, not what that variable contains at runtime. 在推断泛型参数时,编译器使用传递的变量的声明类型,而不是该变量在运行时包含的内容。

If you want to do things this way, you could use reflection to call the method with the correct generic argument: 如果您想以这种方式执行操作,可以使用反射来使用正确的泛型参数调用该方法:

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

If it's going to be called many times, I would create a Dictionary<Type, MethodInfo>() to store the MethodInfo objects in as you create them, as creation is slow. 如果要多次调用它,我会创建一个Dictionary<Type, MethodInfo>()来在创建它时存储MethodInfo对象,因为创建很慢。

You could also make the Add() method non-generic, and use reflection on context.Set<T>() within Add() . 您还可以使Add()方法非泛型,并在Add() context.Set<T>()上使用反射。

This code is working as it should do. 这段代码正常运行。

When looping through a list of objects, the Add(entity) call assigns 'object' to T as that is what it knows about the type. 循环遍历对象列表时,Add(entity)调用将'object'分配给T,因为它就是它所知道的类型。

Perhaps the best solution here is to have each of your entity modules derive from a given interface, that then allows you to cast it to that interface to register with the Context - I have not been able to test if this works though. 也许这里最好的解决方案是让每个实体模块都从一个给定的接口派生,然后允许你将它转换为该接口以向Context注册 - 我无法测试它是否有效。

Does the Context have a Set(Type t) method? Context是否有Set(Type t)方法?

If so you could easily just do:- 如果是这样,你可以很容易地做到: -

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

Use the overloaded DbContext.Set method that accepts a type object instead of a generic type parameter: 使用重载的DbContext.Set方法接受类型对象而不是泛型类型参数:

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

The type used by a generic type parameter is determined at compile time. 泛型类型参数使用的类型在编译时确定。 In this case, the compile time type of your entity is object . 在这种情况下,实体的编译时类型是object Instead of using a generic type parameter, you need to use the runtime type of your entity to get the DbSet you want to work with. 您需要使用实体的运行时类型来获取要使用的DbSet,而不是使用泛型类型参数。

If you need to continuously add new entities to your set, you could catch the exception thrown when your context saves changes for each entity. 如果需要不断向集合中添加新实体,则可以捕获当上下文保存每个实体的更改时抛出的异常。

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM