He creado un filtrado BindingList a partir de esta fuente. Funciona muy bien:

list.Filter("Customer == 'Name'");

hace lo que debe. El trabajo interno como un analizador, que convierte la expresión == o != en System.Linq.Expressions.Expression. En este caso, == se convierte en System.Linq.Expressions.Expression.Equal.

Lamentablemente System.Linq.Expressions.Expression no contiene un operador like y no sé cómo resolver esto.

El código inicial se parece a esto:

private static Dictionary<String, Func<Expression, Expression, Expression>> 
    binaryOpFactory = new Dictionary<String, Func<Expression, Expression, Expression>>();

static Init() {
    binaryOpFactory.Add("==", Expression.Equal);
    binaryOpFactory.Add(">", Expression.GreaterThan);
    binaryOpFactory.Add("<", Expression.LessThan);
    binaryOpFactory.Add(">=", Expression.GreaterThanOrEqual);
    binaryOpFactory.Add("<=", Expression.LessThanOrEqual);
    binaryOpFactory.Add("!=", Expression.NotEqual);
    binaryOpFactory.Add("&&", Expression.And);
    binaryOpFactory.Add("||", Expression.Or);
}

A continuación, he creado una expresión que hará lo que yo quiero:

private static System.Linq.Expressions.Expression<Func<String, String, bool>>
    Like_Lambda = (item, search) => item.ToLower().Contains(search.ToLower());

private static Func<String, String, bool> Like = Like_Lambda.Compile();

por ejemplo,

Console.WriteLine(like("McDonalds", "donAld")); //true
Console.WriteLine(like("McDonalds", "King"));   //false

Pero binaryOpFactory requiere este:

Func<Expression, Expression, Expression>

La predefinidos expresiones parecen ser exactamente eso:

System.Linq.Expressions.Expression.Or;

¿Alguien puede decirme cómo convertir mi expresión?

