试图开发一种新的扩展方法[英] Trying to develop a new extension method

本文是小编为大家收集整理的关于试图开发一种新的扩展方法的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我正在使用实体框架,我开发了此扩展方法:

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

现在此代码不起作用! 我需要了解如何声明类似扩展方法的"字段".

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

在运行时我会收到此错误:不可能的utilizzare un'espressione di tipo'system.func`2 [testmdf.Equipment,system.string]'per un tipo restituito'system.string'

推荐答案

这是我的第一个版本:

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

}

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

其他推荐答案

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

,如果您说"此工作",您将获得更好的答案?运行时错误?异常消息?)

首先,您是否知道动态linq ?它允许您延迟有关如何构造查询的决策,直到运行时,并可能为您解决许多问题.

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

您的Like扩展方法采用表达式(呼叫者通常应该以lambda的形式写出,因为这是这些事物的全部要点).该表达式将从查询结果集中转换一个"记录"并返回字符串值(大概是从记录中存储的数据中选择它).该方法还采用一个值字符串.

,但然后(手工)构造了自己的谓词,该谓词调用Contains Contains lambda的主体.

我猜这应该起作用,因为该lambda的结果是字符串.但是,我看不出您为什么要这么做.有什么问题:

var result = from e in context.es
             where e.Field.Contains("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);

    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]

一些想法?

本文地址:https://www.itbaoku.cn/post/1557193.html

问题描述

I'm using the Entity Framework and I developed this extension method:

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

this code work correctly if I use it like this:

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

Now I need to call this extension method programmatically:

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

Now this code doesn't work! I need to understand how to declare the "field" of the Like extended methods.

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

At runtime I receive this error: Impossibile utilizzare un'espressione di tipo 'System.Func`2[TestMdf.Equipment,System.String]' per un tipo restituito 'System.String'

推荐答案

This is my first release:

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

}

I will modify to manage some rules like "phrase" + and - operator.

其他推荐答案

I assume your second code snippet is just a truncated example - if you really did that, then the results would be unpredictable, because you're taking the first property returned by reflection, which can change between runs of your program.

You'll get better answers if you say "This worked" followed by a description of what happened, and "This didn't work" followed by a description of how you could tell that it didn't work (compiler error? runtime error? exception message?)

Firstly, are you aware of Dynamic Linq? It allows you to delay decisions about how a query should be structured until runtime and may solve many of your problems for you.

But assuming this is a learning exercise...

Your Like extension method takes an expression (which a caller ought to usually write out as a lambda, as that's the whole point of these things). That expression will convert a "record" from a query result set and return a string value (presumably selecting it from the data stored in the record). The method also takes a value string.

But it then constructs (by hand) its own predicate that calls the Contains method on the body of the field lambda.

I guess this ought to work, because the result of that lambda is a string. However, I can't see why you're doing this the hard way. What's wrong with:

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

其他推荐答案

Now I found a partial solution to my problem:

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

Now the next step is to concatenate all the field list:

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

Some ideas?