动态Linq使用SqlMethods.Like

时间:2016-03-19 20:02:00

标签: c# linq linq-to-sql linq-expressions

我正在尝试构建一个动态的Linq to Sql查询,除了调用SqlMethods.Like方法之外,它还运行良好。我的代码在下面,生成的linq语句的主体如下所示:

Body = {((((log.ClientCode == "C1") OrElse 
(log.ClientCode == "C2")) AndAlso 
(log.Source == "S1")) AndAlso Like("Message", "%1%"))}

正如你所看到的,它试图打电话给#34;喜欢"没有SqlMethods类。知道我做错了什么吗?

    public IEnumerable<ILog> Get(int pageNumber, int pageCount,
        List<string> clientCodes, List<string> sources, List<LogLevel> logLevels,
        string messageContains, string userNameContains,
        DateTime? dateStart, DateTime? dateEnd)
    {
        var expressions = new List<Expression>();

        ParameterExpression pe = Expression.Parameter(typeof(Data.Logging.Log), "log");

        if (clientCodes != null && clientCodes.Count > 0)
        {
            expressions.Add(CreateClientCodeExpression(pe, clientCodes));
        }
        if (sources != null && sources.Count > 0)
        {
            expressions.Add(CreateSourceExpression(pe, sources));
        }

        if (logLevels != null && logLevels.Count > 0)
        {
            expressions.Add(CreateLogLevelExpression(pe, logLevels));
        }

        if (!string.IsNullOrWhiteSpace(messageContains))
        {
            expressions.Add(CreateMessageExpression(pe, messageContains));
        }

        Expression exp = null;
        if (expressions.Count > 0)
        {
            exp = expressions[0];
        }
        for (var i = 1; i < expressions.Count; i++)
        {
            exp = Expression.AndAlso(exp, expressions[i]);
        }

        var predicate = Expression.Lambda<Func<Data.Logging.Log, bool>>(exp, pe);
        var results = DbContext.Logs.Where(predicate).ToList();

        foreach (var result in results)
        {
            yield return ConvertDbLogToLog(result);
        }
    }

    private Expression CreateClientCodeExpression(ParameterExpression pe, List<string> clientCodes)
    {
        Expression result = null;
        clientCodes.ForEach(cc =>
        {
            MemberExpression me = Expression.Property(pe, "ClientCode");
            ConstantExpression ce = Expression.Constant(cc);

            if (result == null) { result = Expression.Equal(me, ce); }
            else { result = Expression.OrElse(result, Expression.Equal(me, ce)); }
        });

        return result;
    }

    private Expression CreateSourceExpression(ParameterExpression pe, List<string> sources)
    {
        Expression result = null;
        sources.ForEach(s =>
        {
            MemberExpression me = Expression.Property(pe, "Source");
            ConstantExpression ce = Expression.Constant(s);

            if (result == null) { result = Expression.Equal(me, ce); }
            else { result = Expression.OrElse(result, Expression.Equal(me, ce)); }
        });

        return result;
    }

    private Expression CreateLogLevelExpression(ParameterExpression pe, List<LogLevel> logLevels)
    {
        Expression result = null;
        logLevels.ForEach(l =>
        {
            MemberExpression me = Expression.Property(pe, "LogLevel");
            ConstantExpression ce = Expression.Constant(l.ToString());

            if (result == null) { result = Expression.Equal(me, ce); }
            else { result = Expression.OrElse(result, Expression.Equal(me, ce)); }
        });

        return result;
    }

    private MethodCallExpression CreateMessageExpression(ParameterExpression pe, string message)
    {
        return Expression.Call(typeof(SqlMethods).GetMethod("Like", new[] { typeof(string), typeof(string) }),
            Expression.Constant("Message"), Expression.Constant(string.Format("%{0}%", message)));
    }

2 个答案:

答案 0 :(得分:1)

实际上,'Contains'运算符并不总是足够的。例如,如果你想搜索这样的东西:'first%last'。字符串中的'%'将按字面意思取代而不是预期的通配符。要使用'SqlMethods.Like'运算符,您可以使用以下命令:

public static MethodCallExpression Like(this ParameterExpression pe, string value)
{
    var prop = Expression.Property(pe, pe.Name);
    return Expression.Call(typeof(SqlMethods), "Like", null, prop, Expression.Constant(value));
}

答案 1 :(得分:-2)

您可以跳过Like调用,并使用Contains,Linq-to-SQL和Linq-to-Entities都正确转换为LIKE语句。你甚至可以保存报价! (顺便说一下,你做错了。)

private MethodCallExpression CreateMessageExpression(ParameterExpression pe, string message)
{
    return Expression.Call(Expression.Property(pe, "Message"), "Contains", null, Expression.Constant(message));
}
相关问题