IQueryable<Organization> query = context.Organizations;
Func<Reservation, bool> predicate = r => !r.IsDeleted;
query.Select(o => new {
Reservations = o.Reservations.Where(predicate)
}).ToList();
此查询引发“内部.NET Framework数据提供程序错误1025”异常,但下面的查询不会。
query.Select(o => new {
Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();
我需要使用第一个,因为我需要检查一些if语句来构造正确的谓词。我知道在这种情况下我不能使用if语句,这就是我将委托作为参数传递的原因。
如何使第一个查询起作用?
虽然上面的答案是正确的,但请注意,当在select语句之后尝试使用它时,必须显式调用AsQueryable()
,否则编译器将假设我们正在尝试使用IEnumerable方法,这些方法期望Func
而不是Expression<Func>
。
这可能是原始海报的问题,否则编译器会在大多数时候抱怨它正在寻找Expression<Func>
而不是Func
。
演示:以下内容将失败:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).Any(expr))
.Load()
虽然以下内容有效:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
.Load()
在创造了赏金(老鼠!)后,我找到了this answer,这解决了我的问题。 (我的问题涉及一个.Any()
调用,这比这个问题复杂一点......)
简而言之,这是你的答案:
IQueryable<Organization> query = context.Organizations;
Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;
query.Select(o => new { Reservations = o.Reservations.Where(expr) })
.ToList();
阅读引用的答案以解释为什么需要局部变量expr
,并且不能直接引用返回类型Expression<Func<Reservation, bool>>
的另一种方法。
谢谢你对我说话。我想我毕竟是在正确的轨道上。
无论如何,重申一下,LINQ to Entities(感谢Jon Skeet在评论中我在我自己的思考过程中混淆了纠正我)在Expression Trees上运作;它允许一个projection to translate the lambda expression to SQL by the QueryProvider
。
常规Func<>
适用于LINQ to Objects.
因此,在这种情况下,当您使用实体框架时,传递给EF的IQueryable
的任何谓词都必须是Expression<Func<>>
。
我刚刚在不同的场景中遇到过这个问题。
我有一个充满Expression
谓词的静态类,然后我可以将它组合或传递给EF查询。其中一个是:
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(statuses.Contains);
}
由于Contains
方法组调用,这引发了1025错误。实体框架期望一个Expression并找到一个方法组,导致错误。转换代码以使用lambda(可以隐式转换为Expression)修复了错误
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(x => statuses.Contains(x));
}
旁白:然后我将表达式简化为ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));
有类似的问题。 ViewModel库看起来像这样:
public class TagViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
{
Id = t.Id,
Name = t.Name,
};
这有效:
var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
.ToArrayAsync();
但是,这不会编译:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
})
.ToArrayAsync();
因为第二个.Select
是一个混乱 - 第一个实际上被称为ICollection,这不是IQueryable,所以它消耗第一个表达式作为普通的Func
,而不是Expression<Func...
。返回IEnumerable<...
,如本页所述。所以.AsQueryable()
救援:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
但是这会产生一个新的,更奇怪的问题:我得到内部框架...错误1025,或者我得到一个完全加载的.Post
属性的post变量,但是.Tags
属性有一个EF代理对象似乎用于Lazy-加载。
解决方案是通过结束使用Anonymous类来控制Tags的返回类型:
public class PostViewModel
{
public Post Post { get; set; }
public IEnumerable<TagViewModel> Tags { get; set; }
现在选择这个,这一切都有效:
var post = await db.Posts.Take(10)
.Select(p => new PostViewModel {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();