从类型中在运行时获取记录列表的EF方式

6
目的: 我需要循环遍历所有记录,例如:
var records = db.Set<UserAccount>().ToList();

然后循环。
foreach (var record in records)
{
    // do something with the record 
}

但是它在运行时不能指定类型,因为我需要循环遍历类型,所以不知道例如“UserAccount”。 只有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; }
}

定义了一系列的类型来控制输出:

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个回答

2

我知道这是一个老问题,但问题没有指定EF版本,而且所提出的答案在Entity Framework Core中不再起作用(至少在此回答发布时,DbContext在EF core中没有非泛型集合方法)。

然而,您仍然可以使用Jon Skeet对这个问题的答案来编写一个有效的扩展方法。为了方便起见,我的代码如下。

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

谢谢您的输入 :-) 看起来非常有趣! - pladekusken

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的解决方案可以让您在不使用非泛型类型并且一次性获取整个集合的情况下获得所需结果。

谢谢@Matt。这正是我所需要的 :-) - pladekusken

1

我最初认为你不能仅凭类型获取实体,但看了Matt的答案后,你可以。所以,这个答案可能不是最好的...

基本思路是获取一个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时都会从数据库获取新鲜数据。


谢谢你的回答。我从你的代码中得到了灵感,我认为我可以在另一个上下文中使用它。 @matt 给出的答案正是我所需要的 :-) - pladekusken

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接