webentwicklung-frage-antwort-db.com.de

Anfügen an einen Ausdruck

Gestützt auf meine Frage von gestern :

wenn ich an meinen vorhandenen Wo-Ausdruck anhängen müsste, wie würde ich ihn anhängen?

Expression<Func<Client, bool>> clientWhere = c => true;

if (filterByClientFName)
{
    clientWhere = c => c.ClientFName == searchForClientFName;
}

 if (filterByClientLName)
    {
        clientWhere = c => c.ClientLName == searchForClientLName;
    }

Der Benutzer kann entweder Vor- oder Nachnamen oder beides eingeben. Wenn sie beide eingeben, möchte ich an den Ausdruck anhängen. Versucht zu sehen, ob es ein Äquivalent zu einem Anhang gibt, wo ich tun könnte

clientWhere.Append or clientWhere += add new expression

oder etwas ähnliches

33
ltech

Ich glaube, Sie können einfach Folgendes tun:

Expression<Func<Client, bool>> clientWhere = c => true;

if (filterByClientFName)
{
    var prefix = clientWhere.Compile();
    clientWhere = c => prefix(c) && c.ClientFName == searchForClientFName;
}
if (filterByClientLName)
{
    var prefix = clientWhere.Compile();
    clientWhere = c => prefix(c) && c.ClientLName == searchForClientLName;
}

Wenn Sie alles in Expression-land behalten müssen (zur Verwendung mit IQueryable), können Sie auch Folgendes tun:

Expression<Func<Client, bool>> clientWhere = c => true;

if (filterByClientFName)
{
    Expression<Func<Client, bool>> newPred = 
        c => c.ClientFName == searchForClientFName;
    clientWhere = Expression.Lambda<Func<Freight, bool>>(
        Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters);
}
if (filterByClientLName)
{
    Expression<Func<Client, bool>> newPred = 
        c => c.ClientLName == searchForClientLName;
    clientWhere = Expression.Lambda<Func<Freight, bool>>(
        Expression.AndAlso(clientWhere, newPred), clientWhere.Parameters);
}

Dies kann durch Definieren dieser Erweiterungsmethode weniger ausführlich gemacht werden:

public static Expression<TDelegate> AndAlso<TDelegate>(this Expression<TDelegate> left, Expression<TDelegate> right)
{
    return Expression.Lambda<TDelegate>(Expression.AndAlso(left, right), left.Parameters);
}

Sie können dann die folgende Syntax verwenden:

Expression<Func<Client, bool>> clientWhere = c => true;
if (filterByClientFName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientFName == searchForClientFName);
}
if (filterByClientLName)
{
    clientWhere = clientWhere.AndAlso(c => c.ClientLName == searchForClientLName);
}
46
Jason

Dies ist ein komplexes Szenario. Sie bauen fast Ihre eigene Abfrage-Engine auf LINQ auf. Die Lösung von JaredPar (wo ist sie hingegangen?) Ist großartig, wenn Sie ein logisches UND zwischen all Ihren Kriterien wünschen, aber das ist möglicherweise nicht immer der Fall.

Als ich mich kürzlich in einem meiner Projekte damit auseinandergesetzt habe, habe ich zwei Listen erstellt:

List<Predicate<T>> andCriteria;
List<Predicate<T>> orCriteria;

(In diesem Fall ist T Client für Sie)

Ich würde die Listen mit Prädikaten füllen, die ich für wahr halten möchte. Zum Beispiel,

decimal salRequirement = 50000.00;
andCriteria.Add(c => c.Salary > salRequirement);
orCriteria.Add(c => c.IsMarried);

Dann würde ich nach allen Kriterien in den Listen in meiner Where-Klausel suchen. Zum Beispiel:

Expression<Func<Client, bool>> clientWhere =
    c => andCriteria.All(pred => pred(c) ) && orCriteria.Any(pred => pred(c) );

Dies könnte aus Gründen der Lesbarkeit auch mit einer for-Schleife erfolgen. Denken Sie daran, bei der Anwendung Ihrer OR - und AND-Klauseln die richtige Reihenfolge der Operationen einzuhalten.

8
JoshJordan

Werfen Sie einen Blick auf Predicate Builder , ich glaube, das könnte bei Ihnen funktionieren.

6

Wenn Sie auf ein ähnliches Problem stoßen, finden Sie alle möglichen Lösungen in diesem großartigen Thema . Oder verwenden Sie einfach PredicateBuilder ist ein großartiger Helfer für diese Poporse.

