Laden...

Linq abfrage bei unbekannter anzahl von Suchparametern

Erstellt von Sythus vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.549 Views
S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren
Linq abfrage bei unbekannter anzahl von Suchparametern

verwendetes Datenbanksystem: keines - nur eine XML Datei

Hallo 😃

Mittels eines Formulars soll es möglich sein eine XML Datei zu durchsuchen und die gefundenen Datensätze anzeigen zulassen.

Dabei bin ich auf ein Problem gestoßen.

Die Anzahl der Suchparameter ist unbekannt.
Es gibt z.B. drei Felder: Vorname, Nachname, Emailadresse
(In der realität sind es mehr Felder)

Gibt man nur den Vornamen an, sollte natürlich nur der Vorname abgefragt.
Gibt man Vorname und Emailadresse an, natürlich nur Vorname und Emailadresse.

Da ich aber nicht weiß, wieviele Suchparameter genutzt werden weiß ich nicht genau wie ich die Abfragen realisieren soll.

die Datensätze in der XML Datei sehen folgendermaßen aus:

<dataset firstName="Olga" lastName="Wukawitsch" email="olga.wukawitsch@1234.de" />

Für Ideen und Hilfestellungen bin ich dankbar,

viele Grüße,
Sythus

1.564 Beiträge seit 2007
vor 14 Jahren

Hallo Sythus

Du musst halt die XPath-Abfrage abhängig von der User-Eingabe zusammenbauen. Was ist denn genau das Problem?

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Hallo Florian,

ich habe mehrere Eingabefelder

Vorname, Nachname und Email

wäre nun für Vorname : Manfred und für Nachname : Müller eingegeben würde die perfekte Abfrage folgendermaßen aussehen



                 node = (from Dataset in Stammdaten.Elements()
                        where Dataset.Attribute("Vorname").Value == "Manfred"+ Dataset.Attribute("Nachname").Value == "Müller"
                        select Dataset);

Wenn aber nur das Feld für Vorname belegt wäre mit "Manfred" würde die Perfekte Abfrage wie folgt aussehen



                 node = (from Dataset in Stammdaten.Elements()
                        where Dataset.Attribute("Vorname").Value == "Manfred"
                        select Dataset);

(Die Strings wie Vorname und Manfred wären normalerweise in Variablen enthalten)

Natürlich könnte man das bei 3 Feldern problemlos mit if Abfragen prüfen
if(vorname.text != "" && nachname.text != "")
dann folgende Abfrage

oder
if(vorname.text != "" && nachname.text == "")
dann folgende Abfrage

Aber bei sehr vielen Eingabefeldern wären das sehr viele IF Abfragen - und das muss doch sicher auch einfacher gehen oder?

Ich hoffe du verstehst mein Problem - oder seh ich den Wald vor lauter Bäumen nicht mehr?

Danke und schöne Grüße

Sythus

1.564 Beiträge seit 2007
vor 14 Jahren

Wie schon gesagt, ich hätte einfach XPath verwendet, aber du kannst das auch über string.IsNullOrEmpty abbilden:

string firstName = txtFName.Text;
string lastName = txtLName.Text;

node = (from Dataset in Stammdaten.Elements()
      where (string.IsNullOrEmpty(firstName) || Dataset.Attribute("Vorname").Value == firstName)
            && (string.IsNullOrEmpty(lastName) || Dataset.Attribute("Nachname").Value == lastName)
      select Dataset);

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Vielen dank, darauf wär ich warscheinlich nich gekommen - aber du hast recht das wird bei vielen datenfeldern auch ne monströse abfrage...

wie sähe denn das zusammenbauen mit xpath aus?
Danke und lg

6.911 Beiträge seit 2009
vor 14 Jahren

Hallo,

die Abfrage bzw. die Where-Klauseln können mit dem PredicateBuilder zusammengesetzt werden. Kurzbeispiel gibt es hier.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

1.564 Beiträge seit 2007
vor 14 Jahren

Beispiel für XPath (benötigt "using System.Xml.XPath")

         // DOM from anywere
         XDocument dom = new XDocument();

         StringBuilder xpath = new StringBuilder();

         xpath.Append("dataset [");

         // first name
         if (!string.IsNullOrEmpty(textBox1.Text))
            xpath.AppendFormat("@Vorname='{0}' or ", textBox1.Text);
         // last name
         if (!string.IsNullOrEmpty(textBox2.Text))
            xpath.AppendFormat("@Nachname='{0}' or ", textBox1.Text);

         xpath.Append("1=1]");

         var result = dom.XPathSelectElements(xpath.ToString());

