简体   繁体   English

Linq查询中返回IQueryable的内部方法

[英]Inner method in Linq query that return IQueryable

I would like to do following thing in Entity Framework code first: 我想首先在Entity Framework代码中执行以下操作:

Data.GroupBy(x => x.Person.LastName)
.Select(x => x.OtherGrouping(...)).Where(...).GroupBy(...)...etc



public static class Extensions
{

public static IQueryable<IGrouping<T, T>> OtherGrouping<T>(this IQueryable<T> collection /**** Here I want to pass consts and lambdas\expressions*****/)
    {
        // Grouping, filtration and other stuff here
        return collection.GroupBy(x => x);
    }
}

The problem is .net compiles OtherGrouping method, it doesn't represent like an expression, and couldn't be transformed to the SQL. 问题是.net编译OtherGrouping方法,它不像表达式那样,并且无法转换为SQL。

I found LinqKit library which suppose to help in such cases, but I coudn't figure out how to apply it in my specific case. 我发现LinqKit库假设在这种情况下提供帮助,但我不知道如何在我的特定情况下应用它。 It works fine for simple cases, when I just have expression like x => x+2 , but I get stucked with return IQueryable. 它适用于简单的情况,当我只有x => x+2这样的表达式时,但我得到了返回IQueryable。 Probably it is possible to write expression tree completely by hand but I don't want to go so deep. 可能完全可以手工编写表达式树,但我不想这么深。

Any ideas how it might be done or where I can read about it? 任何想法如何完成或我可以阅读它?


Here is what I tried to do based on Rob's comment 以下是我根据Rob的评论尝试做的事情

void Main()
{
    AddressBases.GroupBy(x => x.ParentId).Select(x => new {x.Key, Items = x.AsQueryable().OtherGrouping(a => a.Country) }).Dump();

}

public static class Extensions
{
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
    {   
        return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
            source.GroupBy(keySelector).Expression);
    }
}

And I got an exception: 我有一个例外:

NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IQueryable`1[System.Linq.IGrouping`2[System.String,InsuranceData.EF.AddressBase]] OtherGrouping[AddressBase,String](System.Linq.IQueryable`1[InsuranceData.EF.AddressBase], System.Linq.Expressions.Expression`1[System.Func`2[InsuranceData.EF.AddressBase,System.String]])' method, and this method cannot be translated into a store expression.

Couldn't figure out yet why I have OtherGrouping method in expression tree? 无法弄清楚为什么我在表达式树中有OtherGrouping方法? OtherGrouping method should just attach another grouping to the expression and pass it to the provider, but not put itself to the tree. OtherGrouping方法应该只是将另一个分组附加到表达式并将其传递给提供程序,但不将其自身放入树中。

Your current code after the edit is correct - where you're going wrong is a common issue with the syntactic sugar we're given by C#. 编辑后的当前代码是正确的 - 你出错的地方是我们用C#给出的句法糖的常见问题。

If you write the following: 如果你写下面的内容:

void Main()
{
    var res = Containers.OtherGrouping(c => c.ContainerID);
    res.Expression.Dump();
    res.Dump();
}

public static class Extensions
{
    public static IQueryable<IGrouping<TKey, TSource>> OtherGrouping<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector)
    {
        return source.Provider.CreateQuery<IGrouping<TKey, TSource>>(
            source.GroupBy(keySelector).Expression
        );
    }
}

You'll see that we get the output: 你会看到我们得到了输出:

Table(Container).GroupBy(c => c.ContainerID)

And the result executes with an issue, properly grouping by our predicate. 结果会执行一个问题,通过我们的谓词正确分组。 However, you're invoking OtherGrouping while inside an expression, when you're passing it to Select . 但是,当您将其传递给Select时,您将表达式中调用OtherGrouping Let's try a simple case without OtherGrouping : 让我们尝试一个没有OtherGrouping的简单案例:

var res = Containers.OtherGrouping(c => c.ContainerID)
    .Select(x => new
    {
        x.Key,
        Items = x.GroupBy(c => c.ContainerID),
    });
res.Expression.Dump();

What you're providing to Select is an expression tree. 您为Select提供的是表达式树。 That is, the inner GroupBy's method is never actually invoked. 也就是说,实际上从未调用过内部GroupBy的方法。 If you change the query to x.AsQueryable().OtherGrouping and put a breakpoint in OtherGrouping , it will only be hit the first time. 如果将查询更改为x.AsQueryable().OtherGrouping并在OtherGrouping放置断点,则只会在第一次OtherGrouping

In addition to that, x is IEnumerable<T> , not IQueryable<T> . 除此之外,x是IEnumerable<T> ,而不是IQueryable<T> Invoking AsQueryable() gives you an IQueryable , but it also gives you a new query provider. 调用AsQueryable()为您提供了一个IQueryable ,但它也为您提供了一个新的查询提供程序。 I'm not 100% sure as to the inner workings of entity framework, but I'd wager to say that this is invalid - as we no longer have the database as a target for the provider. 我不是100%确定实体框架的内部工作方式,但我敢说这是无效的 - 因为我们不再将数据库作为提供者的目标。 Indeed, with linq2sql (using LINQPad), we get an error saying .AsQueryable() is not supported. 实际上,使用linq2sql(使用LINQPad),我们得到一个错误说。不支持.AsQueryable()

So, what can you do? 所以,你可以做什么? Unfortunately there's no clean way to do this. 不幸的是,没有干净的方法来做到这一点。 Since the expression tree is already built before OtherGrouping has a chance, it doesn't matter what the body of OtherGrouping is. 由于表达式树已经OtherGrouping有机会之前OtherGrouping ,因此OtherGrouping的主体是什么并不重要。 We'll need to change the expression tree after it's built, but before it's executed. 我们需要在构建之后更改表达式树,但在执行之前。

For that, you'll need to write an expression visitor which will look for .OtherGrouping expression calls, and replace it with Queryable.GroupBy 为此,您需要编写一个表达式访问者,它将查找.OtherGrouping表达式调用,并将其替换为Queryable.GroupBy

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM