gpt4 book ai didi

c# - 使用 LinqToSQL 将参数传递到表达式规范

转载 作者:行者123 更新时间:2023-11-30 16:13:24 26 4
gpt4 key购买 nike

我想通过使用 Expression<Func<T,bool>> 减少 LinqToSQL 查询中的重复逻辑.在使用静态属性之前,我们已经成功完成了:

public static Expression<Func<Document, bool>> IsActive
{
get
{
return document => !document.Deleted;
}
}

...

_workspace.GetDataSource<Document>().Where(DocumentSpecifications.IsActive)

但是,当需要像这样将其他参数传递到表达式中时,我很难让它工作:

public static Expression<Func<Comment, bool>> IsUnread(int userId, Viewed viewed)
{
return
c =>
!c.Deleted && c.CreatedByActorID != actorId
&& (viewed == null || c.ItemCreatedDate > viewed.LastViewedDate);
}

...

// Throwing "Argument type 'System.Linq.Expression<X,bool>' is not assignable
// to parameter type 'System.Func<X,bool>'"
return (from a in alerts
select
new UnreadComments
{
TotalNumberOfUnreadComments =
a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView))
})

我如何转换规范才能以这种方式被接受并且它仍能正确转换为 SQL?

编辑:按照 Anders 的建议,我将 .Compile() 添加到查询中。现在在内存集合中进行单元测试时可以正常工作;但是,当 LinqToSQL 尝试将其转换为 SQL 时,出现以下异常:

System.NotSupportedException: Unsupported overload used for query operator 'Count'

我试过:

a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView).Compile())
a.Comments.AsQueryable().Count(CommentSpecifications.IsUnread(actorId, a.LastView))

最佳答案

看起来第二个查询是作为 linq-to-objects 而不是 linq-to-sql 执行的。它需要一个 Func<X, bool>这就是 linq-to-objects 使用的,而 linq-to-sql(或任何其他 IQueryable 提供程序)需要一个可以翻译成其他东西的未编译表达式树)

快速解决方法是调用 Compile()在表达式上将其转换为可执行函数。

a.Comments.Count(CommentSpecifications.IsUnread(actorId, a.LastView).Compile())

更详细地说,您真的应该弄清楚为什么该查询是作为 linq-to-objects 而不是 linq-to-sql 执行的。特别是如果您希望将其转换为高效的 sql,它可能会成为性能噩梦。

更新

在您编辑之后,发生的事情会更加明显:

您在单元测试期间将查询作为 linq-to-objects 运行,稍后作为 linq-to-sql 运行。在这种情况下,将表达式转换为 Func<>通过Compile()将无法工作,因为 linq-to-sql 无法识别它。

更新2

将可重用部分组合到要翻译的查询表达式中很困难 - 它会混淆翻译引擎。 Linq-to-sql 比 linq-to-entities 更宽容一些,但仍然很难让它工作。更好的方法通常是创建在 IQueryable<T> 上运行的链接函数.

public static IQueryable<Comment> WhereIsUnread(this IQueryable<Comment> src, int userId)
{
return src.Where(
c =>
!c.Deleted && c.CreatedByActorID != actorId
&& (viewed == null || c.ItemCreatedDate > c.Alert.LastView.LastViewedDate));
}

...

return (from a in alerts
select
new UnreadComments
{
TotalNumberOfUnreadComments =
a.Comments.WhereIsUnRead(actorId).Count()
})

类似的东西应该可以工作。请注意,我已经重写了访问上次查看日期的方式,否则在作为参数传入时将无法转换为 SQL。

关于c# - 使用 LinqToSQL 将参数传递到表达式规范,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21725131/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com