- PR -

[VS.NET2005][C#][ADO.NET]RowUpdatedイベントのSqlRowUpdatedEventArgs.Rowプロパティ

1
投稿者投稿内容
ベテラン
会議室デビュー日: 2005/05/16
投稿数: 85
お住まい・勤務地: 千葉県在住
投稿日時: 2007-04-26 18:41
【環境】
OS:WindowsXPPro
IDE:VS.NET2005
DB:MSSQLServer2005
言語:C#
AP:Windowsアプリ

SQLDataAdapterを使って、複数レコードデータを格納したDataSetをDataBaseに更新
しようとしています。
対象Tableの主キー(ID)はIdentityを使って自動連番させるようにしています。
また、同時実行違反チェックの為に、timestamp型のカラムも用意しました。

DataBaseには、Insert/Update/Deleteの各処理に対応したストプロを作成し、
InsertCommand/UpdateCommand/DeleteCommandにはそれぞれのストプロを
呼び出すよう設定してあります。

Insert/Updateのストプロ内では、IDやtimestampの値を取得する為に、対象データを
再Selectするようにしていたのですが、ここで1つ問題が発生してしまいました。

CommandのUpdatedRowSourceプロパティは、デフォルトの「Both」のままにして
いたのですが、このプロパティは「None」以外を指定するとデータの更新が
正常終了していても失敗していても、対象DataRowのRowStateを「Unchanged」に
してしまうのです。
※正確には、ストプロにデータを渡す前にRowStateを「Unchanged」にし、
 戻ってきたデータをDataRowに反映させるとRowStateが「Modified」になる。

これでは、複数レコードある更新の途中でエラーが発生した場合に、DBへの
更新はRollBackできても、DataSetの中まではUpdate直前には戻せません。
(更新処理された行までのRowStateがUpdate前と変わってしまっている)

そこで、こちらを参考に、RowUpdatedイベント内でIDとtimestampを取得しようと思ったのですが・・・
SqlRowUpdatedEventArgsのRowプロパティって読み込み専用ですよね?
※もちろんOleDbRowUpdatedEventArgsのRowプロパティも

試しに、こんな感じでコーディングしてみたのですが、やっぱりエラーが発生。
コード:
private void sqlDataAdapterParent_RowUpdated(object sender, System.Data.SqlClient.SqlRowUpdatedEventArgs e)
{
    if (e.Row != null)
    {
        if (e.StatementType == StatementType.Insert)
        {
            e.Row["IdentityKey"] = e.Command.Parameters["@ScopeIdentity"].Value;
        }
    }
}



エラー内容:列 'IdentityKey' は読み取り専用です。

※InsertCommand.UpdatedRowSourceには「None」を指定しています
※@ScopeIdentityは、ストプロでOUTPUTしていした、SCOPE_IDENTITY()の値を格納したパラメータです


これって、MSDNの誤りなんでしょうか?
ちなみに、会社に置いてある「プログラミングADO.NET」も読んでみたのですが、
(ちょっと古いですが「Microsoft公式解説書」って書いてある)
こちらでも、RowUpdatedイベント内で取得したIDをOleDbRowUpdatedEventArgsの
Rowプロパティを使って代入しろと書いてあるんですよね。

自分のプログラムに問題あり?

どなたか、ADO.NETに強い方。お知恵を貸して下さい!!
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-04-26 20:03
引用:

これでは、複数レコードある更新の途中でエラーが発生した場合に、DBへの
更新はRollBackできても、DataSetの中まではUpdate直前には戻せません。


設計思想の問題なのかも知れませんが、
私は更新でエラーが発生した後に、その DataSet を再利用して
更新するような設計はしません。
エラーが出たあとは、異常系の処理だけにしてしまいます。
正しく更新できるという保証はありませんから。

引用:

SqlRowUpdatedEventArgsのRowプロパティって読み込み専用ですよね?


それはその通りですが、

e.Row["IdentityKey"] =

という記述は、Row プロパティに値を設定しているわけではないですよね。
Row プロパティで返された DataRow のインデクサに代入しているだけです。
このインデクサは読み取り専用ではありません。
(プロパティが読み取り専用なら実行時エラーではなくコンパイルエラーになります)

代入したときに実行時エラーになるのは、
DataColumn.Readonly プロパティが true になっているからでしょう。
ID列の場合は Fill したときに true に設定されますから。
これを false にすれば代入は可能と思います。
ベテラン
会議室デビュー日: 2005/05/16
投稿数: 85
お住まい・勤務地: 千葉県在住
投稿日時: 2007-04-27 09:00
KIさん、こんにちは。

引用:

代入したときに実行時エラーになるのは、
DataColumn.Readonly プロパティが true になっているからでしょう。



うぁ。お恥ずかしい。
仰る通りでした。

引用:

設計思想の問題なのかも知れませんが、
私は更新でエラーが発生した後に、その DataSet を再利用して
更新するような設計はしません。
エラーが出たあとは、異常系の処理だけにしてしまいます。
正しく更新できるという保証はありませんから。



仰る事は重々承知しているつもりです。
が、エンドユーザーは良く言うのですよ。
『データを入力した後、エラーが出ても問題の場所さえ修正入力すれば再度登録できるようにして欲しい。
 エラーが出たら今まで入力したものを再度入力させられるなんてありえない』
・・・と(^^;

そこで苦肉の策での、DataSetの再利用という訳です。
KI
大ベテラン
会議室デビュー日: 2007/01/10
投稿数: 239
投稿日時: 2007-04-27 09:22
引用:

仰る事は重々承知しているつもりです。
が、エンドユーザーは良く言うのですよ。
『データを入力した後、エラーが出ても問題の場所さえ修正入力すれば再度登録できるようにして欲しい。
 エラーが出たら今まで入力したものを再度入力させられるなんてありえない』
・・・と(^^;

そこで苦肉の策での、DataSetの再利用という訳です。


ユーザー入力のチェックは、SqlDataAdapter.Update を呼び出す前に
プログラムでチェックすべきだと思います。
「問題の場所さえ修正入力すれば」とユーザーさんがおっしゃるようなエラーなら
事前にチェックすることは可能ですよね。
例外はプログラムで予期できなかった致命的なエラーが起きた場合の処理と考えています。
1

スキルアップ/キャリアアップ(JOB@IT)