... allerdings finde ich den Ansatz von Gü auch recht cool.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Ja beides echt super Möglichkeiten, genau nach sowas habe ich gesucht 😃

Vielen dank euch 2n

Gruß Sythus

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Gute Morgen,

Die XPath Abfrage funktioniert wunderbar. Ich musste lediglich das or in ein and umwandeln, da die abfrage sonst immer "alle" Datensätze findet.

Eine letzte Frage bleibt allerdings noch - ist es auch möglich zu überprüfen ob der Text den man sucht in dem Attribute enthalten ist?

lastName="HansMüller"

suche nach hans (am besten unabhängig von der groß und kleinschreibung)
ergibt den Datensatz HansMüller

Danke und beste Grüße

1.564 Beiträge seit 2007
vor 14 Jahren

Hallo Sythus

Geht, dürfte aber ziemlich auf die Performance gehen, wenn du eine große XML Datei hast.

Schau dir mal die XPath Funktionen lower-case() und contains() an. Bei W3CSchools gibt's eine recht hilfreiche Referenz:
XPath, XQuery, and XSLT Function Reference

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Danke schön, ja die XML Datei ist extrem groß, ca. 50 000 Datensätze

hoffe das mit der Performance geht noch so einigermaßen

Gruß
Sythus

PS: Gibt es Alternativen in sachen Performance?

1.564 Beiträge seit 2007
vor 14 Jahren

Du kannst statt eines XML Files auch eine Datenbank wie SQL Server CE (oder natürlich auch was größeres) verwenden.

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Das Problem daran ist nur, das dass Programm bei jedem Start erstmal eine Exportdatei im CSV Format in dieses XML Umwandelt (nur im Speicher) - damit ich die Abfragesprache und damit eine schnellere Suchfunktion nutzen kann.

Jedesmal eine Datenbankdatei zu schreiben (beim Programmstart) ist einiges mehr Aufwand oder?

Ich muss diese Daten aber immer neu schreiben, da der Export (die CSV Datei) jeden Tag aktualisiert wird.

Gruß
Sythus

1.564 Beiträge seit 2007
vor 14 Jahren

Ich hab gerade mal testweise 50,000 Datensätze in eine SQL Server CE Datenbank importiert. Dauerte ca. 6 Sekunden, mit zwei Indizes zusätzlich auf insgesamt auf knapp 10 Sekunden. Reicht das?

Wenn RAM kein Problem ist wäre eine Andere Option die Daten statt in ein XML eher in eine DataTable zu laden, dürfte noch ein ganzes Stück schneller sein und du bekommst komplexe Suchen geschenkt.

Wenn RAM ein Problem ist und 10 Sekunden zu lange sind aber ein SQL Server 2008 (Expresss/Standard/Enterprise) vorhanden ist kannst du die Daten über einen Bulk-Insert in die Datenbank schreiben. Ist ein komplett anderes Konzept und etwas mehr Aufwand, dürfte dann aber inklusive aller Indizes deutlich weniger als 1 Sekunde dauern.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

WoW!!! Danke für die Mühe

Das sind wirklich tolle Möglichkeiten.

10 Sekunden für ein Laden beim starten des Programmes finde ich okay... man darf nur nicht vergessen das bei einer langsameren CPU gerne mal das doppelte oder dreifache der Ladezeit rauskommen kann.

Gruß
Sythus

M
120 Beiträge seit 2009
vor 14 Jahren

Beispiel für XPath (benötigt "using System.Xml.XPath")

// first name  
         if (!string.IsNullOrEmpty(textBox1.Text))  
            xpath.AppendFormat("@Vorname='{0}' or ", textBox1.Text);  
         // last name  
         if (!string.IsNullOrEmpty(textBox2.Text))  
            xpath.AppendFormat("@Nachname='{0}' or ", textBox1.Text);  
  
         xpath.Append("1=1]");  
  
         var result = dom.XPathSelectElements(xpath.ToString());  
  

So darf dann die Usereingabe aber keine ' enthalten, oder?

1.564 Beiträge seit 2007
vor 14 Jahren

So darf dann die Usereingabe aber keine ' enthalten, oder?

Korrekt. War ein kurz hingehacktes Beispiel 😉

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Kleiner Nachtrag

