【发布时间】:2021-12-23 03:44:41
【问题描述】:
早上好,
如果我 not 在 where 子句中添加 createdtimestamp 的条件,我在 EF Core 中有以下查询。如果我添加其他条件子句来过滤数据,如下所示,查询将毫无问题地执行。
var q = (
from trckhead in DbContext.TrackingBatchHeader
from userdets in DbContext.UserDetails
.Where(u => u.UserId == trckhead.ClosedByUserId).DefaultIfEmpty()
from trckkeys in DbContext.TrackingBatchesItemKey
.Where(t => t.TrackingNo == trckhead.TrackingNo).DefaultIfEmpty()
from trcklink1 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "I").DefaultIfEmpty()
from trcklink2 in DbContext.TrackingBatchesLink
.Where(x => x.TrackingNo == trckhead.TrackingNo && x.TrackingType == "T").DefaultIfEmpty()
join trckref in DbContext.TrackingBatchesReference on trckhead.TrackingBatchType equals trckref
.TrackingBatchType
join mthsite in DbContext.SiteDetail on trckhead.SiteCode equals mthsite.SiteCode
join userdets2 in DbContext.UserDetails on trckhead.CreatedByUserId equals userdets2.UserId
where
(string.IsNullOrEmpty(searchRequest.Status) || trckhead.Status == searchRequest.Status) &&
trckhead.CreatedTimestamp >= DateTime.Now.AddMonths(-11)
select new TrackingBatchSearchResultDto
{
TrackingBatchId = trckhead.TrackingNo,
SiteCode = trckhead.SiteCode,
TrackingBatchType = trckhead.TrackingBatchType,
Status = trckhead.Status,
Created = trckhead.CreatedTimestamp,
CreatedById = trckhead.CreatedByUserId,
ClosedById = trckhead.ClosedByUserId,
UserDescription = trckhead.UsersDescription,
TrackingBatchDescription = trckref.TrackingBatchTypeDescription,
SiteName = mthsite.Title,
CreatedByName = userdets2.FullUserName ?? string.Empty,
ClosedByName = userdets.FullUserName ?? string.Empty,
Link1 = trcklink1.TrackingData ?? string.Empty,
Link2 = trcklink2.TrackingData ?? string.Empty
}).Distinct();
return await q.ToListAsync();
如果我在查询中保留日期子句,则引发的异常显示以下内容。如果我将一个函数传递给查询,但检查一个简单的日期,我会认为服务器评估不会很高兴。我可以在获得整个数据集后按日期过滤结果,但返回的记录数量可能很大,所以我宁愿从数据库查询中过滤掉它。任何帮助将不胜感激。
The LINQ expression 'DbSet<TrackingBatchHeader>
.SelectMany(
source: t => DbSet<UserDetail>
.Where(u => u.UserId == t.ClosedByUserId)
.DefaultIfEmpty(),
collectionSelector: (t, c) => new TransparentIdentifier<TrackingBatchHeader, UserDetail>(
Outer = t,
Inner = c
))
.SelectMany(
source: ti => DbSet<TrackingBatchesItemKey>
.Where(t0 => t0.TrackingNo == ti.Outer.TrackingNo)
.DefaultIfEmpty(),
collectionSelector: (ti, c) => new TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>(
Outer = ti,
Inner = c
))
.SelectMany(
source: ti0 => DbSet<TrackingBatchesLink>
.Where(t1 => t1.TrackingNo == ti0.Outer.Outer.TrackingNo && t1.TrackingType == "I")
.DefaultIfEmpty(),
collectionSelector: (ti0, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>(
Outer = ti0,
Inner = c
))
.SelectMany(
source: ti1 => DbSet<TrackingBatchesLink>
.Where(t2 => t2.TrackingNo == ti1.Outer.Outer.Outer.TrackingNo && t2.TrackingType == "T")
.DefaultIfEmpty(),
collectionSelector: (ti1, c) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>(
Outer = ti1,
Inner = c
))
.Join(
outer: DbSet<TrackingBatchesReference>,
inner: ti2 => ti2.Outer.Outer.Outer.Outer.TrackingBatchType,
outerKeySelector: t3 => t3.TrackingBatchType,
innerKeySelector: (ti2, t3) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>(
Outer = ti2,
Inner = t3
))
.Join(
outer: DbSet<SiteDetail>,
inner: ti3 => ti3.Outer.Outer.Outer.Outer.Outer.SiteCode,
outerKeySelector: s => s.SiteCode,
innerKeySelector: (ti3, s) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>(
Outer = ti3,
Inner = s
))
.Join(
outer: DbSet<UserDetail>,
inner: ti4 => ti4.Outer.Outer.Outer.Outer.Outer.Outer.CreatedByUserId,
outerKeySelector: u0 => u0.UserId,
innerKeySelector: (ti4, u0) => new TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TransparentIdentifier<TrackingBatchHeader, UserDetail>, TrackingBatchesItemKey>, TrackingBatchesLink>, TrackingBatchesLink>, TrackingBatchesReference>, SiteDetail>, UserDetail>(
Outer = ti4,
Inner = u0
))
.Where(ti5 => ti5.Outer.Outer.Outer.Outer.Outer.Outer.Outer.CreatedTimestamp >= (Nullable<DateTime>)DateTime.Now.AddMonths(-11))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
【问题讨论】:
-
不要写这样的查询。 LINQ 不是 SQL 的替代品,EF Core 也不是数据访问库。 DbContext 不是数据库的模型。 EF Core 是一个对象到关系映射器。 EF Core 将根据对象之间的关系生成 SQL 查询。
JOIN在 LINQ 查询中不应该有任何理由。至于Where子句,不需要像string.IsNullOrEmpty(searchRequest.Status)这样的包罗万象的技巧。您可以有条件地添加过滤器,例如if (status!=null){ query=query.Where(t=>t.Status == status);}。 -
如果您的 DbContext 和实体配置正确,您将不需要任何这些连接。如果您在
Select()子句中使用简单的对象访问表示法,LINQ 会将其转换为必要的 JOIN 并在SELECT子句中添加必要的字段 -
异常与您发布的查询不匹配。你能简化查询并发布实际的查询和异常吗?
-
@K4E 你应该。因为这就是简化查询和消除可能导致问题的代码所需要的。在那之后,包罗万象的条款是另一件可能导致问题的事情。
String.IsNullOrWhitespace无法转换为 SQL。没有这样的功能。它甚至不必在查询中,因为它所做的只是禁用下一个子句。您可以链接.Where()调用,结果等效于AND。这就是为什么使用if(status==null){ q=q.Where(...);}可以有条件地添加过滤器 -
@K4E 请忽略一般 cmets,因为它们与问题无关。您使用的是什么 EF Core 版本?因为最新的 EFC 5 / 6 在这种情况下会显示更好的错误消息,其中包含更多可能导致错误的提示。据我所知,问题要么是
DateTime.Now.AddMonths(-11),我很确定它是可翻译的,要么很可能是未映射实体类的CreatedTimestamp属性(通过[NotMapped]或.Ignore)。
标签: c# linq entity-framework-core