如何获取IQueryable.Count的ToTraceString

alp*_*pav 11 c# linq lambda linq-to-entities expression-trees

((ObjectQuery)IQueryable).ToTraceString()用来获取和调整将由LINQ执行的SQL代码.

我的问题是,与大多数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));
    }
Run Code Online (Sandbox Code Playgroud)

执行查询而不编译并返回IQueryable.我想通过这样的方式来做这个伎俩:

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

但是然后CreateQuery给了我以下异常:

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

小智 5

这是我在尝试同样的时候提出的实际工作答案.例外情况说"只能从实现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)) })));
    }
}
Run Code Online (Sandbox Code Playgroud)

要使用它:

var query = context.CreateScalarQuery(() => context.Entity.Count());
MessageBox.Show(((ObjectQuery)query).ToTraceString());
Run Code Online (Sandbox Code Playgroud)

基本上,这样做是在子选择中包装一个非IQueryable查询.它将查询转换为

from dummy in new int[] { 1 }.AsQueryable()
select context.Entity.Count()
Run Code Online (Sandbox Code Playgroud)

除了让上下文的QueryProvider处理查询.生成的SQL几乎是您应该期望的:

SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Entity] AS [Extent1]
)  AS [GroupBy1]
Run Code Online (Sandbox Code Playgroud)