EF从Type获取运行时记录列表

时间:2014-10-21 18:47:52

标签: c# entity-framework type-conversion dbcontext typeof

目的: 我需要循环所有记录,如:

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

然后循环

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

但是它必须在运行时没有特定类型,因为我要遍历类型,因此不知道示例&#34; UserAccount&#34;。 只有Type / TypeOf?

在本说明的底部,我有一个方法 loopAllEntities ,我找不到工作方式

我用一些实体创建了一个DbContext。

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

定义一个Type列表来控制输出:

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

问题出现在我无法找到方法使其工作的地方: - (

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

3 个答案:

答案 0 :(得分:1)

看起来你几乎就在那里,你应该可以做类似以下的事情:

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

这将为您提供一个非泛型集合。它取决于你想要做什么,因为这个集合没有类型它可能会有点棘手,可能会转换为基类型或接口来使用?

编辑:我没有足够的声誉来评论,但是Mayoors解决方案可以在不使用非泛型类型的情况下为您提供所需的内容,并且可以预先获得整个集合。

答案 1 :(得分:1)

我最初认为你不能只获得一种类型的实体,但是看看马特的答案,你可以。所以,这个答案可能并不是最好的答案......

基本想法是获取IEnumerable<object>的列表,其中每个IEnumerable<object>是我们模型中的实体。我们必须使用普通旧object的原因是因为我们希望它返回彼此不相关的不同实体。但是,这并不是那么糟糕,因为当你循环遍历每个对象时,如果需要,可以将它转换为某个实体。

首先,我们将创建一个返回此列表的方法:

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

我们传入DbContext因为它会在我们实际开始循环遍历此方法之外的IEnumerables时使用。因此,我们无法在此处DbContext创建Dispose

我们现在可以使用此方法遍历所有实体:

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

就是这样。就SQL而言,对于外部foreach循环的每次迭代,它将执行SELECT * FROM TABLE_NAME之类的操作。这意味着它不会缓存List<IEnumerable<object>>内的数据,每次使用AllEntities时都会从数据库中获取新数据。

答案 2 :(得分:0)

我知道这是一个老问题,但问题并没有指定EF版本,并且提议的答案不再适用于Entity Framework Core(DbContext在EF核心中至少在日期时没有非通用的set方法)这个答案)。

然而,你仍然可以使用Jon Skeet的回答来this question。为方便起见,我的代码在下面添加。

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