如何在LINQ中使用DbFunctions?

时间:2015-10-09 22:29:36

标签: c# linq-to-entities dbfunctions

我有这个LINQ to entity:

var result = (from inspArch in inspectionArchives
              from inspAuth in inspArch.InspectionAuthority
              group new { inspArch, inspAuth } by inspArch.CustomerId into g
              select new
              {
                  clientId = g.Key,
                  id = g.Select(x => x.inspArch.Id).ToArray(),               
                  authId = g.Select(x => x.inspAuth.Id).Distinct().ToArray()
              });

但是在运行时我得到了这个错误:

"LINQ to Entities does not recognize the method 'Int32[] ToArray[Int32](System.Collections.Generic.IEnumerable`1[System.Int32])' method, and this method cannot be translated into a store expression."

我知道我可以这样写我的LINQ:

var result = (from inspArch in inspectionArchives
              from inspAuth in inspArch.InspectionAuthority
              group new { inspArch, inspAuth } by inspArch.CustomerId into g
              select new
              {
                  clientId = g.Key,
                  id = g.Select(x => x.inspArch.Id),    
                  authId = g.Select(x => x.inspAuth.Id).Distinct()
              }).ToList(); 

然后:

var result2 = (from res in result
               select new
               {
                   clientId = res.clientId,
                   id = res.id.ToArray(),
                   authId = res.authId.ToArray()
               });

它工作正常但是,它将整个表拉入内存然后应用投影,这不是很有效。

所以我读到DbFunctions Class;有没有办法在这些行上使用提到的DbFunctions类?

id = g.Select(x => x.inspArch.Id).ToArray(),               
authId = g.Select(x => x.inspAuth.Id).Distinct().ToArray()

而不是ToArray()方法或其他方法使ToArray()方法可以识别为LINQ to Entities?

1 个答案:

答案 0 :(得分:3)

你是如此亲密。这里没有与DbFunctions相关的内容,您需要考虑的是查询实现的工作原理。

首先让我们从查询开始,删除ToArray()内容:

var query = (from inspArch in inspectionArchives
             from inspAuth in inspArch.InspectionAuthority
             group new { inspArch, inspAuth } by inspArch.CustomerId into g
             select new
             {
                 clientId = g.Key,
                 id = g.Select(x => x.inspArch.Id),               
                 authId = g.Select(x => x.inspAuth.Id).Distinct()
             });

如果你设置一个断点,你会看到这是一个db sql查询,这也可以通过以下方式看到:

var sqlQuery = query.ToString();

现在唯一剩下的就是如何使用ToArray()次调用实现最终投影。从逻辑上讲,第一次尝试将是:

var result = query
    .Select(e => new { e.clientId, id = e.Id.ToArray(), authId = e.authId.ToArray() })
    .ToList();

但结果将是同样的例外,因为EF Linq提供商足够聪明,可以完成所有预测并生成最后一个。

因此我们需要具体化查询才能进行最终投影不要使用ToList()ToArray()!最便宜的方法是使用ToEnumerable(),它将给我们一个最小(单项)临时投影记忆对象,反过来我们将转换为我们的最终投影。

所以我们的最终(和工作)代码将是:

var result = query
    .AsEnumerable()
    .Select(e => new { e.clientId, id = e.Id.ToArray(), authId = e.authId.ToArray() })
    .ToList();

或者把它们放在一起:

var result =
    (from inspArch in inspectionArchives
     from inspAuth in inspArch.InspectionAuthority
     group new { inspArch, inspAuth } by inspArch.CustomerId into g
     select new
     {
          clientId = g.Key,
          id = g.Select(x => x.inspArch.Id),               
          authId = g.Select(x => x.inspAuth.Id).Distinct()
     })
    .AsEnumerable()
    .Select(e => new { e.clientId, id = e.Id.ToArray(), authId = e.authId.ToArray() })
    .ToList();

P.S。使用EF查询时,最好不要在投影中使用ToArray()。最后使用ToList()或保持原样(IEnumerableIOrderedEnumerable等) - EF会使用最适合的容器(通常为List,这就是{{1 }}被识别,而ToList()没有。)