LINQ使用表达式树连接

时间:2015-08-23 02:04:09

标签: c# linq join expression-trees dynamic-linq

我正在使用以下文章中的代码来动态构建LINQ查询。

http://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq

虽然互联网上有更好的建议,但直到现在它才符合我的目的,而且Fitim Skenderi的代码也很容易理解。

我现在需要通过连接两个或多个表来在运行时构建查询。我在网上找不到任何例子。

我的代码:

public enum Op
{
    IsEqualTo,
    IsGreaterThan,
    IsLessThan,
    IsGreaterThanOrEqualTo,
    IsLessThanOrEqualTo,
    Contains,
    StartsWith,
    EndsWith
}

public class Filter
{
    public string PropertyName { get; set; }
    public Op Operation { get; set; }
    public object Value { get; set; }
}

public static class ExpressionBuilder
{
    private static MethodInfo containsMethod = typeof(string).GetMethod("Contains");
    private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
    private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) });

    public static Expression<Func<T, bool>> GetExpression<T>(IList<Filter> filters, bool matchAll)
    {
        if (filters.Count == 0)
            return null;

        ParameterExpression param = Expression.Parameter(typeof(T), "t");
        Expression exp = null;

        if (filters.Count == 1)
            exp = GetExpression<T>(param, filters[0]);
        else if (filters.Count == 2)
            exp = GetExpression<T>(param, filters[0], filters[1], matchAll);
        else
        {
            while (filters.Count > 0)
            {
                var f1 = filters[0];
                var f2 = filters[1];

                if (exp == null)
                    exp = GetExpression<T>(param, filters[0], filters[1], matchAll);
                else
                {
                    if(matchAll)
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0], filters[1], matchAll));
                    else
                        exp = Expression.OrElse(exp, GetExpression<T>(param, filters[0], filters[1], matchAll));
                }

                filters.Remove(f1);
                filters.Remove(f2);

                if (filters.Count == 1)
                {
                    if(matchAll)
                    exp = Expression.AndAlso(exp, GetExpression<T>(param, filters[0]));
                    else
                        exp = Expression.OrElse(exp, GetExpression<T>(param, filters[0]));

                    filters.RemoveAt(0);
                }
            }
        }

        return Expression.Lambda<Func<T, bool>>(exp, param);
    }

    private static Expression GetExpression<T>(ParameterExpression param, Filter filter)
    {
        MemberExpression member = Expression.Property(param, filter.PropertyName);   

        UnaryExpression constant = null;
        if (member.Type == typeof(Decimal?))
        {
           constant = Expression.Convert(Expression.Constant(Decimal.Parse(filter.Value.ToString())), member.Type);
        }
        else if (member.Type == typeof(DateTime?))
        {
           constant = Expression.Convert(Expression.Constant(DateTime.Parse(filter.Value.ToString())), member.Type);
        }
        else if (member.Type == typeof(int?))
        {
            constant = Expression.Convert(Expression.Constant(int.Parse(filter.Value.ToString())), member.Type);
        }
        else
        {
            if (filter.Value.GetType() != member.Type)
            {
                object result = Convert.ChangeType(filter.Value, member.Type);
                constant = Expression.Convert(Expression.Constant(result), member.Type);
            }
            else 
            {
                constant = Expression.Convert(Expression.Constant(filter.Value), member.Type);
            }

        }

        switch (filter.Operation)
        {
            case Op.IsEqualTo:
                return Expression.Equal(member, constant);

            case Op.IsGreaterThan:
                return Expression.GreaterThan(member, constant);

            case Op.IsGreaterThanOrEqualTo:
                return Expression.GreaterThanOrEqual(member, constant);

            case Op.IsLessThan:
                return Expression.LessThan(member, constant);

            case Op.IsLessThanOrEqualTo:
                return Expression.LessThanOrEqual(member, constant);

            case Op.Contains:
                return Expression.Call(member, containsMethod, constant);

            case Op.StartsWith:
                return Expression.Call(member, startsWithMethod, constant);

            case Op.EndsWith:
                return Expression.Call(member, endsWithMethod, constant);
        }

        return null;
    }

    private static BinaryExpression GetExpression<T> (ParameterExpression param, Filter filter1, Filter filter2, bool matchAll)
    {
        Expression bin1 = GetExpression<T>(param, filter1);
        Expression bin2 = GetExpression<T>(param, filter2);

        if(matchAll)
        return Expression.AndAlso(bin1, bin2);
        else
            return Expression.OrElse(bin1, bin2);
    }
}

最终用户选择(合同表),条件(=,&gt;,包含等)字段,并为要查询的每个选定字段输入值。根据最终用户的选择列表,创建filterCollection并生成查询。代码如下:

if (filterCollection.Count > 0)
        {
            var deleg = ExpressionBuilder.GetExpression<Contract>(filterCollection, true).Compile(); //true if all criteria are to be matched
            contractList = ctx.Contracts.Where(deleg).ToList();
            gridControl1.DataSource = contractList;
        }

单表适用。但现在我需要让最终用户选择另一个名为Customer的表的字段,条件和值。 Customer表和Contract表由&#39; CustomerID&#39;链接。

如果用户想要查找名为“John&#39; John”的客户的所有合同,我该如何构建查询? (字段:名称,表格:客户)。任何帮助将不胜感激。

0 个答案:

没有答案