Laden...

SortedList KeyNotFoundException, obwohl der Key sicher enthalten ist

Erstellt von tequila slammer vor 13 Jahren Letzter Beitrag vor 13 Jahren 5.054 Views
T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren
SortedList KeyNotFoundException, obwohl der Key sicher enthalten ist

Hallo Forum,

ich benötige mal wieder eure Hilfe. Ich erstelle eine SortedList<Kunde, Konten>.
Konten ist ein generische Collection mit Konto-Objekten.


foreach (KontoTyp accountType in sortedClientList.Keys)
{
    HtmlGenericControl clientDiv = new HtmlGenericControl("div") { ID = client.Id.ToString() };

    clientDiv.Controls.Add(new Label { Text = accountType.Name });

    //Erstellen des Konten-Div Containers
    HtmlGenericControl clientsContainerDiv = new HtmlGenericControl("div");

    //Hinzufügen der Accounts
    foreach (Konto Account in sortedClientList[accountType]) //hier meint er, das Kontotyp-Objekt ist nich in der Liste. Die Überwachung im Debugger sagt da aber was anders.
    {
        LinkButton lbtAccount = new LinkButton { Text =Account.Name, ID = Account.Id.ToString(), CssClass = "AddAccount" };
        lbtAccount.Attributes.Add("rel", client.Id.ToString());
        lbtAccount.Click += new EventHandler(lbt_AddClick);

        clientsContainerDiv.Controls.Add(lbtAccount);
    }

    clientDiv.Controls.Add(clientsContainerDiv);

    this.PlaceHolderClients.Controls.Add(clientDiv);
}

Als Fehler erscheint immer: Der angegebene Schlüssel war nicht im Wörterbuch angegeben. Aber dann hätte ich ihn in der ersten Foreach-Schleife auch nicht abfragen können. Was läuft hier falsch?

Edit: Kunde durch KontoTyp ersetzt.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer,

vermutlich hast du das client-Objekt nach dem Hinzufügen geändert. In einer SortedList<> darf man das nicht, jedenfalls dann nicht, wenn die Änderung eine Auswirkungen auf die Reihenfolge hat. Der Eintrag bleibt nämlich trotz der Änderung an der alten Position stehen, obwohl er an eigentlich eine andere Position gehört. Etwas vereinfacht gesagt, wird er dann an der anderen Position gesucht, aber da steht er ja nicht.

herbivore

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Guten Morgen herbivore,

ich sehe, gerade das mein Beispiel schlecht gewählt wurde. Besser ist, wenn man Kunde durch Kontotyp ersetzt, da ich eigentlich eine Gruppierung dieser Erstellen wollte.

Trotzdem hat deine Antwort bestand. Ich kann mir nur vorstellen, dass die Liste beim Erzeugen durch meinen Comparer verändert wird.


public static System.Collections.Generic.SortedList<KontoTyp, Konten> GetAccountsByAccountType(Kunde currentClient)
{
    System.Collections.Generic.SortedList<KontoTyp, Konten> accountList = new SortedList<KontoTyp, Konten>(new AccountTypeByRankingNumberComparer());

    foreach (Kontotyp accountType in currentClient.Konten)
    {
        if(!accountList.ContainsKey(accountType))
        {
            accountList.Add(accountType, getAccountTypeAccounts(currentClient.Konten, accountType.Id));
        }
    }

    return accountList;
}

Einfach weglassen kann ich ihn aber auch nicht, da ich sonst nicht prüfen könnte, ob der Kontotyp schon in der Liste ist ->InvalidOperationExeption "Fehler beim Vergleichen von zwei Elementen im Array".

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer,

Trotzdem hat deine Antwort bestand.

eben. 😃 Prüfe doch mal, ob Reihenfolge der Element zum Zeitpunkt des Zugriffs immer noch mit der Reihenfolge übereinstimmt, die der Comparer vorgibt.

herbivore

916 Beiträge seit 2008
vor 13 Jahren

Oder versuch mal anstatt eines SortedDictionaries ein normales zu nehmen, wenn dann der Fehler nicht mehr Auftritt ist das was herbivore gesagt hat wohl der Fall!

Again what learned...

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Also ich habe es nun mal mit einem normalen Dictionary versucht und dort das IEqualityComparer Interface implementiert, da ein Comparer nicht verwendet werden kann. Ich bekomme nun zwar keine Fehler mehr aber auch nicht das gewünschte Ergebnis.

Die ursprüngliche SortedList wird schon beim Erstellen durch den Comparer verändert, die wiederum hat das gewünschte Ergebnis. Und nun, irgendwie stehe ich an der Wand?

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Ich habe noch eine Frage. Wie kann ich die Sortierung bzw. nach welcher Property im Key-Objekt sortiert werden soll bei einer SortedList beeinflussen? Mein AccountTypeRankingNumberComparer verändert ja nunmal die Position des Keys, was zu meinem Problem führt. Wenn ich nun aber keinen Comparer angeben, wo nach sortiert er mir dann? Wahrscheinlich nach dem ganzen Key, oder?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer,

Wenn ich nun aber keinen Comparer angeben, wo nach sortiert er mir dann?

bitte schlage sowas immer selber in der :rtfm: Doku nach:

Die SortedList benötigt für das Sortieren und Vergleichen eine Vergleichsimplementierung. Der Standardvergleich Comparer.Default überprüft, ob der Schlüsseltyp TKey System.IComparable implementiert, und verwendet diese Implementierung, sofern verfügbar. Andernfalls überprüft Comparer.Default, ob der Schlüsseltyp TKey System.IComparable implementiert. Wenn der Schlüsseltyp TKey keine der Schnittstellen implementiert, können Sie in einer Konstruktorüberladung eine System.Collections.Generic.IComparer-Implementierung angeben, die einen comparer-Parameter akzeptiert.

