简体   繁体   中英

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. During the PreApplicationStartMethod I am pulling a list of objects from each plugin (dll) to load into entity framework. 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. As a result the insert fails because I don't have a object dbset in my context. The same happens if I manually convert the objects to their original type.

IModule.cs (main project) public interface IModule { List> Entities { get; } }

Module.cs (plugin)

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)

[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.

You could also make the Add() method non-generic, and use reflection on context.Set<T>() within Add() .

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.

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.

Does the Context have a Set(Type t) method?

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:

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 . 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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