C# Expression使用实践——为FreeSql增加WhereLike方法

1.背景

前端有一个是否模糊查询的参数,当未勾选时,所有字符串类型的值使用a = 'xxx',勾选后使用a like '%xxx%'
如果使用WhereIf来写,每一个判断条件都需要写两行

.WhereIf(!request.IsLike && request.A.HasValue(), (i, o) => o.A== request.A)
.WhereIf(request.IsLike && request.A.HasValue(), (i, o) => o.A.Contains(request.A))

2.初次尝试


public static ISelect<T1, T2> WhereLike<T1, T2>(this ISelect<T1, T2> query, Func<HzyTuple<T1, T2>, string> filed, string value, bool isLike = false) 
    where T2 : class
{
    if (isLike)
    {
        query = query.Where(x => filed(x).Contains(value));
    }
    else
    {
        query = query.Where(x => filed(x) == value);
    }

    return query;
}

public static ISelect<T1, T2> WhereLike<T1, T2>(this ISelect<T1, T2> query, Func<T1, T2, string> filed, string value, bool isLike = false) where T2 : class
{
    if (isLike)
    {
        query = query.Where(x => filed(x.t1, x.t2).Contains(value));
    }
    else
    {
        query = query.Where(x => filed(x.t1, x.t2) == value);
    }

    return query;
}

执行后,发现生成的sql语句不正确where = 'xxx',少了字段名,猜测FreeSql底层表达式解析可能不支持这种写法

3.解决办法

开始大量Google Expression 相关资料,最终找到了解决办法

public static ISelect<T1> WhereLike<T1>(this ISelect<T1> query, Expression<Func<T1, string>> filed, string value, bool isLike = false) where T1 : class
{
    var valueExp = Expression.Constant(value);
    if (isLike)
    {
        var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
    }
    else
    {
        query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
    }

    return query;
}


#region HzyTuple

public static ISelect<T1, T2> WhereLike<T1, T2>(this ISelect<T1, T2> query, Expression<Func<HzyTuple<T1, T2>, string>> filed, string value, bool isLike = false)
        where T2 : class

