Как получить ToTraceString для IQueryable.Count - PullRequest
11 голосов
/ 29 ноября 2011

Я использую ((ObjectQuery)IQueryable).ToTraceString() для получения и настройки кода SQL, который будет выполняться LINQ.

Моя проблема в том, что в отличие от большинства методов IQueryable IQueryable.Count, как определено следующим образом:

    public static int Count(this IQueryable source) {
        return (int)source.Provider.Execute(
            Expression.Call(
                typeof(Queryable), "Count",
                new Type[] { source.ElementType }, source.Expression));
    }

выполняет запрос без компиляции и возврата IQueryable.Я хотел сделать трюк примерно так:

public static IQueryable CountCompile(this IQueryable source) {
    return source.Provider.CreateQuery(
        Expression.Call(
            typeof(Queryable), "Count",
            new Type[] { source.ElementType }, source.Expression));
}

Но тогда CreateQuery дает мне следующее исключение:

LINQ to Entities query expressions can only be constructed from instances that implement the IQueryable interface.

Ответы [ 2 ]

5 голосов
/ 25 января 2012

Вот фактический рабочий ответ, который я придумал, когда попытался сделать то же самое. Исключение говорит, что «может быть создано только из экземпляров, которые реализуют интерфейс IQueryable», поэтому ответ кажется простым: вернуть что-то запрашиваемое. Это возможно при возврате .Count()? Да!

public partial class YourObjectContext
{
    private static MethodInfo GetMethodInfo(Expression<Action> expression)
    {
        return ((MethodCallExpression)expression.Body).Method;
    }
    public IQueryable<TResult> CreateScalarQuery<TResult>(Expression<Func<TResult>> expression)
    {
        return QueryProvider.CreateQuery<TResult>(
            Expression.Call(
                method: GetMethodInfo(() => Queryable.Select<int, TResult>(null, (Expression<Func<int, TResult>>)null)),
                arg0: Expression.Call(
                    method: GetMethodInfo(() => Queryable.AsQueryable<int>(null)),
                    arg0: Expression.NewArrayInit(typeof(int), Expression.Constant(1))),
                arg1: Expression.Lambda(body: expression.Body, parameters: new[] { Expression.Parameter(typeof(int)) })));
    }
}

Чтобы использовать это:

var query = context.CreateScalarQuery(() => context.Entity.Count());
MessageBox.Show(((ObjectQuery)query).ToTraceString());

По сути, это оборачивает не-IQueryable запрос в подвыбор. Преобразует запрос в

from dummy in new int[] { 1 }.AsQueryable()
select context.Entity.Count()

кроме того, что позволяет контекстному QueryProvider обрабатывать запрос. Сгенерированный SQL - это почти то, что вы должны ожидать:

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Entity] AS [Extent1]
)  AS [GroupBy1]
2 голосов
/ 01 декабря 2011

Вы не можете создать объект запроса для 'Count', поскольку он не возвращает IQueryable (что имеет смысл - он возвращает единственное значение).

У вас есть два варианта:

  • (рекомендуется) Используйте eSQL:

    context.CreateQuery<YourEntity>("select count(1) from YourEntitySet").ToTraceString()
    
  • Используйте Reflection для вызова частного метода, который не выполняет проверку IQueryable (это неверно по очевидным причинам, ноесли вам просто нужно это для отладки, это может быть удобно):

    public static IQueryable CountCompile(this IQueryable source)
    {
        // you should cache this MethodInfo
        return (IQueryable)source.Provider.GetType().GetMethod("CreateQuery", BindingFlags.NonPublic | BindingFlags.Instance, null,
                                            new[] {typeof (Expression), typeof (Type)}, null)
            .Invoke(source.Provider, new object[]
                                         {
                                             Expression.Call(
                                                 typeof (Queryable), "Count",
                                                 new[] {source.ElementType}, source.Expression),
                                             source.ElementType
                                         });
    }
    
...