如何从实体框架导航属性创建 LINQ 表达式?

发布于 2024-07-23 10:03:11 字数 1168 浏览 6 评论 0 原文

我有以下代码:

Expression<Func<Subscription, Service>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService).FirstOrDefault();

它创建一个表达式,我稍后可以将其用作实体框架查询的一部分。 我使用的实际代码有一个 where 子句,但为了可读性我省略了它。 这工作正常,我可以在我用于测试的 LINQPad 中运行它。

如果我将代码更改为:

Expression<Func<Subscription, IQueryable<Service>>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService);

它不再编译并出现以下错误:

无法将 lambda 表达式转换为 委托类型 'System.Func>' 因为其中的一些返回类型 该块不是隐式的 可转换为委托返回 类型

这似乎是因为作为导航属性的 ChildRelationships 没有实现 IQueryable。 它实际上是 EntityCollection 类型,它确实实现了 IEnumerable,但不适合创建与 EF 一起使用的表达式。

我想我明白为什么第二个代码块不起作用,并且想知道如何重写它以使其起作用。 同样让我困惑的是为什么第一块代码确实有效。 它还使用 ChildRelationships 导航属性,但成为与 EF 配合使用的表达式没有任何问题。

任何人都可以阐明吗?

I have the following bit of code:

Expression<Func<Subscription, Service>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService).FirstOrDefault();

It creates an expression that I can use later as part of a query with the entity framework. The actual code I'm using has a where clause but I've omitted it for readability. This works fine and I'm able to run it in LINQPad which I'm using for testing.

If I change the code to:

Expression<Func<Subscription, IQueryable<Service>>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService);

It no longer compiles and has the following error:

Cannot convert lambda expression to
delegate type
'System.Func<Farmworks.Data.Subscription,System.Linq.IQueryable<Farmworks.Data.Service>>'
because some of the return types in
the block are not implicitly
convertible to the delegate return
type

It seems to be because ChildRelationships which is a navigation property doesn't implement IQueryable. It is actually of type EntityCollection which does implement IEnumerable but is no good for creating expressions that work with EF.

I think I understand why the second block of code doesn't work and would like to know how to rewrite it so that it does. What also puzzles me is why the first block of code does work. It also uses the ChildRelationships navigation property but doesn't have any problem becoming an expression that works with EF.

Can anyone shed any light?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

凉风有信 2024-07-30 10:03:12

问题不在于 FirstOrDefault 以某种方式使表达式起作用;而是在于。 正如您所说,ChildRelationships 本身并不实现 IQueryable。 您可以根据您的需要通过两种不同的方式解决这个问题。

您可以使用 IEnumerable.AsQueryable 方法。 如果服务已经从上下文加载,这可能是最好的。

如果服务未加载,您可以根据需要使用Load或Include方法在适当的时候加载它们,然后使用上面的解决方案。

如果您有对 ObjectContext 的引用,则可以使用 ObjectQuery,它确实实现了 IQueryable:

Expression<Func<Subscription, MyEntities, IQueryable<Service>>> service2 = 
    (subscription, context) => (
    from s in context.Subscriptions // here is the IQueryable
    where s.Id == subscription.Id
    from relationship in s.ChildRelationships
    select relationship.SecondService);

The issue is not that FirstOrDefault somehow makes the Expression work; it is, as you say, that ChildRelationships does not, by itself, implement IQueryable. You can work around that in two different ways, depending upon your needs.

You can use the IEnumerable.AsQueryable method. This is probably the best if the services are already loaded from the context.

If the services are not loaded, you can use the Load or Include methods, as required, to load them at the appropriate time, and then use the solution above.

If you have a reference to your ObjectContext, you can use the ObjectQuery, which does implement IQueryable:

Expression<Func<Subscription, MyEntities, IQueryable<Service>>> service2 = 
    (subscription, context) => (
    from s in context.Subscriptions // here is the IQueryable
    where s.Id == subscription.Id
    from relationship in s.ChildRelationships
    select relationship.SecondService);
节枝 2024-07-30 10:03:12

我认为 CompiledQuery 类可能会帮助你:

Expression<Func<Subscription, IQueryable<Service>>> service2 = 
    CompiledQuery.Compile( 
        subscription => (
            from relationship in subscription.ChildRelationships
            select relationship.SecondService
        )
    );

I think CompiledQuery class may help you in this case:

Expression<Func<Subscription, IQueryable<Service>>> service2 = 
    CompiledQuery.Compile( 
        subscription => (
            from relationship in subscription.ChildRelationships
            select relationship.SecondService
        )
    );
寻梦旅人 2024-07-30 10:03:12

您需要它是 IQueryable 吗? 我认为 select 返回一个 IEnumerable。 LINQ 直接在 IEnumerable 上运行,因此您只需将 service2 声明为 Expression>> 即可。

Do you need it to be IQueryable<Service>? I think that select is returning an IEnumerable<Service>. LINQ operates directly on IEnumerables, so you could just declare service2 as Expression<Func<Subscription, IEnumerable<Service>>>.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文