{
    var valueExp = Expression.Constant(value);
    if (isLike)
    {
        var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        query = query.Where(Expression.Lambda<Func<HzyTuple<T1, T2>, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
    }
    else
    {
        query = query.Where(Expression.Lambda<Func<HzyTuple<T1, T2>, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
    }

    return query;
}

#endregion

#region Not HzyTuple

public static ISelect<T1, T2> WhereLike<T1, T2>(this ISelect<T1, T2> query, Expression<Func<T1, T2, string>> filed, string value, bool isLike = false)
        where T2 : class

{
    var valueExp = Expression.Constant(value);
    if (isLike)
    {
        var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
        query = query.Where(Expression.Lambda<Func<T1, T2, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
    }
    else
    {
        query = query.Where(Expression.Lambda<Func<T1, T2, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
    }

    return query;
}

#endregion

4.附

以上表达式代码有一定规律,一个个写太费时间了,所以搞了个python脚本生成代码

脚本如下

#!/usr/bin/python3

def PrintHzyTuple(number):
    TValue = ', '.join(['T'+str(i+1) for i in range(number)])
    TWhere = ''
    for i in range(number-1):
        TWhere += f'            where T{str(i+2)} : class\n'
    template = f'''
    public static ISelect<{TValue}> WhereLike<{TValue}>(this ISelect<{TValue}> query, Expression<Func<HzyTuple<{TValue}>, string>> filed, string value, bool isLike = false)
{TWhere}
        {{
            var valueExp = Expression.Constant(value);
            if (isLike)
            {{
                var method = typeof(string).GetMethod("Contains", new[] {{ typeof(string) }});
                query = query.Where(Expression.Lambda<Func<HzyTuple<{TValue}>, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }}
            else
            {{
                query = query.Where(Expression.Lambda<Func<HzyTuple<{TValue}>, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
            }}

            return query;
        }}
    '''
    print(template)

def PrintHzyTupleIf(number):
    TValue = ', '.join(['T'+str(i+1) for i in range(number)])
    TWhere = ''
    for i in range(number-1):
        TWhere += f'            where T{str(i+2)} : class\n'
    template = f'''
    public static ISelect<{TValue}> WhereLikeIf<{TValue}>(this ISelect<{TValue}> query, bool condition, Expression<Func<HzyTuple<{TValue}>, string>> filed, string value, bool isLike = false)
{TWhere}
        {{
            if (!condition){{
                return query;
            }}
            var valueExp = Expression.Constant(value);
            if (isLike)
            {{
                var method = typeof(string).GetMethod("Contains", new[] {{ typeof(string) }});
                query = query.Where(Expression.Lambda<Func<HzyTuple<{TValue}>, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }}
            else
            {{
                query = query.Where(Expression.Lambda<Func<HzyTuple<{TValue}>, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
            }}

            return query;
        }}
    '''
    print(template)

def PrintNotHzyTuple(number):
    TValue = ', '.join(['T'+str(i+1) for i in range(number)])
    TWhere = ''
    for i in range(number-1):
        TWhere += f'            where T{str(i+2)} : class\n'
    template = f'''
    public static ISelect<{TValue}> WhereLike<{TValue}>(this ISelect<{TValue}> query, Expression<Func<{TValue}, string>> filed, string value, bool isLike = false)
{TWhere}
        {{
            var valueExp = Expression.Constant(value);
            if (isLike)
            {{
                var method = typeof(string).GetMethod("Contains", new[] {{ typeof(string) }});
                query = query.Where(Expression.Lambda<Func<{TValue}, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }}
            else
            {{
                query = query.Where(Expression.Lambda<Func<{TValue}, bool>>(Expression.Equal(filed.Body, valueExp),  filed.Parameters));
            }}

            return query;
        }}
    '''
    print(template)
def PrintNotHzyTupleIf(number):
    TValue = ', '.join(['T'+str(i+1) for i in range(number)])
    TWhere = ''
    for i in range(number-1):
        TWhere += f'            where T{str(i+2)} : class\n'
    template = f'''
    public static ISelect<{TValue}> WhereLikeIf<{TValue}>(this ISelect<{TValue}> query, bool condition, Expression<Func<{TValue}, string>> filed, string value, bool isLike = false)
{TWhere}
        {{
            if (!condition){{
                return query;
            }}
            var valueExp = Expression.Constant(value);
            if (isLike)
            {{
                var method = typeof(string).GetMethod("Contains", new[] {{ typeof(string) }});
                query = query.Where(Expression.Lambda<Func<{TValue}, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }}
            else
            {{
                query = query.Where(Expression.Lambda<Func<{TValue}, bool>>(Expression.Equal(filed.Body, valueExp),  filed.Parameters));
            }}

            return query;
        }}
    '''
    print(template)
def RunHzyTuple(num,start = 2):
    if start < num:
        PrintHzyTuple(start)
        start += 1
        RunHzyTuple(num,start)
    else:
        PrintHzyTuple(start)


def RunNotHzyTuple(num,start = 2):
    if start < num:
        PrintNotHzyTuple(start)
        start += 1
        RunNotHzyTuple(num,start)
    else:
        PrintNotHzyTuple(start)
def RunHzyTupleIf(num,start = 2):
    if start < num:
        PrintHzyTupleIf(start)
        start += 1
        RunHzyTupleIf(num,start)
    else:
        PrintHzyTupleIf(start)


def RunNotHzyTupleIf(num,start = 2):
    if start < num:
        PrintNotHzyTupleIf(start)
        start += 1
        RunNotHzyTupleIf(num,start)
    else:
        PrintNotHzyTupleIf(start)
if __name__=="__main__":
    print('''
        public static ISelect<T1> WhereLike<T1>(this ISelect<T1> query, Expression<Func<T1, string>> filed, string value, bool isLike = false) where T1 : class
        {
            var valueExp = Expression.Constant(value);
            if (isLike)
            {
                var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }
            else
            {
                query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
            }

            return query;
        }

''')
    print('''
        public static ISelect<T1> WhereLikeIf<T1>(this ISelect<T1> query, bool condition, Expression<Func<T1, string>> filed, string value, bool isLike = false) where T1 : class
        {
            if (!condition){
                return query;
            }
            var valueExp = Expression.Constant(value);
            if (isLike)
            {
                var method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
                query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Call(filed.Body, method, valueExp), filed.Parameters));
            }
            else
            {
                query = query.Where(Expression.Lambda<Func<T1, bool>>(Expression.Equal(filed.Body, valueExp), filed.Parameters));
            }

            return query;
        }

''')
    print("        #region HzyTuple")
    RunHzyTuple(16)
    print("        #endregion\n\n")
    print("        #region HzyTuple If")
    RunHzyTupleIf(16)
    print("        #endregion\n\n")
    print("        #region Not HzyTuple")
    RunNotHzyTuple(16)
    print("        #endregion")
    print("        #region Not HzyTuple If")
    RunNotHzyTupleIf(16)
    print("        #endregion")
posted @ 2022-07-06 21:28  风轻云断  阅读(699)  评论(0编辑  收藏  举报