But today I have the error:
generated sql is not supported for multiple table delete row
What does that mean??
Da die Fehlermeldung sagt, kann die DbCommandBuilder
nur automatisch generieren SQL für einzelne Tabellenabfragen. Dies macht es einfach, Apps zu implementieren, die Basistabellen wie Kunde, Produkt, Mitarbeiter usw. pflegen. Sie können für eine ähnliche Beziehung wie Customer-Order-OrderLines-ServiceItem
verwendet werden, aber Sie müssen anders vorgehen.
Aber bevor Sie das tun wissen, dass es verschiedene Frameworks gibt, um zu helfen, diese Art von Sache für Sie zu implementieren und zu verwalten. Untersuchen Sie ORMs.
Da die DataAdapter
kann nur eine Tabelle zu einem Zeitpunkt aktualisieren, werden Sie mehrere Adapter benötigen. Anstatt einen SQL-JOIN zu verwenden, können Sie die PK-FK-Beziehung definieren. Änderungen an übergeordneten Zeilen werden auf untergeordnete Elemente angewendet.
Für Demozwecke habe ich Tabellen geschickt Namen Parent
, Child
und SubChild
. Eine übergeordnete Zeile kann viele untergeordnete Zeilen enthalten (1: m). Jedes Kind kann viele Subchild-Zeilen (1: m) haben.
// form/class level objects
DataSet dsSample;
OleDbDataAdapter daParent;
OleDbDataAdapter daChild;
OleDbDataAdapter daSubCh;
An anderer Stelle konfigurieren, dass sie:
daParent = new OleDbDataAdapter("SELECT Id, Name, Lorem FROM Parent",
AceConnStr);
dsSample.Tables.Add("Parent");
var cbP = new OleDbCommandBuilder(daParent);
daParent.UpdateCommand = cbP.GetUpdateCommand();
daParent.InsertCommand = cbP.GetInsertCommand();
daParent.FillSchema(dsSample.Tables["Parent"], SchemaType.Source);
daParent.Fill(dsSample.Tables["Parent"]);
// repeat for Child - use care with copy-paste!
daChild = new OleDbDataAdapter("SELECT Id, ParentId, Name, Lorem FROM Child",
AceConnStr);
dsSample.Tables.Add("Child");
var cbS = new OleDbCommandBuilder(daChild);
daChild.UpdateCommand = cbS.GetUpdateCommand();
daChild.InsertCommand = cbS.GetInsertCommand();
daChild.FillSchema(dsSample.Tables["Child"], SchemaType.Source);
daChild.Fill(dsSample.Tables["Child"]);
// do the same for the subchild adapter
// omitted for brevity
// ...
// the PK cols
DataColumn colParent = dsSample.Tables["Parent"].Columns["Id"];
DataColumn colChild = dsSample.Tables["Child"].Columns["Id"];
// set FK constraints, rules
ForeignKeyConstraint fkParentChild = new ForeignKeyConstraint("ParentChild",
colParent,
dsSample.Tables["Child"].Columns["ParentId"]);
fkParentChild.DeleteRule = Rule.Cascade;
fkParentChild.UpdateRule = Rule.Cascade;
fkParentChild.AcceptRejectRule = AcceptRejectRule.Cascade;
dsSample.Tables["Child"].Constraints.Add(fkParentChild);
// set FK constraints, rules for Child-SubChild
ForeignKeyConstraint fkChildSub = new ForeignKeyConstraint("ChildSub",
colChild,
dsSample.Tables["SubChild"].Columns["ChildId"]);
fkChildSub.DeleteRule = Rule.Cascade;
fkChildSub.UpdateRule = Rule.Cascade;
fkChildSub.AcceptRejectRule = AcceptRejectRule.Cascade;
dsSample.Tables["SubChild"].Constraints.Add(fkChildSub);
dsSample.EnforceConstraints = true;
Wenn ein Elternteil Zeile gelöscht wird die zugehörigen untergeordneten Zeilen betroffen sein werden; Gleiches für Child-to-SubChild. Beachten Sie, dass selbst wenn Sie sich nicht für eine Art von ORM entscheiden, viele davon aufgeteilt und von Klassen behandelt werden können, die sich auf den Akteur beziehen. Zum Beispiel eine Room
oder City
Klasse, die dieses Datenbit behandelt.
Testen der Beziehung (en)
Eine Abfrage für die auf "ParentB" im Zusammenhang Reihen:
- Es gibt 3 im Zusammenhang Kind Reihen (3 verschiedene Child.Ids)
- Es gibt 4 SubChild Zeilen im Zusammenhang mit denen ChildRows
- Wenn Sie möchten, zu de Code die Namen,
ChildB1
ist die erste untergeordnete Zeile für ParentB; ChildB-1C
bedeutet, dass das Subchild mit der ersten Childrow verknüpft ist, die sich auf ElternB bezieht. Das ist nicht so klar geworden, wie ich gedacht habe.
Test:
// delete ParentB
var dr = dsSample.Tables["Parent"].Select("name = 'ParentB'")[0];
dr.Delete();
// if constraints work, there should be
// multiple Child and SubChild rows affected
var childChanges = dsSample.Tables["Child"].GetChanges(DataRowState.Deleted);
var subChanges = dsSample.Tables["SubChild"].GetChanges(DataRowState.Deleted);
Console.WriteLine("Child changes: {0}, subCh changes: {1}",
childChanges.Rows.Count,
subChanges.Rows.Count);
Ergebnis:
Child changes: 3, subCh changes: 4
Perfect! Löschen einer einzelnen übergeordneten Zeile kaskadiert, um auch die 3 untergeordneten Zeilen zu löschen, die wiederum 4 untergeordnete Zeilen löschen. Achten Sie darauf, dass Zeilen auf jeder Ebene sind. Wenn es keine verknüpften Subchildzeilen gibt, erhalten Sie eine NulLReferenceException
unter subChanges.Rows.Count
.
Datenbank aktualisiert
Dies bringt die Änderungen bekommen und sie um die Anwendung. Normalerweise wendet DataAdapter
alle Einfügungen, Aktualisierungen und Löschungen für Sie auf einmal an. Das wird nicht funktionieren ohne Einschränkungen zu verletzen, so einige Code verwenden, um die Änderungen anzuwenden, um:
// Assuming changes were applied on DS/DT rows,
// changed rows should be duly marked, so just
// run the updates
// insert first
DataTable parentRows = dsSample.Tables["Parent"].GetChanges(DataRowState.Added);
DataTable childRows = dsSample.Tables["Child"].GetChanges(DataRowState.Added);
DataTable subChRows = dsSample.Tables["SubChild"].GetChanges(DataRowState.Added);
// dont Update the DS.Tables...it will try
// to do ALL pending changes
if (parentRows != null)
daParent.Update(parentRows);
if (childRows != null)
daChild.Update(childRows);
if (subChRows != null)
daSubCh.Update(subChRows);
// then update..order doesnt matter
parentRows = dsSample.Tables["Parent"].GetChanges(DataRowState.Modified);
childRows = dsSample.Tables["Child"].GetChanges(DataRowState.Modified);
subChRows = dsSample.Tables["SubChild"].GetChanges(DataRowState.Modified);
if (parentRows != null)
daParent.Update(parentRows);
if (childRows != null)
daChild.Update(childRows);
if (subChRows != null)
daSubCh.Update(subChRows);
// then deletes...in reverse order!
parentRows = dsSample.Tables["Parent"].GetChanges(DataRowState.Deleted);
childRows = dsSample.Tables["Child"].GetChanges(DataRowState.Deleted);
subChRows = dsSample.Tables["SubChild"].GetChanges(DataRowState.Deleted);
if (subChRows != null)
daSubCh.Update(subChRows);
if (childRows != null)
daChild.Update(childRows);
if (parentRows != null)
daParent.Update(parentRows);
// our work is done...time for cheesecake
dsSample.AcceptChanges();
Achten Sie darauf, Prozess löscht in umgekehrter Reihenfolge - subChild zunächst auf bis zu Eltern Reihen. Wenn alles wie vorgesehen funktioniert, sollte die Access-Abfrage aus, bevor jetzt leer sein:
et voilà!
Es ist nicht möglich, von dieser SQL zu sagen, was Sie tun (oder wenn die DB Vorbild ist auch richtig). Aber es ist zweifelhaft, dass Sie für jeden Tisch einen Adapter benötigen würden - einige sehen wie Lookups aus. In diesem Fall könnten sie Hilfsklassen/Objekte sein, die diesen Dienst bereitstellen.
Ressourcen
Sie haben 'CommandText' von' DeleteCommand' nicht angezeigt. –
Ich verwende nicht den Befehlstext. Ich lösche die aktuelle Zeile mit myrow.Delete() und commandbuider – NewPassionnate