具有未知属性类型的IQueryable WhereLike扩展

时间:2017-07-07 13:50:21

标签: c# dynamic-linq

我正在尝试为IQueryable创建WhereLike扩展,但我无法在运行时知道该属性的类型。

这是我的代码:

def getTasks(database):
    print "Checking for new tasks"
    query = 'SELECT * FROM fosbot_tasks WHERE asked = %s'
    args = (0)
    try:
        with database.cursor() as cursor:
            cursor.execute(query, args)
        return cursor.fetchall()
    except database.Error as e:
        print e

我得到了例外:实例属性' foo'未定义类型' System.Object'

您是否知道在编译时不知道目标类型的情况下处理属性设置的方法?

2 个答案:

答案 0 :(得分:1)

如果您能够使用通用IQueryable<T>变体,这将变得更加容易,因为您不再需要CreateQuery,并且可以直接针对IQueryable<T>来源执行。

public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, 
    string pattern)
{
    if (source == null) throw new ArgumentNullException("source");
    if (propertyName == null) throw new ArgumentNullException("propertyName");

    var a = Expression.Parameter(typeof(T), "a");
    var prop = Expression.PropertyOrField(a, propertyName);

    var expr = Expression.Call(
            typeof(SqlMethods), "Like",
            null,
            prop, Expression.Constant(pattern));

    var lambda = Expression.Lambda<Func<T, bool>>(expr, a); 
    return source.Where(lambda);
}

注意两个要点:

如果我们使用PropertyOrField,我们可以正确地支持为可能暴露字段的Linq-2-SQL生成的代码,而不仅仅是抓取属性。

此外,由于我们正在针对IQueryable<T>源执行,因此我们需要根据“Like”MethodCallExpression的结果创建一个lambda表达式。

如果您需要非泛型变体,您仍然可以完成同样的事情,尽管您需要将Like MethodCallExpression包装在Where MethodCallExpression中,以使其结构合理:

public static IQueryable WhereLike(this IQueryable source, string propertyName, 
    string pattern)
{
    if (source == null) throw new ArgumentNullException("source");
    if (propertyName == null) throw new ArgumentNullException("propertyName");

    var a = Expression.Parameter(source.GetType().GetGenericArguments().First(), "a");
    var prop = Expression.PropertyOrField(a, propertyName);

    var expr = Expression.Call(
            typeof(SqlMethods), "Like",
            null,
            prop, Expression.Constant(pattern));

    MethodCallExpression whereCallExpression = Expression.Call(
            typeof(Queryable),
            "Where",
            new Type[] { source.ElementType },
            source.Expression,
            Expression.Lambda(expr, a));

    return source.Provider.CreateQuery(whereCallExpression);
}

您可以使用通配符调用任一变体:

var data = source.WhereLike("ColumnName", "%o%");   

答案 1 :(得分:-1)

  

确定特定字符串是否与指定模式匹配。模式可以包括常规字符和通配符。在模式匹配期间,常规字符必须与字符串中指定的字符完全匹配。但是,通配符可以与字符串的任意片段匹配。使用通配符使LIKE运算符比使用=和!=字符串比较运算符更灵活。如果任何一个参数不是字符串数据类型,则SQL Server数据库引擎会将其转换为字符串数据类型(如果可能)。 MSDN

like运算符仅适用于string类型。如果这是您想要做的,您只能使用Contains方法实现,还有StartsWithEndsWith等效。

您只能在此扩展方法中使用Where方法

  var containsParam = Expression.Parameter(typeof(T), "p");
MemberExpression multiSelectmember = Expression.Property(containsParam,propertyname);
    var lstValues = stringvalue;

ConstantExpression multiSelectConstant = Expression.Constant(stringvalue);
var callExpression = Expression.Call(typeof(String), "Contains",
new[] { typeof(string) }, multiSelectConstant, multiSelectmember);
var containsexp = Expression.Lambda<Func<T, bool>>(callExpression,
                                                containsParam);