内部.NET Framework数据提供程序错误1025。

59
IQueryable<Organization> query = context.Organizations;

Func<Reservation, bool> predicate = r => !r.IsDeleted;

query.Select(o => new { 
    Reservations = o.Reservations.Where(predicate)
}).ToList();

这个查询会抛出 "Internal .NET Framework Data Provider error 1025" 异常,但下面的查询则不会。

query.Select(o => new { 
    Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();

我需要使用第一个查询,因为我需要检查一些if语句以构建正确的谓词。我知道在这种情况下不能使用if语句,这就是为什么我将委托作为参数传递的原因。

我该如何使第一个查询起作用?


2
我并不完全确定这是否是原因(甚至是否正确的方向),但是Linq2Objects操作表达式树,这意味着你通常会传递 Expression<Func<Reservation, bool>> predicate 而不是裸的 Func<Reservation, bool> predicate。你可以尝试一下。 :) - Patryk Ćwiek
1
听起来你可能需要一个谓词构建器。看一下这个问题:http://stackoverflow.com/questions/9184942/nesting-or-using-linq-predicatebuilder - tsells
1
@JonSkeet - 我遇到了与 OP 完全相同的问题,只是我将谓词抽象成了一个静态方法,因为我在很多地方都在使用它。我不想将谓词复制粘贴到所有使用它的地方。有什么解决办法? - Shaul Behr
@Trustme-I'maDoctor - 我认为你应该在这里获得奖励。请在下面发布你的答案... - Shaul Behr
是的,我们不得不在每个地方都复制/粘贴表达式,这太荒谬了。该方法接受一个Func<type,bool>作为lambda表达式。我们无法将其提取到变量中,然后再传递变量。我遇到的问题是框架开发人员不会说英语,并认为“Any”是“All”的反义词,而实际上,“Any”表示“一个或多个”,相反的是“None”。 “All”太通用了,因为它使空集的处理模糊不清,应该分成两个单独的方法命名为“AnyAndAll”或“AllOrNone”。 - Triynko
显示剩余2条评论
5个回答

48

虽然其他答案是正确的,但需要注意的是,在尝试在选择语句之后使用它时,必须显式调用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()

这甚至适用于嵌套的投影(.Include)。干得好! - Zachary Scott
这对我非常有帮助。我的问题正是因为直接使用表达式导致调用了“IEnumerable”重载。 - julealgon

26

创建悬赏(可恶的老鼠!)后,我发现这个答案解决了我的问题(我的问题涉及一个.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>>的另一个方法。


在我看来,这似乎与“相信我 - 我是医生”在16日提供的建议相同。也许他应该得到赏金? - Mike Beeler

21
感谢您的联系。我想我一直在正确的道路上。
无论如何,为了重申,LINQ to Entities(感谢Jon Skeet在评论中纠正我的混乱思维过程)操作Expression Trees;它允许通过QueryProvider进行projection将lambda表达式翻译成SQL。
常规的Func<>非常适用于LINQ to Objects
因此,在这种情况下,当您使用Entity Framework时,传递给EF的任何谓词都必须是Expression<Func<>>

2
我在上面被评论了关于Func<>委托和Expression<>参数的问题。如果你再看一下这行代码Reservations = o.Reservations.Where(predicate),你会意识到o.Reservations是ICollection<>而不是IQueryable<>,因为它只是导航属性。任何对它的Where扩展方法都是IEnumerable<>扩展。因此,传递Func<>委托而不是Expression<>是正确的。 - Freshblood

5

我在另一个场景中遇到了这个问题。

我有一个静态类,里面充满了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),但是发现了一个方法组(method group),因此导致了错误。将代码转换为使用lambda表达式(可以隐式转换为表达式)可以修复该错误。

    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));


0

遇到了类似的问题。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属性的帖子变量,但是.Tags属性具有似乎用于延迟加载的EF代理对象。

解决方案是通过结束使用匿名类来控制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();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接