Die XPathAbfrage ist auch bei 50 000 Datensätzen und zusätzen wie "starts-with" und "lower_case" noch recht Perfomant.

Problematisch wird es nur die Daten alle in einer ListView anzuzeigen.

Das Problem ist also nicht die XPathAbfrage sondern das anzeigen der Ergebnisse.

Gibt es da performantere Controls?

Gruß
Sythus

1.564 Beiträge seit 2007
vor 14 Jahren

Beste Lösung
Kein ListView verwenden 😉. 50.000 Datensätze sind für ein ListView definitiv zu viel. Diese Menge an Daten macht eigentlich in gar keiner Client-Anzeige Sinn. Wenn's aber wirklich sein muss würde ich dringend ein DataGridView empfehlen.

Das Einzige das man beim ListView noch machen kann ist BeginEdit()/EndEdit() wird aber auch nicht wirklich den Tiger in den Tank packen 8)

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Danke Florian,

ich zeige nun einfach maximal 500 Suchergebnisse an und die Information das der Sucher seine Suche doch etwas spezifizieren soll, da es mehr als 500 Suchergebnisse gibt.

Damit kommt das Listview Control ganz gut klar - 50 000 Suchergebnisse anzeigen wär eh wenig Sinnvoll.

Gruß
Sythus

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Ich muss leider nochmal ne kleine Frage stellen, hast du zufällig ne Idee wie man die Abfrage ohne beachtung der Groß- und Kleinschreibung regeln könnte?

Habs nun sehr umständlich gemacht - und das is auch sehr performance lastig.
Beim Einlesen in das XML File wandel ich alle Strings zu lower-case Strings um...

Anschließend das gleiche bei der XPath Abfrage mit dem Text des Inputfeldes.

Das ist Irre - gibts da ne bessere Möglichkeit?

1.564 Beiträge seit 2007
vor 14 Jahren

Alles am Anfang alles konvertieren kostet natürlich einmalig sehr viel Performance, hat jedoch den Vorteil, dass die Suchen später schneller sein sollten.

Ansonsten kannst du innerhalb der XPath-Anweisung mit lower-case arbeiten:


         // first name
         if (!string.IsNullOrEmpty(textBox1.Text))
            xpath.AppendFormat("lower-case(@Vorname)='{0}' or ", textBox1.Text);

Wie sich das auf die Geschwindigkeit auswirkt musst du aber testen.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

dann müsste ich die Strings der Inputfelder aber auch ToLowerCase wandeln oder?


if (!string.IsNullOrEmpty(stNumber.Text))
                xpath.AppendFormat("starts-with(lower-case(@stNumber) , '{0}') and ", stNumber.Text);

Geht leider schonmal nicht ->
{"Namespace-Manager oder 'XsltContext' erforderlich. Diese Abfrage hat einen Präfix, eine Variable oder eine benutzerdefinierte Funktion."}

1.564 Beiträge seit 2007
vor 14 Jahren

Ja so ein Misst...

Habe gerade mal versucht und gegoogelt. .NET unterstützt nur XPath 1.0. lower-case ist eine Funktion aus XPath 2.0. Witziger Weise sind die Funktionen für XSLT schon vorhanden...

Dann kannst du wohl nur "translate()" als XPath 1.0 Krücke verwenden:

         string xml = @"<?xml version='1.0' encoding='iso-8859-2'?><root><child attr1='BLA'></child></root>";
         XmlDocument dom = new XmlDocument();

         dom.LoadXml(xml);

         XmlNodeList list = dom.SelectNodes("root/child [@attr1=translate('bla', 'abcdefghijklmnopqrstuvwxyzäöü', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ')]");

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 14 Jahren

Hallo Flo,

na das probier ich gleich mal aus - danke^^ bin mal gespannt inwiefern das die performance beeinflusst - ich meld mich dann wieder

thx!

Nachtrag:

Ich kriege das leider nicht mit starts-with zum laufen, es spuckt zwar keine Fehlermeldung aus, aber finden tut er auch nichts


/Root/Dataset[starts-with(@stNumber=translate('s', 'abcdefghijklmnopqrstuvwxyzäöü', 'ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜ'), 's') and 1=1]

Die Performance nur mit "translate" war aber voll in Ordnung

Gruß
Sythus

1.564 Beiträge seit 2007
vor 14 Jahren

Du transformierst mit "translate()" deinen Text erst von "s" nach "S" und prüfst den Wert dann auf "s" 😉

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.