EF get list of records in runtime from Type

久未见 提交于 2020-01-13 10:36:12

问题


Purpose: I need to loop all records like:

var records = db.Set<UserAccount>().ToList();

Then loop

foreach (var record in records)
{
    // do something with the record 
}

But it has to not type specific in runtime, as I am to loop through types and therefor do not know example "UserAccount". Only the Type/TypeOf?

In the bottom of this description I have a method loopAllEntities that I cannot find a way to work

I have created an DbContext with some entities.

public class MyEntities : DbContext
{
    public DbSet<UserAccount> UserAccounts { get; set;}
    public DbSet<UserRole> UserRoles { get; set; }
    public DbSet<UserAccountRole> UserAccountRoles { get; set; }
}

Defined a list of Type to control the output:

public static List<Type> ModelListSorted()
{
    List<Type> modelListSorted = new List<Type>();
    modelListSorted.Add(typeof(UserRole));
    modelListSorted.Add(typeof(UserAccountRole));
    modelListSorted.Add(typeof(UserAccount));
    return modelListSorted;
}

The problem is below where I cannot find a way to make it Work :-(

public static loopAllEntities()
{
    List<Type> modelListSorted = ModelHelper.ModelListSorted();

    foreach (Type type in modelListSorted) 
    {           
        var records = ????? // get a list of records in the current table from type.
        foreach (var record in records)
        {
            // do something with the record
        } 
    }
}

回答1:


It looks like you are almost there, you should be able to do something like the following:

    public static void loopAllEntities(DbContext dbContext)
    {
        List<Type> modelListSorted = ModelHelper.ModelListSorted();

        foreach (Type type in modelListSorted) 
        {
            var records = dbContext.Set(type);
            // do something with the set here
        }
    }

This will get you a non generic set to work with. It depends from there what you want to do as there is no type to this set it can get a bit tricky, maybe cast to a base type or interface to work with?

Edit: I don't have enough reputation to comment, but Mayoors solution would get you what you want without using a non-generic type and get the whole collection up front.




回答2:


I know this is an old question, yet the question does not specify EF version and the proposed answer does not work anymore for Entity Framework Core (DbContext does not have non-generic set method in EF core at least at the date of this answer).

Yet you can still have a working extension method using the answer of Jon Skeet to this question. My code is added below for convenience.

    public static IQueryable Set(this DbContext context, Type T)
    {

        // Get the generic type definition
        MethodInfo method = typeof(DbContext).GetMethod(nameof(DbContext.Set), BindingFlags.Public | BindingFlags.Instance);

        // Build a method with the specific type argument you're interested in
        method = method.MakeGenericMethod(T);

        return method.Invoke(context, null) as IQueryable;
    }



回答3:


I had initially thought that you couldn't get entities with just a type, but looking at Matt's answer, you can. So, this answer probably isn't the best one...

The basic idea is to get a list of IEnumerable<object>, where each IEnumerable<object> is an Entity in our model. The reason we have to use plain old object is because we want it to return different entities which are not related to each other. But, that isn't so bad, since as you loop through each object, you can cast it to a certain entity if you need to.

First, we'll create a method that returns this list:

private static List<IEnumerable<object>> AllEntities(MyEntities db)
{
    List<IEnumerable<object>> list = new List<IEnumerable<object>>();
    list.Add(db.UserRole);
    list.Add(db.UserAccountRole);
    list.Add(db.UserAccount);

    return list;
}

We pass in our DbContext because it will get used when we actually start looping through the IEnumerables, which occurs outside of this method. So, we can't create the DbContext in here and then Dispose of it.

We can now use this method to loop through all of our entities:

using (var db = GetMyEntities())
{
    List<IEnumerable<object>> recordsList = AllEntities(db);
    foreach (IEnumerable<object> records in recordsList)
    {
        foreach (object record in records)
        {
            // Do something with record.
            //  If you need to access type-specific properties,
            //   do something like below
            if (record is UserAccount)
            {
                UserAccount account = (UserAccount)record;
                Console.WriteLine("User Name: " + account.UserName);
            }
        }
    }
}

And that is it. In terms of SQL, it will do something like SELECT * FROM TABLE_NAME for each iteration of the outer foreach loop. This means it is not caching the data inside of the List<IEnumerable<object>>, and every time you use AllEntities it is getting fresh data from the database.



来源:https://stackoverflow.com/questions/26494050/ef-get-list-of-records-in-runtime-from-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!