簡易ORMフレームワークを作成してみる(19)
型指定されたDataSetを永続化するための更新系の実装です。発想的には検索系とほぼ同じです。基本コンセプトはDataSetには各行は状態値を持って、追加、更新、削除を判断できるようになっています。この状態に合わせてデータベースに対してINSERT、UPDATE、DELETEを実行することでDataSetで操作された内容がデータベースに保存されることになります。標準のデータアダプタと同じ発想です。
今回の工夫としては、複数のテーブルに対して処理を実行する際に、親子の順番を意識してINSERTやDELETEを処理します。
public virtual int Update(DataSet target, UpdatePolicy policy) { bool enforceConstraints = target.EnforceConstraints; try { target.EnforceConstraints = false; _updatedTable = new List(); if *1 return 0; _updatedTable.Add(targetTable); int affectCount = 0; DbCommand cmd = CommandBuilder.GetUpdateCommand(schemaTable); foreach (DataRow row in targetTable.Rows) { if (row.RowState == DataRowState.Modified) { AssignParameter(cmd, row); int updatedCount = CommandHelper.ExecuteUpdateRow(cmd, row); if (updatedCount == 0) { throw new DBConcurrencyException( cmd.CommandText, null, new DataRow { row }); } affectCount += updatedCount; } } if ((policy & UpdatePolicy.ChildRelation) == UpdatePolicy.ChildRelation) { foreach (DataRelation relation in schemaTable.ChildRelations) { affectCount += ModifyRecursive( relation.ChildTable, targetTable.DataSet.Tables[relation.ChildTable.TableName], policy); } } if ((policy & UpdatePolicy.ParentRelation) == UpdatePolicy.ParentRelation) { foreach (DataRelation relation in schemaTable.ParentRelations) { affectCount += ModifyRecursive( relation.ParentTable, targetTable.DataSet.Tables[relation.ParentTable.TableName], policy); } } return affectCount; } protected virtual int AddRecursive(DataTable schemaTable, DataTable targetTable, UpdatePolicy policy) { if (_updatedTable.Contains(targetTable)) return 0; _updatedTable.Add(targetTable); bool pkReadonly = targetTable.PrimaryKey[0].ReadOnly; try { targetTable.PrimaryKey[0].ReadOnly = false; int affectCount = 0; DbCommand cmd = CommandBuilder.GetInsertCommand(schemaTable); foreach (DataRow row in targetTable.Rows) { if (row.RowState == DataRowState.Added) { AssignParameter(cmd, row); int insertedCount = CommandHelper.ExecuteUpdateRow(cmd,row); if (insertedCount == 0) { throw new DBConcurrencyException( cmd.CommandText, null, new DataRow { row }); } affectCount += insertedCount; } } if *2 return 0; _updatedTable.Add(targetTable); int affectCount = 0; if ((policy & UpdatePolicy.ChildRelation) == UpdatePolicy.ChildRelation) { foreach (DataRelation relation in schemaTable.ChildRelations) { affectCount += DeleteRecursive( relation.ChildTable, targetTable.DataSet.Tables[relation.ChildTable.TableName], policy); } } if ((policy & UpdatePolicy.ParentRelation) == UpdatePolicy.ParentRelation) { foreach (DataRelation relation in schemaTable.ParentRelations) { affectCount += DeleteRecursive( relation.ParentTable, targetTable.DataSet.Tables[relation.ParentTable.TableName], policy); } } DbCommand cmd = CommandBuilder.GetDeleteCommand(schemaTable); foreach (DataRow row in targetTable.Rows) { if (row.RowState == DataRowState.Deleted) { AssignParameter(cmd, row, DataRowVersion.Original); affectCount += CommandHelper.ExecuteCommand(cmd); } } return affectCount; }
*1:policy & UpdatePolicy.DirectMask) == UpdatePolicy.None)
policy |= UpdatePolicy.ChildRelation;
int affectCount = 0;
affectCount += DeleteRecursive(RootTable, target.Tables[RootTable.TableName], policy);
_updatedTable = new List
*2:policy & UpdatePolicy.ChildRelation) == UpdatePolicy.ChildRelation) { foreach (DataRelation relation in schemaTable.ChildRelations) { affectCount += AddRecursive( relation.ChildTable, targetTable.DataSet.Tables[relation.ChildTable.TableName], policy); } } if ((policy & UpdatePolicy.ParentRelation) == UpdatePolicy.ParentRelation) { foreach (DataRelation relation in schemaTable.ParentRelations) { affectCount += AddRecursive( relation.ParentTable, targetTable.DataSet.Tables[relation.ParentTable.TableName], policy); } } return affectCount; } finally { targetTable.PrimaryKey[0].ReadOnly = pkReadonly; } } protected virtual int DeleteRecursive(DataTable schemaTable, DataTable targetTable, UpdatePolicy policy) { if (_updatedTable.Contains(targetTable