但是今天我报错了:
generated sql is not supported for multiple table delete row
这是什么意思??
正如错误消息所说,DbCommandBuilder 只能为单表查询自动生成 SQL。这使得维护基本表(例如客户、产品、员工等)的应用程序很容易实现。它们可以用于Customer-Order-OrderLines-ServiceItem 关系之类的东西,但您必须以不同的方式处理它。
但在您这样做之前要知道,有各种框架可以帮助您实施和管理这种事情。查看 ORM。
由于DataAdapter 一次只能更新一个表,您将需要多个适配器。您可以定义 PK-FK 关系,而不是使用 SQL JOIN。对父行的更改将级联到子行。
出于演示目的,我巧妙地将表格命名为 Parent、Child 和 SubChild。一个父行可以有很多子行(1:m);每个子行可以有许多子行 (1:m)。
// form/class level objects
DataSet dsSample;
OleDbDataAdapter daParent;
OleDbDataAdapter daChild;
OleDbDataAdapter daSubCh;
在其他地方,配置它们:
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;
当父行被删除时,相关的子行将受到影响; Child-to-SubChild 也一样。请注意,即使您不选择某种 ORM,其中很多也可以由与参与者相关的类分解和处理。例如,处理该位数据的 Room 或 City 类。
测试关系
查询与“ParentB”相关的行:
- 有 3 个相关子行(3 个不同的 Child.Id)
- 有 4 个 SubChild 行与这些 ChildRows 相关
- 如果您想对名称进行解码,
ChildB1 是 ParentB 的第一个子行; ChildB-1C 表示子子项与与 parentB 相关的第一个子行相关。结果并没有我想象的那么清楚。
测试:
// 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);
结果:
子变化:3,子通道变化:4
完美!删除单个父行级联也删除了 3 个子行,这反过来又删除了 4 个子子行。注意每一层都有行——如果没有相关的子行,你会在subChanges.Rows.Count上得到一个NulLReferenceException。
更新数据库
这需要获取更改并按顺序应用它们。通常,DataAdapter 会立即为您应用所有插入、更新和删除。如果不违反约束,那将无法正常工作,因此请使用一些代码按顺序应用更改:
// 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();
请务必以相反的顺序处理删除操作 - 先从子子行到父行。如果一切正常,之前的 Access 查询现在应该是空的:
等等!
不可能从那个 SQL 中看出你在做什么(或者数据库是否被正确建模)。但您是否需要为每个表配备一个适配器是值得怀疑的 - 有些 看起来像 查找。在这种情况下,它们可能是提供该服务的辅助类/对象。
资源