Create dynamic LINQ query expression at runtime wh

2019-05-25 06:28发布

问题:

I want to create my custom expression for IQueryable. Sample extension method:

    public static IQueryable<T> GetByIntTest<T>(this IQueryable<T> qSource, Expression<Func<T, int?>> field, int? value) 
    {
        if (value == null)
            return qSource;

        ConstantExpression constantExpression = Expression.Constant(value, typeof(int?));
        BinaryExpression binaryExpression = Expression.Equal(field.Body, constantExpression);
        var predicate = Expression.Lambda<Func<T, bool>>(binaryExpression, field.Parameters);
        return qSource.Where(predicate);
    }

It works, but problem is that it translate into not parameterized sql.

For example code without extension

int userId = 3;
var testUsual = Context.Set<User>().Where(u => u.Id == userId);

Translate into next sql

SELECT [Extent1].[Id] AS [Id],
       [Extent1].[FirstName] AS [FirstName], 
       [Extent1].[LastName] AS [LastName], 
FROM [dbo].[User] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0

And extension method

int userId = 3;
var testExtension = Context.Set<User>().GetByIntTest(u => u.Id, userId);

Translate into

SELECT [Extent1].[Id] AS [Id],
       [Extent1].[FirstName] AS [FirstName], 
       [Extent1].[LastName] AS [LastName], 
FROM [dbo].[User] AS [Extent1]
WHERE 3 = [Extent1].[Id]

So how can I write expression for generating something like @p_linq_0 in sql?

Update

Thanks to usr answer, I rewrite my extension method and now it generate @p_linq_0 as i want.

public static IQueryable<T> GetByIntTest<T>(this IQueryable<T> qSource, Expression<Func<T, int?>> field, int? value) 
{
    if (value == null)
        return qSource;

    var binaryExpression = Expression.Equal(field.Body, ExpressionClosureFactory.GetField(value));
    var predicate = Expression.Lambda<Func<T, bool>>(binaryExpression, field.Parameters);
    return qSource.Where(predicate);
}

public class ExpressionClosureFactory
{
    public static MemberExpression GetField<TValue>(TValue value)
    {
        var closure = new ExpressionClosureField<TValue>
        {
            ValueProperty = value
        };

        return Expression.Field(Expression.Constant(closure), "ValueProperty");
    }

    class ExpressionClosureField<T>
    {
        public T ValueProperty;
    }
}

回答1:

I have had this problem. You are generating u => u.Id == 3 as an expression while the C# compiler would generate:

class CompilerGeneratedClosure { public int UserId; }
var closure = new CompilerGeneratedClosure() { UserId = 3 };
u => u.Id == closure.UserId

Do it like the C# compiler would. Create a class to hold the ID or use a tuple. Inject closure as a constant expression.