如何在 LINQ 中使用 DbFunctions?
How to use DbFunctions in LINQ?
我有这个 LINQ to 实体:
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()
});
它工作正常,但是,它将整个 table 拉入内存,然后应用投影,这不是很有效。
所以我读到了 DbFunctions Class;有什么方法可以在这些行上使用提到的 DbFunctions Class?
id = g.Select(x => x.inspArch.Id).ToArray(),
authId = g.Select(x => x.inspAuth.Id).Distinct().ToArray()
改为使用 ToArray() 方法或其他方式使 ToArray() 方法可被 LINQ to Entities 识别?
你们真亲密。此处与 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()
或保持原样(IEnumerable
、IOrderedEnumerable
等)- EF 将使用最适合的容器(通常是 List,这就是为什么 ToList()
被识别,而 ToArray()
不被识别)。
我有这个 LINQ to 实体:
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()
});
它工作正常,但是,它将整个 table 拉入内存,然后应用投影,这不是很有效。
所以我读到了 DbFunctions Class;有什么方法可以在这些行上使用提到的 DbFunctions Class?
id = g.Select(x => x.inspArch.Id).ToArray(),
authId = g.Select(x => x.inspAuth.Id).Distinct().ToArray()
改为使用 ToArray() 方法或其他方式使 ToArray() 方法可被 LINQ to Entities 识别?
你们真亲密。此处与 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()
或保持原样(IEnumerable
、IOrderedEnumerable
等)- EF 将使用最适合的容器(通常是 List,这就是为什么 ToList()
被识别,而 ToArray()
不被识别)。