【问题标题】:Create dynamic LINQ query expression at runtime which translates into a parameterized SQL query在运行时创建动态 LINQ 查询表达式,该表达式转换为参数化 SQL 查询
【发布时间】:2014-01-31 03:48:09
【问题描述】:

我想为 IQueryable 创建我的自定义表达式。示例扩展方法:

    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);
    }

它可以工作,但问题是它转换为未参数化的 sql。

例如没有扩展名的代码

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

翻译成下一条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

及扩展方法

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

翻译成

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

那么我该如何编写表达式以在 sql 中生成类似 @p_linq_0 的内容?

更新

感谢usr的回答,我重写了我的扩展方法,现在它生成了我想要的@p_linq_0

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;
    }
}

【问题讨论】:

    标签: c# linq-to-sql expression expression-trees iqueryable


    【解决方案1】:

    我遇到过这个问题。您正在生成 u =&gt; u.Id == 3 作为表达式,而 C# 编译器将生成:

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

    像 C# 编译器那样做。创建一个类来保存 ID 或使用元组。将closure 注入为常量表达式。

    【讨论】:

    • 我不明白这个“将闭包作为常量表达式注入”是什么意思。如果我在扩展方法中执行var closure = new CompilerGeneratedClosure() { UserId = value.Value }; ConstantExpression constantExpression = Expression.Constant(closure.UserId, typeof(int?)); 之类的操作,它仍然会生成 3,而不是 @p_linq_0
    • @YuriyP 你不是在注入闭包,而是在注入数字。你需要像Expression.Field(Expression.Constant(closure), "UserId")这样的东西。
    • 感谢 svick 的澄清。使用 Expression.Field 它可以按预期工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多