var predicate = PredicateBuilder.True<Client>();

if (filterByClientFName)
{
    predicate = predicate.And(c => c.ClientFName == searchForClientFName);
}

if (filterByClientLName)
{
        predicate = predicate.And(c => c.ClientLName == searchForClientLName);
}

var result = context.Clients.Where(predicate).ToArray();

Es ist eine Builder-Implementierung.

public static class PredicateBuilder
    {
        // Creates a predicate that evaluates to true.        
        public static Expression<Func<T, bool>> True<T>() { return param => true; }

        // Creates a predicate that evaluates to false.        
        public static Expression<Func<T, bool>> False<T>() { return param => false; }

        // Creates a predicate expression from the specified lambda expression.        
        public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }

        // Combines the first predicate with the second using the logical "and".        
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.AndAlso);
        }

        // Combines the first predicate with the second using the logical "or".        
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
        {
            return first.Compose(second, Expression.OrElse);
        }

        // Negates the predicate.        
        public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
        {
            var negated = Expression.Not(expression.Body);
            return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
        }

        // Combines the first expression with the second using the specified merge function.        
        static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
        {
            // Zip parameters (map from parameters of second to parameters of first)
            var map = first.Parameters
                .Select((f, i) => new { f, s = second.Parameters[i] })
                .ToDictionary(p => p.s, p => p.f);

            // replace parameters in the second lambda expression with the parameters in the first
            var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);

            // create a merged lambda expression with parameters from the first expression
            return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
        }

        class ParameterRebinder : ExpressionVisitor
        {
            readonly Dictionary<ParameterExpression, ParameterExpression> map;

            ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
            {
                this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
            }

            public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
            {
                return new ParameterRebinder(map).Visit(exp);
            }

            protected override Expression VisitParameter(ParameterExpression p)
            {
                ParameterExpression replacement;
                if (map.TryGetValue(p, out replacement))
                {
                    p = replacement;
                }
                return base.VisitParameter(p);
            }
        }
    }
1
trueboroda

Es ist nicht genau die Antwort auf Ihre Frage, aber ich habe nach der gleichen Sache gesucht, die Sie sind, und dann habe ich eine bessere Antwort auf meine Frage gefunden.

Anstatt einen dynamischen Ausdruck zu erstellen, können Sie IQueryable abrufen und dann filtern, was Sie möchten:

var customers = CustomerRepository.AllEntities();

if (!forename.IsNullOrEmpty())
    customers = customers.Where(p => p.Forename == forename);
if (!familyname.IsNullOrEmpty())
    customers = customers.Where(p => p.FamilyNames.Any(n => n.Name==familyname));
if (dob.HasValue)
    customers = customers.Where(p => p.DOB == dob);

Hinweis: Ich hatte Bedenken, mehr als eine ".Where" -Anweisung auszuführen, weil ich befürchtete, dass dies mehr als eine Abfrage in der Datenbank erzeugen würde, oder weil ich alle Datensätze abrufen und dann filtern müsste. Dies ist jedoch nicht der Fall. Linq Dynamic generiert nur eine Abfrage, wenn Sie die .ToList () -Methode aufrufen.

Hier können Sie die ursprüngliche Frage sehen, von der ich das Beispiel genommen habe.

1
Daniel

Oder etwas, das ich zu Josh hinzufügen möchte (lege es in meine Trickkiste):

public static IQueryable<TSource> ObjectFilter<TSource>(this TSource SearchObject, List<Predicate<TSource>> andCriteria, List<Predicate<TSource>> orCriteria) where TSource : IQueryable<TSource>
        {
            //Yeah :)
            Expression<Func<TSource, bool>> ObjectWhere = O => andCriteria.All(pred => pred(O)) && orCriteria.Any(pred => pred(O));
            return SearchObject.Where<TSource>(ObjectWhere);
        }
0
MarkWalls

Ich habe versucht, solche Sachen zu implementieren. Ich habe einen Tag gebraucht, um das herauszufinden. Meine Lösung basiert auf dem Filtern in einer Schleife, die auf einem Array von Prädikaten basiert. Hinweis: Es handelt sich um eine generische und basierende Reflektion, da die einzigen Informationen zu Klasse und Feld String sind. Um es einfach zu machen, rufe ich direkt die Model-Klasse auf, aber in einem Projekt solltest du von einem Controller gehen, der das Model aufruft.

