【问题标题】:Trying to develop a new extension method尝试开发一种新的扩展方法
【发布时间】:2009-05-31 06:12:17
【问题描述】:

我正在使用实体框架并开发了这个扩展方法:

public static IQueryable<TResult> Like<TResult>(this IQueryable<TResult> query, Expression<Func<TResult, string>> field, string value) 
{
    var expression = Expression.Lambda<Func<TResult, bool>>(
        Expression.Call(field.Body, typeof(string).GetMethod("Contains"),
        Expression.Constant(value)), field.Parameters);

    return query.Where(expression);
}

如果我这样使用,这段代码可以正常工作:

var result = from e in context.es.Like(r => r.Field, "xxx")
             select e

现在我需要以编程方式调用这个扩展方法:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind)
{
    // Collect fields
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties();
    List<string> fields = new List<string>();
    foreach (PropertyInfo propertyInfo in propertiesInfo)
    {
        if (
            (propertyInfo.PropertyType == typeof(string)) ||
            (propertyInfo.PropertyType == typeof(int)) ||
            (propertyInfo.PropertyType == typeof(long)) ||
            (propertyInfo.PropertyType == typeof(byte)) ||
            (propertyInfo.PropertyType == typeof(short))
            )
        {
            fields.Add(propertyInfo.Name);
        }
    }

    ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name);
    Expression expression = Expression.Lambda(Expression.Property(parameter, typeof(TSource).GetProperty(fields[0])), parameter);
    Expression<Func<TSource, string>> field = Expression.Lambda<Func<TSource, string>>(expression, parameter);

    return source.Like(field, textToFind);
}

现在这段代码不起作用! 我需要了解如何声明 Like 扩展方法的“字段”。

Expression<Func<TSource, string>> field = Expression.Lambda<Func<TSource, string>>(expression, parameter);

在运行时我收到此错误:Impossibile utilizzare un'espressione di Tipo 'System.Func`2[TestMdf.Equipment,System.String]' per un tipo restituito 'System.String'

【问题讨论】:

  • 我对你的第二种扩展方法有点困惑。您遍历元素上的所有 PropertyInfo,将它们添加到字段集合中,然后只需选择第一个。这似乎真的很模糊和没有针对性......很可能会返回随机结果。在我提供任何答案之前……这是你想要的吗?或者你真的需要选择一个特定的属性来搜索......或者你需要遍历所有属性并为每个属性调用 .Like() 并聚合结果?
  • 嗨,你对循环的看法是正确的......这个扩展方法的最终版本将生成几个 where 子句。例如,您有字段 F1、F2、F3 是数字或字符串,我想在这样的文本中搜索: ' ' + F1 + ' ' + F2 + ' ' + F3 + ' ' LIKE '% %' 但是会更复杂...我想添加一个小搜索引擎:' ' + F1 + ' ' + F2 + ' ' + F3 + ' ' LIKE '%%' AND ' ' + F1 + ' ' + F2 + ' ' + F3 + ' ' LIKE '%%' AND ' ' + F1 + ' ' + F2 + ' ' + F3 + ' ' NOT LIKE '%% ' 并管理短语。

标签: c# linq entity-framework linq-to-entities lambda


【解决方案1】:

我假设你的第二个代码 sn-p 只是一个截断的例子 - 如果你真的这样做了,那么结果将是不可预测的,因为你正在使用反射返回的第一个属性,它可以在你的程序运行之间改变.

如果您说“这有效”,然后描述发生的事情,然后说“这不起作用”,然后描述如何判断它不起作用(编译器错误),您会得到更好的答案?运行时错误?异常消息?)

首先,你知道Dynamic Linq吗?它允许您将关于查询结构的决定延迟到运行时,并且可以为您解决许多问题。

但假设这是一个学习练习......

您的 Like 扩展方法采用一个表达式(调用者通常应该将其写为 lambda,因为这就是这些事情的全部意义所在)。该表达式将从查询结果集中转换“记录”并返回一个字符串值(可能是从存储在记录中的数据中选择它)。该方法还接受一个值字符串。

但它随后(手动)构造了自己的谓词,该谓词在 field lambda 的主体上调用 Contains 方法。

我想这应该可行,因为那个 lambda 的结果是一个字符串。但是,我不明白你为什么要这样做。有什么问题:

var result = from e in context.es
             where e.Field.Contains("xxx"))
             select e

【讨论】:

  • 嗨,我想在所有字段中进行搜索,所以您必须像这样 ' ' + F1 + ' ' + F2 ... 我需要一个扩展方法,因为我不需要'不知道字段列表。你是对的,我的第二个代码 sn-p 只是一个截断的例子。我觉得这个扩展的方法会很有用,希望能找到一些帮助来完成它。
  • 我的第一个扩展方法是我发现动态调用 Contains 方法的唯一方法。我需要动态地调用它,因为我必须构建要比较的字段。像 (" " + F1 + " " + F2 + " ").Contains(" " + + " ")
  • 我需要了解如何构建参数来调用 Like 方法。如果你写在你的代码 field.Like("text") 这很简单。但我不知道我该如何称呼它。关于“不起作用”:它可以编译,但我在运行时收到此错误:“Impossibile utilizzare un'espressione di Tipo 'System.Func`2[TestMdf.Equipment,System.String]' per un tipo restituito 'System.字符串'"