herbivore

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Ich konnte bisher noch nicht in Erfahrung bringen, ob die IComparable Implementierung verloren geht, wenn ich das Objekt per WebService bekomme. Das Basisobjekt hat die Schnittstelle IComparable jedenfalls. Da mein Fehler durch die Konstruktorüberladung bzw. den Comparer auftritt denke ich nun darüber nach mir eine Wrapper-Klasse zu bauen und dort IComparable zu implementieren. Ist das zu kompliziert Gedacht?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer,

ich wüsste nicht, was das für eine Verbesserung bringen sollte, wenn du einen Wrapper schreibst. Der Weg, auf dem die SortedList den Comparer bekommt, spielt doch keine Rolle, sondern nur dessen Implementierung. Du kannst also den Comparer weiterhin direkt übergeben, wie du das oben schon gemacht hast.

Mögliche Ursachen für den Fehler habe ich schon genannt. Die musst du finden und beheben. Die die Art und Weise, wie du der SortedList klar machst, welchen Comparer sie verwenden soll, spielt keine Rolle.

herbivore

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Hallo herbivore

Ich habe in den letzten Tagen viel mit dem Comparer ausprobiert und komme trotzdem kein Schritt weiter. Das ist der alte und neue Stand:


public class AccountTypeByRankingNumberComparer : IComparer<AccountType>
    {
        #region IComparer<AccountType> Member

        public int Compare(AccountType x, AccountType y)
        {
            if (x.RankingNumber > y.RankingNumber)
            {
                return 1;
            }
            else
            {
                return -1;
            }
        }

        #endregion
    }

Irgendwie verstehe ich SortedList nicht. Wenn ich einen Comparer mitgebe und auch vergleiche, so muss das SortedList-Objekt doch davon ausgehen, dass sich im internen Index auch was positionsmäßig verändern kann. Oder nicht?

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer,

so muss das SortedList-Objekt doch davon ausgehen, dass sich im internen Index auch was positionsmäßig verändern kann.

nein natürlich nicht. Die SortedList bekommt die Änderungen an den Keys ja nicht (direkt) mit. Wie soll sie darauf reagieren? Wenn sie nun alternativ vor jedem Zugriff erstmal neu sortieren würde, wäre ja der ganze Witz verloren. Ich verstehe nicht, warum du immer noch an dieser Stelle hängt. Ich habe schon oben geschrieben:

vermutlich hast du das client-Objekt nach dem Hinzufügen geändert. In einer SortedList<> darf man das nicht, jedenfalls dann nicht, wenn die Änderung eine Auswirkungen auf die Reihenfolge hat.

Oder mit den Worten des MSDN:

Schlüsselobjekte müssen unveränderlich sein, solange sie als Schlüssel in SortedList verwendet werden

herbivore

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Ich bekomme es einfach nicht hin.

Gehe ich den Code nochmal durch kann ich es einfach nicht verstehen.

Die statische Methode soll mir alle Accounts sortiert nach dem Ranking zurückgeben. Quasi gruppieren. Dabei prüfe ich, ob ich diesen Typ schon als Key haben. Ist dem nicht so, wird mit der statischen Methode getAccountTypeAccounts eine AccountCollection zurückgegeben.


public static System.Collections.Generic.SortedList<KontoTyp, Konten> GetAccountsByAccountType(Kunde currentClient)
{
    System.Collections.Generic.SortedList<KontoTyp, Konten> accountList = new SortedList<KontoTyp, Konten>(new AccountTypeByRankingNumberComparer());

    foreach (Kontotyp accountType in currentClient.Konten)
    {
        if(!accountList.ContainsKey(accountType))
        {
//hier wird die SortedList verändert, wenn der Kontotyp noch nicht enthalten ist. Anschließend wird mit getAccountTypeAccounts eine Collection mit allen Konten die diesen Typ habe zurückgegeben.
            accountList.Add(accountType, getAccountTypeAccounts(currentClient.Konten, accountType.Id));
        }
    }

    return accountList;
} 

Die statische Methode getAccountTypeAccounts sieht so aus. Nichts, was den Key der SortedList verändert.


private static AccountCollection getAccountTypeAccounts(accounts, Guid accountTypeId)
{
    AccountCollection accCol = new AccountCollection();

    foreach (Konto item in accounts)
    {
        if (item.KontoTypeId == accountTypeId)
        {
            accCol.Add(item);
        }
    }

    return accCol;
}

Debugge ich nun alles ist das Ergebnis wie folgt:
Durchlauf 1 - Ordnungsnummer: 2
Durchlauf 2 - Odnungsnummer: 1
Durchlauf 2 - Odnungsnummer: 2

Demnach ist der Comparer das Problem. Der wiederrum ist aber eine Standardimplementierung.

49.485 Beiträge seit 2005
vor 13 Jahren

Hallo tequila slammer.

dein Comparer berücksichtigt nicht, dass beide Werte auch gleich sein können. In dem Fall muss der Comparer 0 liefern. Selbst wenn alle Objekte unterschiedlich sind und damit die RankingNumber eindeutig ist, musst du diesen Fall implementieren, weil es ja möglich ist, dem Comparer in beiden Parametern dasselbe Objekt zu übergeben.

BTW: Wenn du gerade dabei bist, solltest du auch den Fall berücksichtigen, dass für einen oder beide Parameter null übergeben werden könnte.

herbivore

T
tequila slammer Themenstarter:in
253 Beiträge seit 2006
vor 13 Jahren

Hallo herbivore,

ein ganz großes Dankeschön. Es funktioniert nun. Diese Hilfe hier ist Gold wert. Wo kann man spenden?