Also los geht's: Der Model-Teil, in dem T ein Generic in der Klasse ist

    public class DALXmlRepository<T> where T : class
    {
    public T GetItem(Array predicate)
    {
        IQueryable<T> QueryList = null;

        QueryList = ObjectList.AsQueryable<T>().Where((Expression<Func<T, bool>>)predicate.GetValue(0));
        for (int i = 1; i < predicate.GetLength(0); i++)
        {
            QueryList = QueryList.Where((Expression<Func<T, bool>>)predicate.GetValue(i));
        }

        if (QueryList.FirstOrDefault() == null)
            throw new InvalidOperationException(this.GetType().GetGenericArguments().First().Name + " not found.");
        return QueryList.FirstOrDefault();
    }
    }

Jetzt ist der LambdaExpression Builder eine Basis (mit String-Typ oder etwas anderem), Sie können ihn mit mehr Funktionalität verbessern:

    private static Expression BuildLambdaExpression(Type GenericArgument, string FieldName, string FieldValue)
    {
        LambdaExpression lambda = null;

        Expression Criteria = null;

        Random r = new Random();
        ParameterExpression predParam = Expression.Parameter(GenericArgument, r.Next().ToString());

        if (GenericArgument.GetProperty(FieldName).PropertyType == typeof(string))
        {
            Expression left = Expression.PropertyOrField(predParam, FieldName);
            Expression LefttoUpper = Expression.Call(left, "ToUpper", null, null);
            //Type du champ recherché
            Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
            Expression right = Expression.Constant(FieldValue, propType);
            Expression RighttoUpper = Expression.Call(right, "ToUpper", null, null);
            Criteria = Expression.Equal(LefttoUpper, RighttoUpper);
        }
        else
        {
            Expression left = Expression.PropertyOrField(predParam, FieldName);
            Type propType = GenericArgument.GetProperty(FieldName).PropertyType;
            Expression right = Expression.Constant(Convert.ChangeType(FieldValue, propType), propType);

            Criteria = Expression.Equal(left, right);
        }

        lambda = Expression.Lambda(Criteria, predParam);
        return lambda;
    }

Nun die Calling Funktion:

    public static Hashtable GetItemWithFilter(string Entity, XMLContext contextXML, Hashtable FieldsNameToGet, Hashtable FieldFilter)
    {
        //Get the type
        Type type = Type.GetType("JP.Model.BO." + Entity + ", JPModel");
        Type CtrlCommonType = typeof(CtrlCommon<>).MakeGenericType( type );
        //Making an instance DALXmlRepository<xxx> XMLInstance = new DALXmlRepository<xxx>(contextXML);
        ConstructorInfo ci = CtrlCommonType.GetConstructor(new Type[] { typeof(XMLContext), typeof(String) });
        IControleur DalInstance = (IControleur)ci.Invoke(new object[] { contextXML, null });

        //Building the string type Expression<func<T,bool>> to init the array
        Type FuncType = typeof(Func<,>).MakeGenericType( type ,typeof(bool));
        Type ExpressType = typeof(Expression<>).MakeGenericType(FuncType);
        Array lambda = Array.CreateInstance(ExpressType,FieldFilter.Count);

        MethodInfo method = DalInstance.GetType().GetMethod("GetItem", new Type[] { lambda.GetType() });

        if (method == null)
            throw new InvalidOperationException("GetItem(Array) doesn't exist for " + DalInstance.GetType().GetGenericArguments().First().Name);

        int j = 0;
        IDictionaryEnumerator criterias = FieldFilter.GetEnumerator();
        criterias.Reset();
        while (criterias.MoveNext())
        {
            if (!String.IsNullOrEmpty(criterias.Key.ToString()))
            {
                lambda.SetValue(BuildLambdaExpression(type, criterias.Key.ToString(), criterias.Value.ToString()),j);
            }
            else
            {
                throw new JPException(JPException.MessageKey.CONTROLER_PARAMFIELD_EMPTY, "GetItemWithFilter", criterias.Key.ToString());
            }
            j++;
        }

        Object item = method.Invoke(DalInstance, new object[] { lambda });
        }

Das Argument lautet: String Entity: Name der Entitätsklasse. XMLContext: Es ist die Arbeitseinheit des Repositorys, Argument, das ich zum Initialisieren der Modellklasse verwende mach den Lambda-Ausdruck

Viel Glück.

0
Alan