【解决方案2】:

现在我找到了解决问题的部分方法:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind)
{
    // Collect fields
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties();
    List<string> fields = new List<string>();
    foreach (PropertyInfo propertyInfo in propertiesInfo)
    {
        if (
            (propertyInfo.PropertyType == typeof(string)) ||
            (propertyInfo.PropertyType == typeof(int)) ||
            (propertyInfo.PropertyType == typeof(long)) ||
            (propertyInfo.PropertyType == typeof(byte)) ||
            (propertyInfo.PropertyType == typeof(short))
            )
        {
            fields.Add(propertyInfo.Name);
        }
    }

    ParameterExpression parameter = Expression.Parameter(typeof(TSource),     source.ElementType.Name);

    var property = typeof(TSource).GetProperty(fields[0]);
    var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var constantValue = Expression.Constant(textToFind);
    var equality = Expression.Call(Expression.Call(Expression.Property(parameter,     property), "ToUpper", null, null), typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(textToFind.ToUpper()));

    return source.Where(Expression.Lambda<Func<TSource, bool>>(equality, parameter));
}

现在下一步是连接所有字段列表:

" " + fields[0] + " " + ... fields[n]

一些想法?

【讨论】:

  • 字段连接?您可能想使用 StringBuilder。
【解决方案3】:

这是我的第一个版本:

public static IQueryable<TSource> SearchInText<TSource>(this IQueryable<TSource> source, string textToFind)
{
    if (textToFind.Trim() == "")
    {
        return source;
    }
    string[] textToFindList = textToFind.Replace("'", "''").Split(' ');

    // Collect fields
    PropertyInfo[] propertiesInfo = source.ElementType.GetProperties();
    List<string> fieldList = new List<string>();
    foreach (PropertyInfo propertyInfo in propertiesInfo)
    {
        if (
            (propertyInfo.PropertyType == typeof(string)) ||
            (propertyInfo.PropertyType == typeof(int)) ||
            (propertyInfo.PropertyType == typeof(long)) ||
            (propertyInfo.PropertyType == typeof(byte)) ||
            (propertyInfo.PropertyType == typeof(short))
            )
        {
            fieldList.Add(propertyInfo.Name);
        }
    }

    ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name);
    MethodInfo concatMethod = typeof(String).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });

    var spaceExpression = Expression.Constant(" ");
    var concatenatedField = BinaryExpression.Add(spaceExpression, Expression.MakeMemberAccess(parameter, typeof(TSource).GetProperty(fieldList[0])), concatMethod);

    for (int i = 1; i < fieldList.Count; i++)
    {
        concatenatedField = BinaryExpression.Add(concatenatedField, spaceExpression, concatMethod);
        concatenatedField = BinaryExpression.Add(concatenatedField, Expression.MakeMemberAccess(parameter, typeof(TSource).GetProperty(fieldList[i])), concatMethod);
    }

    concatenatedField = BinaryExpression.Add(concatenatedField, spaceExpression, concatMethod);
    var fieldsExpression = Expression.Call(concatenatedField, "ToUpper", null, null);

    var clauseExpression = Expression.Call(
        fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
        Expression.Constant(textToFindList[0].ToUpper())
        );

    if (textToFindList.Length == 1)
    {
       return source.Where(Expression.Lambda<Func<TSource, bool>>(clauseExpression, parameter));
    }

    BinaryExpression expression = Expression.And(Expression.Call(
            fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
            Expression.Constant(textToFindList[1].ToUpper())
            ), clauseExpression);
    for (int i = 2; i < textToFindList.Length; i++)
    {
        expression = Expression.And(Expression.Call(
            fieldsExpression, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
            Expression.Constant(textToFindList[i].ToUpper())
            ), expression);
    }

    return source.Where(Expression.Lambda<Func<TSource, bool>>(expression, parameter));

}

我将修改以管理一些规则,例如“短语”+ 和 - 运算符。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-20
    • 2018-11-14
    • 2020-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-21
    相关资源
    最近更新 更多