Попытка разработать новый метод расширения - PullRequest
0 голосов
/ 31 мая 2009

Я использую Entity Framework и разработал этот метод расширения:

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'

Ответы [ 3 ]

0 голосов
/ 31 мая 2009

Теперь я нашел частичное решение моей проблемы:

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]

Некоторые идеи?

0 голосов
/ 31 мая 2009

Это мой первый релиз:

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));

}

Я буду изменять некоторые правила, такие как «фраза» + и - оператор.

0 голосов
/ 31 мая 2009

Я предполагаю, что ваш второй фрагмент кода является просто усеченным примером - если вы действительно это сделаете, то результаты будут непредсказуемыми, потому что вы берете первое свойство, возвращаемое отражением, которое может меняться между запусками вашей программы.

Вы получите лучшие ответы, если скажете «Это сработало», за которым следует описание того, что произошло, и «Это не сработало», а затем описание того, как вы могли бы сказать, что это не сработало (ошибка компилятора «ошибка времени выполнения» сообщение об исключении?)

Во-первых, знаете ли вы Dynamic Linq ? Он позволяет вам отложить принятие решения о том, как запрос должен быть структурирован, до времени выполнения и может решить многие ваши проблемы за вас.

Но при условии, что это учебное упражнение ...

Ваш метод расширения Like принимает выражение (которое вызывающий должен обычно записывать как лямбда, поскольку в этом весь смысл этих вещей). Это выражение преобразует «запись» из набора результатов запроса и возвращает строковое значение (предположительно, выбирая его из данных, хранящихся в записи). Метод также принимает строку значения.

Но затем он создает (вручную) свой собственный предикат, который вызывает метод Contains для тела field лямбда.

Я думаю, это должно сработать, потому что результатом этой лямбды является строка. Тем не менее, я не понимаю, почему вы делаете это трудным путем. Что не так с:

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