用EF中的新查询替换不需要的foreach的用法

时间:2019-01-30 07:01:02

标签: c# entity-framework linq

我有一个如下表

Employee
-------------------
id    role 
1      a
2      a
3      b
4      c
5      b
----------------------

我拥有的过滤器是ids {1,3,5} and roles {a,b}

所以我需要获取具有id=1 and role=a , id=2 and role=b , id=3 and role=a, id=3 and role=b etc...的行

基本上id * roles是我需要从数据库中获取的。

我的项目中使用的当前代码如下

foreach (int role in roles)
                    {

                        foreach (int id in ids)
                        {
//get the data using id and role and append to a list 
}
}

并且每次迭代都会发生数据库命中,因此这会造成巨大的永久性问题。因此,我正在尝试改进已有的代码,并已用以下代码替换了foreach。

var itemsTobeDeleted = context.TableName.Where
                       (
                        item =>
                            obj.ids.Contains(item.Id) &&
                            obj.roles.Contains(item.Role)
                        ).ToList();

我的问题是这是我的新查询将给出与每个给定相同的结果吗? 如果是,那么性能如何?

还是我们有更好的方法来做同样的事情?

2 个答案:

答案 0 :(得分:1)

是的,您的第二个解决方案更有效!

您使用DbContext访问表。显然,该表位于数据库中。数据库管理系统在查询数据方面进行了非常优化。

但是,查询中较慢的部分之一是查询和所选数据在流程与DBMS之间的传输。因此,限制这一点是明智的。

您的foreach方法将对每个[role,id]组合执行一次查询。这是非常低效的。您的第二个方法将只执行一个查询,并且仅返回一个结果序列。这样更有效。

  

添加:
  伊万·斯托夫(Ivan Stoev)向我展示了下面的方法不起作用:与IEnumerable相比,IQueryable.Contains只能处理基本类型。因此,下面的“包含”将不起作用。

因此,以下内容不能作为IQueryable使用。
可以对该查询进行一些优化:让您的流程创建所需的[role,id]组合的序列,并在数据库中查询与该组合匹配的所有TableNames

class RoleIdCombination
{
    public char Role {get; set;}
    public int Id {get; set;}
}

// let your process create the requested RoleIdCombinations:
var roleIdCombinations = CreateRoleIdCombinations(roles, ids);

// do only one query:
var result = dbContext.TableNames
    .Select(tableName => new
    {
         // for easier equality check: make a RoleIdCombination:
         RoleIdCombination = new RoleIdCombination
         {
              Role = tableName.Role,
              Id = tableName.Id,
         }
         // remember the original item
         TableName = tableName,
     })
    .Where(item => roleIdCombinations.Contains(item.RoleIdCombination));

所以现在您知道您的数据库管理系统将只枚举您的表一次。

功能CreateRoleIdCombinations

IEnumerable<RoleIdCombination> CreateRoleIdCombinations(
    IEnumerable<char> roles,
    IEnumerable<int> ids)
{
     foreach (var role in roles)
     {
         foreach (var id in ids)
         {
              yield return new RoleIdCombination
              {
                  Role = role,
                  Id = id,
              };
         }
     }
}

为此,我们需要(角色数量* id数量)枚举。我希望它会比表中的项目数小得多,毕竟,您不会每次都删除表的一半吗?

答案 1 :(得分:0)

如果将集合更改为 set ,则包含的成本为O(1),因此,性能更好的则为 List.Contains (O(n) )。

HashSet<int> ids= new HashSet<int>{ 1,3,5 };