【问题标题】:IQueryable WhereLike extension with unknown property type具有未知属性类型的 IQueryable WhereLike 扩展
【发布时间】:2017-07-07 23:17:11
【问题描述】:

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

这是我的代码:

    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(typeof(object), "a");
        var prop = Expression.Property(a, propertyName);

        return source.Provider.CreateQuery(
            Expression.Call(
                typeof(SqlMethods), "Like",
                null,
                prop, Expression.Constant(pattern)));
    }

我得到了异常:没有为类型“System.Object”定义实例属性“foo”

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

【问题讨论】:

  • SQL like 运算符不是只对字符串起作用吗?
  • @Fran 为什么这很重要?
  • @Servy 因为如果他无法确定属性的类型,他可以尝试在不支持它的属性上使用 WhereLike(DateTime、Double、...)然后他当它发生时会得到一个运行时异常。也许他试图抓住源 ElementType 并使用反射查找属性类型。
  • @Fran 是的,如果提供的属性类型不是字符串,代码将在运行时失败。尝试自己检查并没有真正的帮助,因为它在运行时仍然会失败。这就是拥有完全动态类型代码的本质。值类型中的错误在运行时出现,而不是编译时。
  • @Servy True。我想我正试图尽早捕获错误。并返回一条消息,表明此函数不支持字符串以外的类型。所以我只是启动了 SSMS 来运行一些类似的查询。一些喜欢在日期时间工作,但不是我预期的方式。就像十进制一样不起作用。有趣的是它们不会抛出语法错误,它们只是不返​​回数据。

标签: c# dynamic-linq


【解决方案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&lt;T&gt; 源执行的,因此我们需要根据“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%");   

【讨论】:

    【解决方案2】:

    确定特定字符串是否与指定模式匹配。模式可以包括常规字符和通配符。在模式匹配过程中,常规字符必须与字符串中指定的字符完全匹配。但是,通配符可以与字符串的任意片段匹配。使用通配符使 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);
    

    【讨论】:

    • 我也尝试过 StartsWith、EndsWith 等...但是模式复杂(“%​​AA_BB%CC%”),这个解决方案似乎有限
    猜你喜欢
    • 1970-01-01
    • 2010-11-14
    • 2022-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-29
    • 2010-12-15
    • 1970-01-01
    相关资源
    最近更新 更多