none
Linq 'anpassen' RRS feed

  • Frage

  • Hi,

    ist es möglich Linq irgendwie wie nachstehend zu erweitern:

    class Contacts
    {
    public string LastName {get; set; }
    public string FirstNname {get; set; }
    ....
    }

    var q = contacts.Where( cnt => sucheInAlleFeldernNach( "Muster" ) ); oder
    var q2 = contacts.Where( cnt =>sucheInAlleFeldernNach( "Muster" ) && cnt.FirstName == "Hubert" );

    Für die Umsetzung eines eigenen Linq Providers müsste ich noch spezielle Suchen anbieten. Ich möchte irgendwie formulieren können, dass z.B. in allen Feldern nach "Muster" gesucht werden soll.
    Mein erster Gedanke war, es über eigene IQueryable Erweiterungsmethoden zu versuchen. Da es sicherlich schöner wäre über die bereits vorhandenen Funktionen (z.B. Where) zu gehen, habe ich diesen Ansatz verworfen.

    Mein nächster Ansatz wäre wie folgt:

    var q3 = contacts.Where( cnt => cnt == new SucheInAllenFeldern( "Muster" ));

    Habt Ihr hierzu evtl. noch einen besseren Vorschlag?

    Vielen Dank und viele Grüße,
    Christian

    Dienstag, 25. Juni 2013 13:57

Antworten

  • Hallo Christian,

    hier kommt ein Beispiel, in dem alle Properties mittels Reflection ermittelt werden und auf Übereinstimmung mit einem regulären Ausdruck überprüft werden. Im Code findest Du sowohl eine Query-Comprehension- als auch eine Fluent-Variante (de gustibus non est disputandum):

    // Eine Liste von Contact-Objekten
    List<Contact> contacts = new List<Contact> { 
     new Contact { FirstName="Karl", LastName="Baumann" },
     new Contact { FirstName="Tim", LastName="Bauers" },
     new Contact { FirstName="Anna", LastName="Bau" },
     new Contact { FirstName="Baudy", LastName="Smith" },
     new Contact()
    };
    
    // Regulärer Ausdruck: Suche Werte, in denen die
    // Zeichenfolge "au" von einem der in Klammern angegebenen
    // Buchstaben gefolgt wird
    var pattern = "au[m,e,d]";
    
    // Durchsuche alle Properties der Contact-Objekte 
    // auf Übereinstimmung mit dem regulären Ausdruck
    // (Query Comprehension-Syntax)
    var query = from p in typeof(Contact).GetProperties()
                from c in contacts
                let f = (string)p.GetValue(c, new object[]{})
    	    where f != null && 
    Regex.IsMatch(f, pattern, RegexOptions.Compiled) select c; // Ausgabe 1 foreach(var c in query) Console.WriteLine("{0}, {1}", c.LastName, c.FirstName); // Durchsuche alle Properties der Contact-Objekte // auf Übereinstimmung mit dem regulären Ausdruck // Fluent-Syntax var query2 = typeof(Contact).GetProperties() .SelectMany( p => contacts .Where(c => { var f = (string)p.GetValue(c, new object[]{}); return f != null && Regex.IsMatch(f, pattern, RegexOptions.Compiled); })); // Ausgabe 2 foreach(var c in query2) Console.WriteLine("{0}, {1}", c.LastName, c.FirstName); }


    P.S. Im obigen Beispiel bin ich von deiner Contact-Klasse ausgegangen. Hat die Klasse auch Nicht-String-Properties, müssen diese ausgefiltert werden:

    var query = from p in typeof(Contact).GetProperties()
                where p.PropertyType == typeof(string)
                [...]
    Gruß

    Marcel


    Dienstag, 25. Juni 2013 16:36
    Moderator

Alle Antworten

  • Hallo Christian,

    hier kommt ein Beispiel, in dem alle Properties mittels Reflection ermittelt werden und auf Übereinstimmung mit einem regulären Ausdruck überprüft werden. Im Code findest Du sowohl eine Query-Comprehension- als auch eine Fluent-Variante (de gustibus non est disputandum):

    // Eine Liste von Contact-Objekten
    List<Contact> contacts = new List<Contact> { 
     new Contact { FirstName="Karl", LastName="Baumann" },
     new Contact { FirstName="Tim", LastName="Bauers" },
     new Contact { FirstName="Anna", LastName="Bau" },
     new Contact { FirstName="Baudy", LastName="Smith" },
     new Contact()
    };
    
    // Regulärer Ausdruck: Suche Werte, in denen die
    // Zeichenfolge "au" von einem der in Klammern angegebenen
    // Buchstaben gefolgt wird
    var pattern = "au[m,e,d]";
    
    // Durchsuche alle Properties der Contact-Objekte 
    // auf Übereinstimmung mit dem regulären Ausdruck
    // (Query Comprehension-Syntax)
    var query = from p in typeof(Contact).GetProperties()
                from c in contacts
                let f = (string)p.GetValue(c, new object[]{})
    	    where f != null && 
    Regex.IsMatch(f, pattern, RegexOptions.Compiled) select c; // Ausgabe 1 foreach(var c in query) Console.WriteLine("{0}, {1}", c.LastName, c.FirstName); // Durchsuche alle Properties der Contact-Objekte // auf Übereinstimmung mit dem regulären Ausdruck // Fluent-Syntax var query2 = typeof(Contact).GetProperties() .SelectMany( p => contacts .Where(c => { var f = (string)p.GetValue(c, new object[]{}); return f != null && Regex.IsMatch(f, pattern, RegexOptions.Compiled); })); // Ausgabe 2 foreach(var c in query2) Console.WriteLine("{0}, {1}", c.LastName, c.FirstName); }


    P.S. Im obigen Beispiel bin ich von deiner Contact-Klasse ausgegangen. Hat die Klasse auch Nicht-String-Properties, müssen diese ausgefiltert werden:

    var query = from p in typeof(Contact).GetProperties()
                where p.PropertyType == typeof(string)
                [...]
    Gruß

    Marcel


    Dienstag, 25. Juni 2013 16:36
    Moderator
  • Die Idee mit der Erweiterungsmethode finde ich übrigens nichteinmal so schlecht.
    Du könntest damit eleganten Code wie diesen schreiben:

    var query3 = contacts.Where(cont => cont.PropertiesMatch(pattern));

    Und wenn Du die Erweiterungsmethod generisch machst, kannst Du sie mit allen Typen verwenden:

    public static class MyExtensions
    {
        public static IEnumerable<PropertyInfo> cachedPropertyInfos;
    
        public static bool PropertiesMatch<T>(this T entity, string regExPattern) 
    where T : class { if (cachedPropertyInfos == null) cachedPropertyInfos = typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)); var allPropertiesValues = cachedPropertyInfos .Select(p => (string)p.GetValue(entity, new object[] { })); return Regex.IsMatch(String.Join(" ", allPropertiesValues.ToArray()), regExPattern, RegexOptions.Compiled); } }

    Ein Tick raffinierter geht's mit dem PredicateBuilder von Albahari, der es dir erlaubt zusätzliche And()- und Or()-Bedingungen fluent zu verknüpfen. Sieh dir mal seine Seite bei Gelegenheit an.

    Gruß
    Marcel

    Mittwoch, 26. Juni 2013 10:12
    Moderator
  • Hi Marcel,

    vielen Dank für Deine Hilfe!

    Viele Grüße,
    Christian

    Mittwoch, 26. Juni 2013 10:58