2 Stimmen

Dynamische Abfrage in LINQ

Wie schreibe ich eine dynamische Abfrage für Linq, wenn ich sagen, Kundenklasse, die die Felder enthält:

string name
string address
int phoneno

Ich muss auf der Grundlage von Informationen abfragen, die ähnlich lauten

query = string.Empty;

if(!string.IsNullorEmpty(name))
{
   query += "@name = name";
}

if(!string.IsNullorEmpty(address))
{
   query += "@address = address";
}

if(!string.IsNullorEmpty(phoneno))
{
   query += "@phoneno = phoneno";
}

var result = from condition in customer
    where(query)
    select condition;

Bearbeiten #1:

die Elemente sind während der Laufzeit änderbar wie

private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
   here the attribute may be, name alone, or name and address, or name address and phoneno

      foreach(string field in attributes.key)
      {
           query += field == attributes[key];

      }

         Customers[] =ExecuteQuery(query);

}

Wird diese Art der Abfrage von LINQ unterstützt?

Bearbeiten #2:

Hallo Mouk,
Da ich neu in C# bin, bin ich immer noch kämpfen, dies ist nicht für mich arbeiten.

var query = _ConfigFile.ConnectionMasterSection;

for(int i = 0; i < filter.count; i++)
{
    query = result.Where(p => typeof(ConnectionMaster).GetProperty(filter[i].Attribute).Name == filter[i].Value);
}

Dies führt zu einer Leere, während ich folgendes verwendet habe

var query = _ConfigFile.ConnectionMasterSection;

//Hard coded
res.Where(q => q.category == filter[0].Value);

Und es hat so funktioniert, wie ich es erwartet hatte.

Hallo Bryan Watts,
Ich habe Ihren Code auch ausprobiert und bekomme diesen Fehler: "Lambda Parameter nicht im Geltungsbereich".

for(int i = 0; i < filter.count; i++)
{
    Field item = filter[i];

    MemberExpression param = Expression.MakeMemberAccess(Expression.Parameter(typeof(Connection), "p"), typeof(Connection).GetProperty(item.Attribute));

    MemberExpression constant = Expression.MakeMemberAccess(Expression.Constant(item), typeof(Field).GetProperty("Value"));
}

try
{
    var myquery = Queryable.Where(coll, Expression.Lambda<Func<Connection, bool>>(
    Expression.Equal(param, constant), Expression.Parameter(typeof(Connection),"p")));
}

Worin liegt hier der Fehler?

5voto

Quintin Robinson Punkte 78652

Sehen Sie sich das an http://www.albahari.com/nutshell/predicatebuilder.aspx ermöglicht die Erstellung von stark typisierten Prädikaten, was sehr nützlich sein kann. Wenn Sie tatsächlich dynamische String-Prädikate wollen, dann können Sie die LINQ Bibliothek für dynamische Abfragen bereitgestellt von ScottGu.

Beides führt zu dem, was Sie wollen, obwohl ich die erste Option der zweiten vorziehen würde.

Erlaubt Ihnen zu tun:

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

if(!string.IsNullOrEmpty(name))
    predicate = predicate.And(p => p.name == name);

...

var myResults = Context.MyLinTypeQueryTable.Where(predicate);

Und mehr.

4voto

Andrew Arnott Punkte 77359

Hier ist es:

var result = from customer in Customers
             where string.IsNullOrEmpty(phoneNo) || customer.PhoneNo == phoneNo
             where string.IsNullOrEmpty(address) || customer.Address == address
             select customer;

Wenn Sie sich Sorgen machen, dass dies die optimale SQL-Abfrage darunter generiert, sollten Sie wie immer einen SQL Query Analyzer anschließen und überprüfen. Ich glaube aber, dass der Ausdrucks-Parser in Linq To Sql die Where-Klauseln je nach Wert der Argumente entsprechend kürzen wird.

0voto

Mouk Punkte 1777

Sie können die Fluent-Schnittstelle verwenden und für jede Bedingung eine neue Where-Klausel hinzufügen. Etwa so:

 var result = from cus in customers select cus;
 if(!string.IsNullOrEmpty(name))
         result= result.Where(p => p.Name == name);

EDIT nach diesem Kommentar:

