LookupImportPlus für XrmToolBox: Dataverse-Lookups importieren, ohne zu raten
LookupImportPlus für XrmToolBox: Dataverse-Lookups importieren, ohne zu raten
Du kennst das Szenario: Du exportierst eine Liste aus Dataverse, jemand trägt in einer Spalte einen Firmennamen ein, du importierst zurück — und die Hälfte deiner Kontakte hängt am falschen Account, weil zwei Firmen zufällig „Contoso GmbH” heißen. Der Standard-Import hat still den ersten Treffer genommen. Keine Warnung, kein Fehler. Auffallen tut es Wochen später.
LookupImportPlus ist ein XrmToolBox-Plugin (WinForms, .NET Framework 4.8), das genau das verhindert. Seine Kernregel: Lookups werden deterministisch aufgelöst oder an einen Menschen eskaliert – nie geraten. Dieser Artikel zeigt dir zwei Dinge konkret: wie du eine Konfiguration Schritt für Schritt anlegst, und wie die verschiedenen Auflösungsverfahren (GUID → Business Key → Suchfeld) im Detail zusammenspielen.
Das Plugin ist die Portierung einer Power-Apps-Code-App auf das Dataverse-SDK (statt Web API) — dieselben Menüs und Workflows, nur nativ in XrmToolBox. Stand dieses Artikels: Version 0.1.13.
In XrmToolBox nach „LookupImportPlus” suchen und öffnen. Die Verbindung kommt vom Host — kein separater Login.
Warum der Standard-Import bei Lookups scheitert
Ein Lookup in Dataverse ist keine Textspalte, sondern ein Verweis auf einen Datensatz einer anderen Tabelle — technisch über dessen GUID plus die Zieltabelle. Das Feld „Firma” eines Kontakts (parentcustomerid) zeigt also nicht auf den Namen „Contoso GmbH”, sondern auf einen konkreten Account-Datensatz.
Der Standard-Excel-Import kennt nur den Namen. Findet er mehrere Treffer, nimmt er stillschweigend den ersten. LookupImportPlus dreht das um: entweder eindeutig auflösen, oder anhalten und dich fragen.
Das Prinzip: Konfiguration zuerst, dann der Round-Trip
Du startest nicht bei einer Excel-Datei, sondern bei einer Job-Konfiguration: einer gespeicherten, versionierten Beschreibung, wie eine Tabelle exportiert und wieder importiert wird — inklusive der genauen Auflösungslogik je Lookup-Spalte. Danach ist jeder Lauf derselbe Round-Trip:
Export → in Excel bearbeiten → Import (Hochladen → Dry Run → Konflikte lösen → Commit).
Jeder Importlauf friert einen Snapshot der verwendeten Konfiguration ein — spätere Änderungen deuten einen alten Lauf nie nachträglich um.
Konfiguration anlegen — Screen für Screen
Links die Navigation: Job-Konfigurationen · Importläufe · Konflikte · Importhistorie.
Start: Job-Konfigurationen
Die Übersicht zeigt jede gespeicherte Konfiguration als Karte (Zieltabelle, Operation, Spalten-/Lookup-Zahl, Version, Entwurfsstatus). Pro Karte: Export ▾ (Leeres Template / Daten exportieren), Bearbeiten, Import starten, Löschen. Oben Neue Konfiguration und Excel importieren (eine Datei ohne vorgewählte Config — die Zuordnung kommt aus dem eingebetteten Manifest).
Jede Karte ist eine wiederverwendbare Import-/Export-Definition. Export ist erst möglich, wenn mindestens eine Spalte gewählt ist.
Klick Neue Konfiguration — der Editor öffnet einen Assistenten mit vier Tabs. Die Tabs 2–4 sind gesperrt, bis du eine Zielentität gewählt hast.
1 · Entität & Quelle
Wähle die Zielentität (die Tabelle, in die importiert wird) — die Metadaten werden geladen. Als Exportquelle wählst du „Entität (alle Datensätze)” oder eine gespeicherte Ansicht (savedquery). Bei einer Ansicht gilt: die Ansicht filtert die Zeilen, die Konfiguration bestimmt die Spalten — die Config-Attribute werden ins View-FetchXML injiziert.
Schritt 1 schaltet die übrigen Tabs frei. Entity Set und Primär-ID werden nach dem Laden angezeigt.
2 · Allgemein
Hier setzt du Name, Beschreibung, die Operation (create / update / createOrUpdate) und den Standard-Schreibmodus (strict / partial).
strict schreibt nichts, bis alle Zeilen sauber sind; partial schreibt die sauberen Zeilen sofort.
3 · Spalten
Die Attributliste der Zieltabelle lässt sich filtern (Suche · Nur ausgewählte · Nur Lookups · Nur Pflichtfelder · Nur beschreibbare). Kreuze die Spalten an und setze je Spalte die Verwendung: Import & Export, Nur Export oder Nur Import. Buttons: Daten-Vorschau, Leeres Template, Daten exportieren (aus Quelle).
Über der Liste steht der wichtige Hinweis: Der Datensatz-Schlüssel lip__recordid wird bei Update/Upsert automatisch angehängt und ist bewusst nicht auswählbar.
Bei Tabellen mit 200+ Systemspalten sparen die Filter viel Sucherei. Ohne ausgewählte Spalte gibt es keinen Export.
Die Daten-Vorschau zeigt echte Datensätze — umschaltbar zwischen CRM-Spalten (rohe Dataverse-Felder) und Schema-Spalten (mit generierten), also dem Excel-Layout, das der Export erzeugt. Dort siehst du auch die technischen Lookup-Spalten, um die es gleich geht.
4 · Lookups — hier passiert die eigentliche Arbeit
Jede ausgewählte Lookup-Spalte bekommt eine eigene Karte. Oben erklärt der Assistent die Reihenfolge selbst: „Wie jeder Lookup aufgelöst wird — von oben nach unten, pro Zeile; der nächste Schritt greift nur, wenn der vorige keinen eindeutigen Treffer hatte.”
Pro Karte konfigurierst du:
- Sichtbare Excel-Spalte — die menschenlesbare Spalte (z. B. „Firma”).
- GUID-Spalte — die technische Spalte mit der Ziel-GUID (z. B. „Firma Id”).
- Business-Key-Spalte (optional) — ein eindeutiger Alternativwert (z. B. Kontonummer).
- Konfliktstrategie — Escalate (in die Konfliktliste), SkipRow oder FailRow.
- Zieltabelle(n), in denen gesucht wird — bei polymorphen Lookups (z. B.
customerid= account oder contact) kreuzt du mehrere an. - Pro Ziel: Suchfeld (auf dem Ziel), Business-Key-Attribut (optional) und Suchbedingungen — je Bedingung ein Attribut, ein Operator und eine Wertquelle: Fester Wert, Excel-Spalte (derselben Zeile) oder Relatives Datum (Tage).
Das wichtigste Bild: hier legst du fest, wie sicher aufgelöst wird. Suchfeld und Business-Key-Attribut sind Auswahllisten echter Ziel-Attribute — kein Freitext. Für einen polymorphen Lookup hakst du zusätzlich weitere Zieltabellen an und konfigurierst sie pro Ziel.
Zwei Details aus der aktuellen Version, die dir Ärger ersparen: Suchfeld und Business-Key-Attribut sind DropDownLists echter Attribute (nur gültige Spalten sind speicherbar), und unvollständige Bedingungen werden nicht gespeichert — halb ausgefüllte Zeilen bleiben amber markiert, landen aber nicht in der Konfiguration.
Kopfleiste des Editors: Abbrechen · Speichern · Import starten.
Die verschiedenen Auflösungsverfahren
Das ist der Kern des Tools. Die Reihenfolge ist fest, der erste Treffer gewinnt, und es ist eine Kaskade mit Fallthrough — pro Excel-Zeile datengesteuert:
1) GUID-Spalte → 2) Business Key → 3) Suchfeld (+ Bedingungen)
1 · GUID-Spalte. Steht in der Zelle eine gültige GUID, wird der Datensatz direkt per Id geladen und der Zieltyp geprüft → exakter Treffer, fertig, kein Konflikt möglich. Leer/ungültig/nicht gefunden → weiter zu Schritt 2. Bei polymorphen Lookups fixiert die Typ-Spalte das Ziel (z. B. account vs. contact).
2 · Business Key. Hat die BK-Spalte einen Wert, sucht das Tool das Ziel mit «BK-Attribut = Zellwert». Genau 1 Treffer → fertig. 0 → weiter zu Schritt 3. Mehrere → Konflikt (es wird nicht weitergesucht, sondern eskaliert).
3 · Suchfeld (+ Bedingungen). Der sichtbare Excel-Wert wird gegen das Suchfeld des Ziels geprüft, plus optionale Suchbedingungen. 1 → fertig · 0 → Nicht gefunden · mehrere → Konflikt.
Zwei Punkte, die in der Praxis am häufigsten für Missverständnisse sorgen:
„Alle Zellen leer” ist nicht dasselbe wie „nicht gefunden”. Sind GUID-, Business-Key- und Sicht-Zelle alle leer, bleibt der Lookup unbesetzt (Status Empty, nicht-blockierend) — die Zeile ist trotzdem schreibbar. Ein vorhandener, aber nicht auffindbarer Wert wird dagegen zu Nicht gefunden (blockierend). Das ist eine bewusste Abweichung von der Vorlage und spart dir sinnlose Blocker.
Mehrdeutigkeit wird nie geraten. Mehrere Treffer laufen immer in die Konfliktstrategie:
escalate(in den Konflikt-Screen),skip(Zeile überspringen) oderfail(Zeile schlägt fehl).
Die technischen Spalten je Lookup
Zu einer sichtbaren Lookup-Spalte „Firma” gehören im Export drei technische Spalten:
| Spalte | Zweck | Stufe |
|---|---|---|
| Firma Id | die Ziel-GUID | 1 (gewinnt sofort) |
| Firma Type | Zieltyp bei polymorphen Lookups (account/contact) | fixiert Schritt 1–3 |
| Firma Number | Business Key (z. B. Kontonummer) | 2 |
Der sicherste Weg bei bekannt mehrdeutigen Namen: echte Daten exportieren, die Spalte „Firma Id” mit der korrekten GUID füllen, reimportieren. Die GUID gewinnt — kein Konfliktscreen nötig.
Der Importlauf
XLSX hochladen → zuerst die Konfigurationsprüfung (Schema-Drift: echte Fehler blockieren, Warnungen werden angezeigt) → dann der Dry Run, der jede Zeile klassifiziert. Die Statuskacheln zeigen Bereit / Konflikte / Fehler / Zeilen gesamt. Über den Schreibmodus (Strict/Partial) entscheidest du, was committet wird; bei offenen Konflikten führt Konflikte öffnen → weiter. Commit schreibt via ExecuteMultipleRequest — Strict blockiert, bis alles gelöst ist, Partial schreibt die sauberen Zeilen sofort. Jede Zeile bekommt einen klaren Status; der Fortschrittsbalken ist determiniert, weil die Zeilenzahl vorab feststeht.
Statuswerte
| Status | Bedeutung |
|---|---|
| Bereit / Aufgelöst | schreibbar |
| Mehrdeutig | mehrere Kandidaten → Entscheidung nötig |
| Nicht gefunden | Wert angegeben, aber kein Treffer |
| Pflichtfeld fehlt | Validierung blockiert |
| Falscher Zieltyp | Typ-Spalte passt nicht zum erlaubten Ziel |
| Übersprungen | bewusst ausgelassen |
| Geschrieben / Schreiben fehlgeschlagen | Commit-Ergebnis |
Erinnerung: Eine leere Lookup-Spalte blockiert nicht — der Lookup bleibt ungesetzt, die Zeile ist trotzdem schreibbar.
Konflikte lösen
Der Konflikt-Screen gruppiert nach Quellwert: Zielfeld, betroffene Zeilen, Kandidatenzahl, Status. Auflösen → (bei ≥ 1 Kandidat) bzw. Bearbeiten → (0 Treffer). Eine Entscheidung kann für die ganze Gruppe gelten — „41 × Contoso GmbH” ist eine Entscheidung, nicht 41. Nichts wird automatisch geraten.
Beim Auflösen siehst du die zugrunde liegende Abfrage (inklusive aufgelöstem Zeitanker), die Kandidatenliste mit Deep-Link Öffnen ↗, und die Checkbox „Entscheidung auf alle n Zeilen anwenden”. Auswahl übernehmen → schreibt die Entscheidung zurück und protokolliert sie — Regel, Kandidaten, gewählte GUID, Benutzer, Zeitpunkt (Attribut lip_resolutiondecision). Überspringen markiert die Zeile(n) bewusst als übersprungen.
Importhistorie
Jeder Lauf steht hier mit eingefrorenem Konfig-Snapshot und Zählern: Gestartet, Konfiguration + Version, Modus, Zeilen, Geschrieben, Konflikte, Status — nachvollziehbar bis zur einzelnen Lookup-Entscheidung.
Auditierbar bis auf die Zeile. Der Snapshot macht jeden alten Lauf reproduzierbar.
Berechtigungen: die häufigste Stolperfalle
Der verbundene Benutzer bzw. Anwendungsbenutzer braucht ausreichende Leserechte auf Organisationsebene für Ziel- und Referenztabellen. Die Rolle „System Customizer” allein reicht nicht — sie darf Tabellen wie account/contact nur auf Benutzer-Ebene lesen (nur eigene Datensätze). Folge: Abfragen liefern 0 Datensätze, obwohl im Web-Client Daten sichtbar sind. Abhilfe: dem App-Benutzer eine Rolle mit Organisations-Lesetiefe geben (oder System Administrator) — oder interaktiv als voll berechtigter Benutzer verbinden.
Unter der Haube
LookupImportPlus ist ein WinForms-Plugin auf .NET Framework 4.8 — die Basisklasse ist ein XrmToolBox-PluginControlBase, die Verbindung kommt vom Host (IOrganizationService). Gegenüber der Power-Apps-Vorlage ist der Transport getauscht: statt Web API und @odata.bind läuft alles über das Dataverse-SDK — Metadaten via RetrieveEntity/RetrieveAllEntities (inkl. polymorpher Lookup-Targets), Suche über QueryExpression, Lookup-Bindung über EntityReference, und der Commit als ExecuteMultipleRequest (Batch, ContinueOnError). Konfigurationen und Historie liegen als JSON im XrmToolBox-Settings-Ordner. Vor jedem Lauf prüft eine Schema-Drift-Preflight die Konfiguration gegen die aktuellen Metadaten.
Installieren & loslegen
- Tool Store: In XrmToolBox nach „LookupImportPlus” suchen und installieren (der Store scannt nuget.org, Tag „XrmToolBox Plugin”).
- Plugin-Repo (maßgeblich): github.com/brunsforge/XMRToolbox_LookupImportPlus
- NuGet: nuget.org/packages/LookupImportPlus
- Original-Code-App (Portierungsvorlage): github.com/brunsforge/LookupImportPlus
- XrmToolBox: xrmtoolbox.com
Der Ablauf in Kurzform: Konfiguration anlegen → Leeres Template oder Daten exportieren → in Excel bearbeiten (GUID gewinnt sofort; sonst Business Key; sonst Namenssuche + Bedingungen) → Import starten, Dry Run prüfen → Konflikte lösen → Commit. Der Lauf landet mit Snapshot in der Historie — und du hast einen Import, der nie geraten hat.