Вот фактический рабочий ответ, который я придумал, когда попытался сделать то же самое. Исключение говорит, что «может быть создано только из экземпляров, которые реализуют интерфейс 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]