Wenn Sie eine Sammlung im Speicher abfragen, können Sie die Eigenschaften mithilfe von Reflection abrufen.

private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
      var result = from cus in customers select cus;    

      foreach(string key in attributes.Keys)
             result= result.Where(p => GetProperty(p, key )== attributes[key]);

         return result.ToList();    
}

Angenommen, GetProperty ruft die Eigenschaft durch Reflexion ab.

Mit Linq2Sql wird diese Methode dazu führen, dass alle Datensätze abgerufen und dann mit Hilfe von Reflection durchlaufen werden.

0voto

Bryan Watts Punkte 43539

Es klingt, als müssten Sie Abfragen dynamisch zusammenstellen.

Siehe meine Antwort a diese Frage .

Er erklärt, wie Abfragen gegen eine IQueryable<T> vom Compiler zusammengesetzt werden, und was Sie tun können, um dynamische Elemente hinzuzufügen.

Editar

Hier ist ein Beispiel dafür, wie Sie dynamisch eine Reihe von Where Bedingungen auf der Grundlage eines IQueryable<Customer> :

// This method ANDs equality expressions for each property, like so:
//
// customers.Where(c => c.Property1 == value1 && c.Property2 == value2 && ...);

private IQueryable<Customer> FilterQuery(IQueryable<Customer> customers, IDictionary<string, string> filter)
{
    var parameter = Expression.Parameter(typeof(Customer), "c");
    Expression filterExpression = null;

    foreach(var filterItem in filter)
    {
        var property = typeof(Customer).GetProperty(filterItem.Key);
        var propertyAccess = Expression.MakeMemberAccess(parameter, property);
        var equality = Expression.Equal(propertyAccess, Expression.Constant(filterItem.Value));

        if(filterExpression == null)
        {
            filterExpression = equality;
        }
        else
        {
            filterExpression = Expression.And(filterExpression, equality);
        }
    }

    if(filterExpression != null)
    {
        var whereBody = Expression.Lambda<Func<Customer, bool>>(filterExpression, parameter);

        customers = customers.Where(whereBody);
    }

    return customers;
}

0voto

JulianR Punkte 15805

Ich habe gute Erfahrungen gemacht mit Dynamischer LINQ .

Ich habe es für eine umfangreiche HTML-Tabelle verwendet, die serverseitig gefiltert und sortiert werden konnte. Der Server erhält eine Anforderung mit einem Anforderungsparameter, wobei der Schlüssel der Name der Eigenschaft (z. B. "Nachname") und der Wert der Wert ist, nach dem die Eigenschaft sortiert werden muss (z. B. "Schmidt"). Mit diesen Informationen habe ich eine Abfragezeichenfolge erstellt, die ich an die Dynamic LINQ's Where Methode.

Grob könnte man sich das wie folgt vorstellen:

public static IQueryable<T> Filter<T>(this IQueryable<T> query, Dictionary<string, string> dictionary)
{
    Type t = typeof(T);
    StringBuilder sb = new StringBuilder();
        PropertyInfo[] properties = t.GetProperties();
        foreach(string key in dictionary.Keys)
        {
            PropertyInfo property = properties.Where(p => p.Name == key).SingleOrDefault();
            if(property != null)
            {
                if (sb.Length > 0) sb.Append(" && ");

                string value = dictionary[key];

                sb.Append(string.Format(@"{0}.ToString().Contains(""{1}"")", key, value));
            }
        }

        if (sb.Length > 0)
        return query.Where(sb.ToString());
    else
            return query;
}

Der Code ist aus der Luft gegriffen und daher ungetestet.

Dies ist natürlich die einfachste Version: Sie führt einen einfachen String-Vergleich durch. Wenn Sie einen numerischen Vergleich durchführen wollen (d.h. Sie wollen z.B. den Benutzer, dessen UserID genau 100 ist, und nicht den, dessen UserID.ToString().Contains("100") ), oder verschachtelte Eigenschaften abfragen ( Customer.Company.CompanyAddress zum Beispiel), oder Abfrage Sammlungen wird dies komplizierter. Sie sollten auch an die Sicherheit denken: Dynamic LINQ ist zwar nicht anfällig für SQL-Injection, aber Sie sollten nicht zulassen, dass es blindlings alle Benutzereingaben analysiert.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X