簡易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(); affectCount += ModifyRecursive(RootTable, target.Tables[RootTable.TableName], policy); _updatedTable = new List(); affectCount += AddRecursive(RootTable, target.Tables[RootTable.TableName], policy); return affectCount; } finally { target.EnforceConstraints = enforceConstraints; } } protected virtual int ModifyRecursive(DataTable schemaTable, DataTable targetTable, UpdatePolicy policy) { if (_updatedTable.Contains(targetTable

*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