17. oktober 2007 - 11:20Der er
20 kommentarer og 1 løsning
Søgning i c#
Hej
Jeg har en collectionklasse med en List<Bruger> med brugerdata.
Alle mine brugere bliver indlæst i et listview når jeg starter programmet op.
jeg vil gerne kunne søge i mine brugere ved at indgrænse på fx navn og tlf.
Jeg har lavet nogle tekstfelter og combobokse med forskellige ting jeg gerne vil kunne indgrænse min brugerliste.
Så når jeg fx skriver Mads i feltet "navn" er det kun dem der hedder Mads der bliver vist i mit listview. Og hvis jeg nedenunder indtaster postnr 5200, vil det kun være dem der hedder Mads og bor i postnr 5200 der bliver vist.
Søgningen skal ske løbene imens jeg taster i feltet. Dvs. onchanged eller hvad det nu hedder..
Den hårde og tunge vej er jo at lave en søgemetode som su kører for hvert tastetryk... Hvis du ikke har for mange navne vil dette performe ok... (sortering + binær søgning kan være en lille optimering oven på dette)....
Har du rigtig mange navne har jeg selv lavet et "søge træ"... hvordan man lige skal kombinere navn samt postnummer her kræver lidt tænkeri...
og personlig set ville det heller ikke være nemmere da det skulle igennem mindst 2 lag (Domain og DataAccess)...
alternativt til at lave det optimale Trie eller et RadixTrei.. så kan du jo: public List<Bruger> Search( string userSearch, string zipSearch ) { List<Bruger> partialList = new List<Bruger>(); foreach( Bruger bruger in brugere ) { if( bruger.Name.StartsWith(userSearch) && bruger.ZipCode.ToString().StartsWith(zipSearch ) ) partialList.Add( bruger ); } return partialList; }
(Det er ikke sikkert overstående kompilere, lige noget hurtig skrevet direkte her, men burde vise hvordan)...
Ulæmpen ved overstående er at den skal rende igennem hele listen hver gang brugeren laver et tastetryk, og på mange data kan det godt give en sløv respons tid...
Et Trie eller RadixTrei finde frem til de relevante resultater meget hurtigere... og kan desuden implementeres sådan at det husker ens position i træet... og bare flytter op hvis man fjerne en char, eller ned hvis man tilføjer en char...
Search metoden burde nok laves med List<> FindAll og den jeg tror såmænd ikke at den performer så dårligt - det tager ikke mange milliontedele sekunder at søge en liste med nogle få hundrede entries igennem.
Det første lange stykke tid vil systemet nok kun indeholde omkring 200 brugere, men der vil være god mulighed for at det på et senere tidspunkt vil blive udvidet til 1000+ brugere.
Jeg har nu lavet systemet med en knap, så den ikke bare søger af sig selv, da jeg tror det bliver lidt for tungt.
Systemet skal også kodes op mod databasen vha. SOAP protokollen, når jeg engang får styr på dette, hvilket måske også vil betyde lidt dårligere performance.
Jeg har fået lavet et array af SQL statements så hvis man søger på de forskellige ting bliver der lige tilføjet et AND xx = 'xx' i sætningen ;) Måske en lidt primitiv løsning, men det fungerer faktisk ret godt.
Det andet der med Trie kan jeg ikke rigtigt overskue hvordan jeg skal stille op :S
Det er nu ellers ikke så svært igen... men hvis vi snakker nede i de størelsesordner ville jeg ikke mene det er nødvendigt...
Men du kan jo implementere en Search metode lidt ala den jeg viser på din collectionklasse... så kan man jo sige den er inkapsuleret således at hvis du en dag står med så mange brugere at det begynder at blive tungt, kan det omimplementeres uden det store besvær.
Og så kan du naturligvis som Arne v bruge FindAll, den tager en delegate til en metode som skal returnere hvor vidt et element skal medtages i resultatet af FindAll kaldet...
Det er stadig hurtigere at søge direkte i ram istedet for at skulle ned i en database, men du skal igen op i mange flere brugere før det bliver et problem...
ok den måde du lige bruger find all på ville jeg ikke anbefale til at starte med, benyt istedet en enkelt findall... for dit brance problem kan du bruge den der heder Exists istedet til at finde ud af om listen af brancher indeholder en given branche...
så noget ala:
resultat = kunder.FindAll(delegate(Kunde k) { bool result = false; if (sælger != 0 && k.Sælger1.Brugernr.Equals(sælger)) result = true;
if (sælger2 != 0 && k.Sælger2.Brugernr.Equals(sælger2)) result = true;
if (branche != null && k.Brancher.Exists(delegate(Branche b) { return b.Equals(branche); })) result = true;
if (kunde != null && k.KundeNavn.ToLower().StartsWith(kunde, StringComparison.InvariantCultureIgnoreCase)) result = true;
if (postnr != 0 && k.Postnr.Equals(postnr)) result = true;
Alle kombinationsmuligheder skal være mulige.. Fx hvis man vil søge på sælger1 og postnr, så den finde alle de kunder der har den søgte sælger1 og postnr.
Og hvorfor returnerer den en bool, når det er søgeresultater jeg skal trække?
public List<Kunde> søgKunde(int sælger, int sælger2, string branche, string kunde, int postnr, int cvr) { List<Kunde> resultat = kunder;
resultat = kunder.FindAll(delegate(Kunde k) { bool result = true; if (sælger != 0 || !k.Sælger1.Brugernr.Equals(sælger)) result = false;
if (sælger2 != 0 || !k.Sælger2.Brugernr.Equals(sælger2)) result = false;
if (branche != null || !k.Brancher.Exists(delegate(Branche b) { return b.Equals(branche); })) result = false;
if (kunde != null || !k.KundeNavn.StartsWith(kunde, StringComparison.InvariantCultureIgnoreCase)) result = false;
if (postnr != 0 || !k.Postnr.Equals(postnr)) result = false;
if (cvr != 0 || !k.Cvr.Equals(cvr)) result = false;
return result; });
return resultat; }
Hvis jeg ikke skriver noget i kundenavn går den ned, fordi værdien ikke må være null. Og hvis jeg skriver noget i kundenavn som jeg ved eksistere bliver der alligevel ikke added noget til listen :S
Ja jeg blev forviret over det du gør i den anden... den er nemlig lige lidt underlig måde du har stillet den anden op på, og var derfor ikke helt klar over hvordan du ville udfører den...
Principet er at du i den blok her: { bool result = true; if (sælger != 0 || !k.Sælger1.Brugernr.Equals(sælger)) result = false;
if (sælger2 != 0 || !k.Sælger2.Brugernr.Equals(sælger2)) result = false;
if (branche != null || !k.Brancher.Exists(delegate(Branche b) { return b.Equals(branche); })) result = false;
if (kunde != null || !k.KundeNavn.StartsWith(kunde, StringComparison.InvariantCultureIgnoreCase)) result = false;
if (postnr != 0 || !k.Postnr.Equals(postnr)) result = false;
if (cvr != 0 || !k.Cvr.Equals(cvr)) result = false;
return result; }
Returnere true hvis et kundeobject skal medtages ud fra de kreterier der er... (som jeg har sat det op er det krævet at alle er opfyldt hvis der er noget i dem, for sådan ville jeg have troet det skulle være)...
og du skal nok lige rettet all || til &&... var fordi jeg havde den anden og fik Resharper til at inverte if'sne... så prøv evt med:
Og hvorfor returnerer den en bool, når det er søgeresultater jeg skal trække?
Det er fordi det er en anonym metode der står for at vurdere hvor vidt en kunde skal med eller ej, det er FindAll metoden som opretter en liste med alle funde resultater og returnere den.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.