Y ¿cómo se COMO funcionan? Me pueden ayudar a construir una Expresión, pero necesito entender cómo quieres que funcione primero… regex? contiene? etc?
Que no importa. La implementación final será probablemente ser con regexp. Básicamente tengo una Func<String, String, bool> para que me pase 2 Cadenas y obtener verdadero o falso, como returnvalue. Mi Problema es que no entiendo la Implementación de los Objetos en el Sistema.Linq.Las expresiones.La expresión espacio de Nombres, que parecen ser Func<a la Expresión, la Expresión, la Expresión> (mirar el tipo genérico argements de binaryOpFactory) así que no puedo crear mi propia comparación.
Re comentario: la comprensión de la Expresión de la API puede tomar algo de trabajo… yo trate de hablar de algunos conceptos básicos en mi blog; Jon del libro (C# en Profundidad) también da una visión general de alto nivel.

OriginalEl autor Jürgen Steinblock | 2009-06-05

2 Comentarios

  1. 16

    Algo como:

    static IEnumerable<T> WhereLike<T>(
            this IEnumerable<T> data,
            string propertyOrFieldName,
            string value)
    {
        var param = Expression.Parameter(typeof(T), "x");
        var body = Expression.Call(
            typeof(Program).GetMethod("Like",
                BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public),
                Expression.PropertyOrField(param, propertyOrFieldName),
                Expression.Constant(value, typeof(string)));
        var lambda = Expression.Lambda<Func<T, bool>>(body, param);
        return data.Where(lambda.Compile());
    }
    static bool Like(string a, string b) {
        return a.Contains(b); //just for illustration
    }

    En términos de un Func<Expression,Expression,Expression>:

    static Expression Like(Expression lhs, Expression rhs)
    {
        return Expression.Call(
            typeof(Program).GetMethod("Like",
                BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)
                ,lhs,rhs);
    }
    se ve bien thx, pero necesito algo. que devuelve una Func<a la Expresión, la Expresión, la Expresión> Pero la pregunta es, ¿qué es expresión1, expresión2 y expression3 en este contexto? Un ejemplo de cómo la Expresión.Igual funciona internamente debe ser agradable.
    Debo admitir que yo no entiendo toda la magia detrás de el código, pero el segundo pice de código funciona como un encanto.
    Hace este trabajo con LINQ to entities?
    No tengo idea; ¡háganoslo saber!
    Funciona con LINQ de las Entidades, pero no es lo ideal. La costumbre «Como» método es desconocido y por lo tanto no puede ser traducido a SQL. El resultado es que la consulta se evalúa en este punto. Las alteraciones (que permite la traducción a SQL) es la construcción de la expresión p => p.Prop.Contains(val) directamente así: Expression op = Expression.Call(lhs, "Contains", Type.EmptyTypes, rhs);.

    OriginalEl autor Marc Gravell

  2. 5

    He creado 2 métodos de extensión WhereFilter() para IEnumerable y IQueryable.
    En este modo se puede utilizar este filtro también, por ejemplo, con Entity Framework y es el filtrado se realiza en el servidor.

    He utilizado un filtro basado en * (no ?) lo que podría utilizar el inferior métodos de Linq StartsWith(), EndsWith() y Contains(). Formatos compatibles:*, *, **, a*B

    Uso:

    var filtered = list.WhereFilter(i => i.Name, "a*", "First Name");

    Aquí los conceptos básicos de la clase:

    ///<summary>
    ///Extension Methods for Filtering on IQueryable and IEnumerable
    ///</summary>
    internal static class WhereFilterExtensions
    {
    ///<summary>
    ///Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B
    ///</summary>
    ///<param name="source"></param>
    ///<param name="selector">Field to use for filtering. (E.g: item => item.Name)</param>
    ///<param name="filter">Filter: A*, *A, *A*, A*B</param>
    ///<param name="fieldName">Optional description of filter field used in error messages</param>
    ///<returns>Filtered source</returns>
    public static IEnumerable<T> WhereFilter<T>(this IEnumerable<T> source, Func<T, string> selector, string filter, string fieldName)
    {
    if (filter == null)
    return source;
    if (selector == null)
    return source;
    int astrixCount = filter.Count(c => c.Equals('*'));
    if (astrixCount > 2)
    throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName == null ? "" : " for '" + fieldName + "'"));
    if (filter.Contains("?"))
    throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'"));
    //*XX*
    if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") && filter.EndsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(item => selector.Invoke(item).Contains(filter));
    }
    //*XX
    if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(item => selector.Invoke(item).EndsWith(filter));
    }
    //XX*
    if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(item => selector.Invoke(item).StartsWith(filter));
    }
    //X*X
    if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*"))
    {
    string startsWith = filter.Substring(0, filter.IndexOf('*'));
    string endsWith = filter.Substring(filter.IndexOf('*') + 1);
    return source.Where(item => selector.Invoke(item).StartsWith(startsWith) && selector.Invoke(item).EndsWith(endsWith));
    }
    //XX
    if (astrixCount == 0 && filter.Length > 0)
    {
    return source.Where(item => selector.Invoke(item).Equals(filter));
    }
    //*
    if (astrixCount == 1 && filter.Length == 1)
    return source;
    //Invalid Filter
    if (astrixCount > 0)            
    throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'"));
    //Empty string: all results
    return source;
    }
    ///<summary>
    ///Filters a sequence of values based on a filter with asterix characters: A*, *A, *A*, A*B
    ///</summary>
    ///<param name="source"></param>
    ///<param name="selector">Field to use for filtering. (E.g: item => item.Name)        </param>
    ///<param name="filter">Filter: A*, *A, *A*, A*B</param>
    ///<param name="fieldName">Optional description of filter field used in error messages</param>
    ///<returns>Filtered source</returns>
    public static IQueryable<T> WhereFilter<T>(this IQueryable<T> source, Expression<Func<T, string>> selector, string filter, string fieldName)
    {
    if (filter == null)
    return source;
    if (selector == null)
    return source;
    int astrixCount = filter.Count(c => c.Equals('*'));
    if (astrixCount > 2)
    throw new ApplicationException(string.Format("Invalid filter used{0}. '*' can maximum occur 2 times.", fieldName==null?"":" for '" + fieldName + "'"));
    if (filter.Contains("?"))            
    throw new ApplicationException(string.Format("Invalid filter used{0}. '?' is not supported, only '*' is supported.", fieldName == null ? "" : " for '" + fieldName + "'"));
    //*XX*
    if (astrixCount == 2 && filter.Length > 2 && filter.StartsWith("*") &&         filter.EndsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Call(selector.Body, "Contains", null,  Expression.Constant(filter)),
    selector.Parameters[0]
    )
    );
    }
    //*XX
    if (astrixCount == 1 && filter.Length > 1 && filter.StartsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Call(selector.Body, "EndsWith", null, Expression.Constant(filter)),
    selector.Parameters[0]
    )
    );
    }
    //XX*
    if (astrixCount == 1 && filter.Length > 1 && filter.EndsWith("*"))
    {
    filter = filter.Replace("*", "");
    return source.Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Call(selector.Body, "StartsWith", null,         Expression.Constant(filter)),
    selector.Parameters[0]
    )
    );
    }
    //X*X
    if (astrixCount == 1 && filter.Length > 2 && !filter.StartsWith("*") && !filter.EndsWith("*"))
    {
    string startsWith = filter.Substring(0, filter.IndexOf('*'));
    string endsWith = filter.Substring(filter.IndexOf('*') + 1);
    return source.Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Call(selector.Body, "StartsWith", null,         Expression.Constant(startsWith)),
    selector.Parameters[0]
    )
    ).Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Call(selector.Body, "EndsWith", null,         Expression.Constant(endsWith)),
    selector.Parameters[0]
    )
    );
    }
    //XX
    if (astrixCount == 0 && filter.Length > 0)
    {
    return source.Where(
    Expression.Lambda<Func<T, bool>>(
    Expression.Equal(selector.Body, Expression.Constant(filter)),
    selector.Parameters[0]
    )
    );
    }
    //*
    if (astrixCount == 1 && filter.Length == 1)
    return source;
    //Invalid Filter
    if (astrixCount > 0)
    throw new ApplicationException(string.Format("Invalid filter used{0}.", fieldName == null ? "" : " for '" + fieldName + "'"));
    //Empty string: all results
    return source;
    }
    }
    por ‘con, por ejemplo, Entity Framework y es el filtrado se realiza en el servidor’ ¿te refieres a la base de datos – es decir: traducido a SQL? Yo estaba bajo la impresión de la selector.Invoke impedido EF de la traducción a SQL?
    Sí, la ExpressionTree es convertido por el «Proveedor de LINQ’. Cuando se utiliza Enity Framework, LINQ to entities proveedor de convertir a una cadena SQL que va a ser ejecutado en la base de datos. El resultado no será la Matriz, pero un IEnumerable que camina a través de la DataReader.

    OriginalEl autor Jo VdB

Dejar respuesta

Please enter your comment!
Please enter your name here