none
DataAdapterによる更新 RRS feed

  • 質問

  • おはようございます。

    VB2005+SQLServerExpressを利用しています。

    DataAdapterを用いて、DataGridViewに表示しているデータの更新を行いたいと思っています。

    データの内容は、社員の入社、退職に関する情報です。

    DBからデータを取得して、DataGridViewに表示するところまではできました。

    次に更新するために以下のコードを記述しました。

    DA.UpdateCommand = New SqlCommand

                With DA.UpdateCommand
                    .Connection = Cn
                    .CommandText = "ストアド名"
                    .CommandType = CommandType.StoredProcedure
                    .Parameters.AddWithValue("@id", "id")
                    .Parameters.AddWithValue("@社員コード", ”社員コード")
                    .Parameters.AddWithValue("@入社日", "入社日")
                    .Parameters.AddWithValue("@退職日", "退職日")
                    .Parameters.AddWithValue("@timestamp", "timestamp")
                End With

                'Data更新
                DA.Update(DS)
    テーブルが一つしかないので、テーブル名は省いてあります。

    Parameterの「入社日」などは、DataTableのフィールド名と一致しております。

    しかし、この状態で更新を行おうとするとエラーとなってしまいます。(エラー内容は不明です。Try~Catch構文を利用しており、
    どの部分でエラーとなったのかをMessageboxで表示するだけの処理となっているためです。エラー内容を取得する方法は
    現在調べています。)

    「入社日」などの日付のデータはDataGridView上では、和暦表示となるようにしてあります。
    もしかしたら、このあたりが問題かと思ってはいるのですが、どのように対処をすればよいかがわかりません。

    試しに、
    .Parameters.AddWithValue("@入社日", "入社日")
    の部分を

    .Parameters.AddWithValue("@入社日", cdate("入社日"))
    としても、エラーなります。

    この場合は、どのように対処をすればよいのでしょうか。
    MSDNを参考に記述をしてみたのですが、なぜだめなのか、よくわかりません。

    どうか、よろしくお願いします。

    2009年12月30日 19:41

回答

  • やりたいことはDataTableの"id"という列から値を取り出して、パラメーター"@id"にセットするといったことですよね?

    Parameters.AddWithValue("@id", "id")というのは、「@idという名前のパラメータに"id"という文字列をセットする」という命令です。
    数値を入れなければいけない@idパラメーターに文字列が入っているため、SQLサーバーが更新データを受け取った時に、「文字列は数値に変換できませんでした」というエラーが発生しています。
    (nvarcharというのはSQLサーバーで文字列をあらわす型です。)


    まず、サーバーのストアドの定義を確認してください。
    たぶんパラメータの型は@idや@社員コードはintやsmallintのような数値で、@入社日,@退職日はdateやあるいはdatetimeのような日時をあらわす型、@timestampはtimestamp型になっているとおもいます。

    パラメーターの型が確認できたら、今度はデータアダプタのUpdataCommand.Parametersに正しい型でパラメータを定義してやります。
    定義には
    Public Function Add (parameterName As String,sqlDbType As SqlDbType,size As Integer, sourceColumn As String ) As SqlParameter
    http://msdn.microsoft.com/ja-jp/library/e5xwx8sc.aspx
    を使います。
    1番目の引数でパラメーター名を指定します。
    2番目の引数でサーバー側でどんな型になっているかを指定します。
    3番目の引数でデータを格納するにはどれくらいのバイト数あるいは文字数が必要かを指定します。
    4番目の引数でDataTableのどの列から値を取り出すかを指定します。

    そうすると以下のように書くことになります。
    .Parameters.Add("@id" , SqlDbType.Int , 4 , "id")
    .Parameters.Add("@入社日" , SqlDbType.DateTime , 8 , "入社日")
    .Parameters.Add("@timestamp" , SqlDbType.TimeStamp , 8 , "timestamp")

    文字列だとしたら.Parameters.Add("@社員コード" , SqlDbType.NVarChar , 20 , "社員コード")といった感じになります。

    今回の問題でわかっていなかったのは、SQLサーバーのテーブルにも型があることと、ストアド等のパラメータにも型があり一致していないといけないということですね。
    サーバーのデータ型とSqlDataTypeの関係は データ型の対応 に書いてあります。
    サーバーのデータ型 の各データ型からのリンクでサーバーデータ型の詳しい説明が書いてあります。
    ここらへんに目を通しておくことよいでしょう。
    • 回答としてマーク TI-cb400 2009年12月31日 11:35
    2009年12月31日 0:18

すべての返信

  • 自己レスです。

    エラー内容を出す方法がわかったので、エラーをMessageBoxに出力してみたところ、

    「nvarcharをintに変換できませんでした」

    というエラー内容でした。

    しかし、DataTableの型をチェックしてみたところ、nvarchar型のものはありませんでした。

    いくつか、このエラーメッセージに関する記事をインターネットで見かけたのですが、よくわかりません。

    どうか、アドバイスお願いします。
    2009年12月30日 22:24
  • エラー内容を出す方法がわかったので、エラーをMessageBoxに出力してみたところ、

    「nvarcharをintに変換できませんでした」

    というエラー内容でした。

    しかし、DataTableの型をチェックしてみたところ、nvarchar型のものはありませんでした。

    たぶん

    > .Parameters.AddWithValue("@id", "id")

    で引っかかっているのだと思います。
    AddWithValue の第二引数は、列名ではなくパラメータに代入される値ですよ。
    ですから、例えば id が 1234 の場合

     .Parameters.AddWithValue("@id", 1234)

    としなければいけません。

    2009年12月31日 0:13
    モデレータ
  • やりたいことはDataTableの"id"という列から値を取り出して、パラメーター"@id"にセットするといったことですよね?

    Parameters.AddWithValue("@id", "id")というのは、「@idという名前のパラメータに"id"という文字列をセットする」という命令です。
    数値を入れなければいけない@idパラメーターに文字列が入っているため、SQLサーバーが更新データを受け取った時に、「文字列は数値に変換できませんでした」というエラーが発生しています。
    (nvarcharというのはSQLサーバーで文字列をあらわす型です。)


    まず、サーバーのストアドの定義を確認してください。
    たぶんパラメータの型は@idや@社員コードはintやsmallintのような数値で、@入社日,@退職日はdateやあるいはdatetimeのような日時をあらわす型、@timestampはtimestamp型になっているとおもいます。

    パラメーターの型が確認できたら、今度はデータアダプタのUpdataCommand.Parametersに正しい型でパラメータを定義してやります。
    定義には
    Public Function Add (parameterName As String,sqlDbType As SqlDbType,size As Integer, sourceColumn As String ) As SqlParameter
    http://msdn.microsoft.com/ja-jp/library/e5xwx8sc.aspx
    を使います。
    1番目の引数でパラメーター名を指定します。
    2番目の引数でサーバー側でどんな型になっているかを指定します。
    3番目の引数でデータを格納するにはどれくらいのバイト数あるいは文字数が必要かを指定します。
    4番目の引数でDataTableのどの列から値を取り出すかを指定します。

    そうすると以下のように書くことになります。
    .Parameters.Add("@id" , SqlDbType.Int , 4 , "id")
    .Parameters.Add("@入社日" , SqlDbType.DateTime , 8 , "入社日")
    .Parameters.Add("@timestamp" , SqlDbType.TimeStamp , 8 , "timestamp")

    文字列だとしたら.Parameters.Add("@社員コード" , SqlDbType.NVarChar , 20 , "社員コード")といった感じになります。

    今回の問題でわかっていなかったのは、SQLサーバーのテーブルにも型があることと、ストアド等のパラメータにも型があり一致していないといけないということですね。
    サーバーのデータ型とSqlDataTypeの関係は データ型の対応 に書いてあります。
    サーバーのデータ型 の各データ型からのリンクでサーバーデータ型の詳しい説明が書いてあります。
    ここらへんに目を通しておくことよいでしょう。
    • 回答としてマーク TI-cb400 2009年12月31日 11:35
    2009年12月31日 0:18
  • > 「nvarcharをintに変換できませんでした」

    そのエラーの原因は、他の回答者のみなさんが書かれている通りですが、それを修正しても
    他にも問題がありそうで、たぶんうまくいかないのではないかと思います。例えば、

    (1) timestamp は DB エンジン側で独自に設定されるもので、AddWithValue 等で値を設定
      するものではありません。楽観的同時実行制御をするためということでしたら、設定
      の方法が違います。

    (2) UPDATE クエリが書いてないのでわかりませんが、楽観的同時実行制御をするなら WHERE
      句に以下のような条件が設定してあるでしょうか?

      ・・・WHERE (([id] = @Original_id) AND ([timestamp] = @Original_timestamp))

      @Original_id, @Original_timestamp を設定する際、その SqlParameter の設定が、
      DataSet から元の値を取得できるようになってないとダメですが、アップされたコードは
      そのようになっていません。設定例は、以下のページが参考になると思います。

      SqlDataAdapter.UpdateCommand プロパティ
      http://msdn.microsoft.com/ja-jp/library/system.data.sqlclient.sqldataadapter.updatecommand.aspx

    (3) ストアドが書いてないのでわかりませんが、UPDATE 後の timestamp の値は DataSet
      に書き戻されているでしょうか?

    (4) id は主キーだと思いますが、もし UPDATE で主キーも変更するのであれば、パラメータの設定に
      もう一工夫必要です(たぶんクエリの方も)。

    型付 DataSet + TableAdapter が簡単に自動生成できますので、それを使用してはいかがです
    か? 勉強のために、自分で ADO.NET の基本クラスライブラリを使って書きたいという場合
    でも、自動生成されたコードが参考になるはずです。

    2009年12月31日 1:48
  • 追伸です。

    MDSN Library はお持ちでしょうか? Express Edition のヘルプでは情報が十分得られない
    ので、もしお持ちでなければ、以下のツールをダウンロードして使ってみてくだい。

    MSDN Library for Visual Studio 2008 SP1 (2008年12月更新版)
    http://www.microsoft.com/downloads/details.aspx?FamilyID=7bbe5eda-5062-4ebb-83c7-d3c5ff92a373&DisplayLang=ja

    Web の MDSN Library とほぼ同じものが含まれています。

    2009年12月31日 2:08
  • ご回答ありがとうございます。

    gekka様
    ご提示の方法でうまくできました。

    いままで
    Add
    AddWithValue
    の違いを理解していなかったのですが、今回なんとなくイメージをつかむことができました。

    SuferOnWww様
    DataTableの内容が更新されても、DBのTimeStamp列を取り込んだフィールドが更新されることはありませんよね。
    このような場合でも、@Original_TimeStampをパラメータとして取得したほうが良いのでしょうか。

    現状では、ストアドプロシージャでDBから取り込んだ時点のTimeStampをWHERE区に加えておりますので、問題はないかと
    思います。

    @idはテーブルの主キーでフォームからいじることが出来ないようにしてあります。

    また、更新後のTimeStampに関しては、更新後、Fillメソッドを再度実施することで、DataTableの内容を最新にしているのですが
    これでは効率が悪いでしょうか。

    2009年12月31日 5:32
  • > DataTableの内容が更新されても、DBのTimeStamp列を取り込んだフィールドが更新されることはありませんよね。
    > このような場合でも、@Original_TimeStampをパラメータとして取得したほうが良いのでしょうか。

    楽観的同実行制御のため DataTable は Original の値と Current の値を保持していて、
    SqlDataAdapter.Update メソッドを実行すると、両方の値を使用して DB に DELETE,
    INSERT, UPDATE をかけます。

    それゆえ @Original_timestamp には DataTable の Original の値が使用されるよう
    に SqlParameter を設定します。

    > 現状では、ストアドプロシージャでDBから取り込んだ時点のTimeStampをWHERE区に加えておりますので、問題はないかと
    > 思います。

    それはいつの時点ですか? UPDATE クエリをかける直前では意味がないですよね。

    最初に DataSet に Fill した時点であれば、DataSet/DataTable に値が保持されているので
    それを使えばすみます。

    何のために DataSet/DataTable + SqlDataAdapter を使うのか、理解されているでしょうか?

    > @idはテーブルの主キーでフォームからいじることが出来ないようにしてあります。

    意味がよく分かりません。それなら何故  .Parameters.AddWithValue("@id", "id") とした
    のですか?

    > また、更新後のTimeStampに関しては、更新後、Fillメソッドを再度実施することで、DataTableの内容を最新にしているのですが
    > これでは効率が悪いでしょうか。

    悪いです。更新した行だけですむところ、全行再取得されてしまいます。


    何故、型付 DataSet + TableAdapter を自動生成して使わないのですか? それなしでは、
    実用になるプログラムが作成できるようになるまでには、相当時間がかかると思いますけど。

    趣味の問題ということで自分でコードを書きたいということでも、自動生成されたコードを眺め
    てみれば勉強になると思うのですが・・・

     

    2009年12月31日 9:30
  • ご回答ありがとうございます。

    私の内容を見ればわかるように、まったくの素人で自分の仕事を楽にするために勉強を兼ねて、プログラムをしております。

    ということで、単純にやりたいことを調べていたところ、今回の内容に行き着いたわけで、せっかくやり始めたことなので、
    まずはこのやり方でやっているだけです。

    ご提示いただいた、型付 DataSet + TableAdapter をやらないのではなく、そこまで同時にやるほどの知識がないので、
    今は調べている段階なので、なんらコメントができない状態です。ご理解下さい。

    ただ、想像以上にプログラムは大変ですね。
    2009年12月31日 11:34
  • > ご提示いただいた、型付 DataSet + TableAdapter をやらないのではなく、そこまで同時にやるほどの知識がないので、
    > 今は調べている段階なので、なんらコメントができない状態です。ご理解下さい。

    今年の3月から提案させていただいているのですが・・・

    > ただ、想像以上にプログラムは大変ですね。

    もっと簡単にできるのに、わざわざ難しい道を選んでいるような感じがします。

    他に、楽に、短時間で、バグが少なく、実用的なプログラムを作る手段があるのですから、
    それを使わない手はないと思うのですが。「自分の仕事を楽にするため」なら特に。

    趣味の問題なら話は別ですが、そうなんですか?

    • 編集済み SurferOnWww 2009年12月31日 13:33 誤記訂正
    2009年12月31日 13:26
  • あけましておめでとうございます。本年もよろしくお願いいたします。<(_ _)>


    さて

    > 単純にやりたいことを調べていたところ、今回の内容に行き着いたわけで、せっかくやり始めたことなので、
    > まずはこのやり方でやっているだけです。

    ADO.NET を生で扱うのは、ある程度の熟練開発者でないと相当難しいと思います。

    まして今までの流れを見てますと、T-SQL の基本やパラメタライズドクエリ、
    さらにセキュリティやトランザクション管理等、開発するに当たっての基本知識が欠けているように思え
    できたと喜んでいたのも束の間、想定外のバグを生み、返って業務に重大な支障をきたす恐れがあります。


    > ご提示いただいた、型付 DataSet + TableAdapter をやらないのではなく、そこまで同時にやるほどの知識がないので、
    > 今は調べている段階なので、なんらコメントができない状態です。ご理解下さい。

    今は入門用の書籍も多く出ていますので、調べるのは左程難しくないです。
    急がば回れといいますが、楽かつ安全にできるなら、そちらに思い切って方向転換するのも勇気のいることです。


    > 想像以上にプログラムは大変ですね。

    ある意味 「職人」 の世界ですからね。

    私も元々CADオペレータからシステム管理者を経てプログラミングの世界に入り、
    以降長くこの業界で仕事させて頂いておりますが、アマとプロとでは全く深みが違います。
    いつまで経っても勉強不足を痛感しております。

    私も元々思い込みが激しいタイプですので、
    設計や実装方法、さらに考え方の誤りを指摘されたり、後から自分で気が付いたりすることが多々あるのですが、
    そのとき思い切って従来のやり方・考え方に対する執着を捨て、より優れた技術を取り入れるよう日々心がけてます。

    2010年1月1日 13:20
    モデレータ
  • 次に技術的なことに対する回答です。

    > DataTableの内容が更新されても、DBのTimeStamp列を取り込んだフィールドが更新されることはありませんよね。
    > このような場合でも、@Original_TimeStampをパラメータとして取得したほうが良いのでしょうか。

    SurferOnWww さんが述べられてますが補足です。
    たぶんトランザクション管理について知識を持たれてないためこの質問をされているのだと思います。

    以下参考URL です。
    これを読めば「楽観的同時実行制御」という意味がおぼろげながら掴めるかと思います。

    http://itpro.nikkeibp.co.jp/article/COLUMN/20080123/291846/
    http://www.atmarkit.co.jp/fdotnet/entwebapp/entwebapp09/entwebapp09_01.html
    http://www.atmarkit.co.jp/fdotnet/basics/adonet06/adonet06_01.html

    もしトランザクションに関して興味を持たれたら、以下の本がお勧めです。
    この本には大変助けられました。赤間さんには未だに感謝しております。

    http://www.amazon.co.jp/dp/4891004312


    > 現状では、ストアドプロシージャでDBから取り込んだ時点のTimeStampをWHERE区に加えておりますので、
    > 問題はないかと思います。

    これはどういう意図を持った設計なのか良く判りません。
    たぶんこういう設計をしている現場はないのではと思います。


    > また、更新後のTimeStampに関しては、更新後、Fillメソッドを再度実施することで、
    > DataTableの内容を最新にしているのですがこれでは効率が悪いでしょうか。

    SurferOnWww さんの仰るとおり、効率が悪いです。

    2010年1月1日 13:46
    モデレータ
  • あけましておめでとうございます。

    本年も、よろしくお願い致します。

    現在、TableAdapterに挑戦しております。
    なかなか、設定することが多く、へこたれそうですが、MSDNも一通り目を通したので、実際に作ってテスト中です。
    こちらに関しては、また、わからないことが出ましたら、是非ともご教授お願い致します。

    本題と外れてしまうのですが、せっかく貴重なご意見を聞かせた頂いているので、別の質問をさせて下さい。(関連はあると思います。)

    現在、ストアドプロシージャにてデータの更新をする際は、

    select id, a, b, timestamp from table where id = @id
    にて、データを取得(idが主キーです)

    update table set a = @a, b = @b where id = @id and timestamp = @timestamp
    にて、データの更新

    ということをしております。

    DataTableにデータを取得して、内容を更新し、DataTableの対応する各列をUpdateの
    ストアドプロシージャのパラメータとしていたのですが、これでは足りないものがあるということでしょうか。

    このあたりの理解が不足しており、一般的にはどのようにするべきかがよくわかっていません。


    2010年1月1日 21:31
  • UPDATEクエリに問題はないですよ。
    timestampの使い方も同時実行制御に使う方法は間違ってないと思います。
    timestampのように一意に出来る値を作れないDBや容量を気にする場合は全ての列のチェックをする方法を使うしかありませんが。
    全ての列をチェックする方法ではSelectで必要もない全ての列データを取得しておかないとチェックできないので無駄ですしね。
    そもそもMSDNのオプティミスティック同時実行制御の説明にtimestampを使った方法がしっかり書いてありますしね。

    #私も独学でしか勉強してないしDBが専門でもないので、絶対にtimestampを使わないかなんて知りませんし。

    Fillで再取得しているのが効率が悪いのは、ストアドでSqlParameter.DirectionをInputOutputにして、新しい値をUpdateの時に拾ってこれると効率が良くなりますね。
    これはストアドを変更しないといけないので出来ればですが。
    #効率が少々悪くても再取得する量が少ないならシビアに考える必要はないでしょうけど。

    皆さんはTableAdapterを使えとおっしゃってますが、実際にデータベースとやり取りしているとTableAdapterの自動生成では満足できなくなって、結局自分でCommandを操作するようになるでしょう。
    その時に今回悩んだことは無駄にならないのでがんばってください。
    ただしMSDNを調べる癖は付けておいてくださいね。
    2010年1月2日 0:06
  • > 現在、ストアドプロシージャにてデータの更新をする際は、
    >
    > select id, a, b, timestamp from table where id = @id
    > にて、データを取得(idが主キーです)
    >
    > update table set a = @a, b = @b where id = @id and timestamp = @timestamp
    > にて、データの更新
    >
    > ということをしております。

    先のレスにも書きましたが、UPDATE クエリをかける直前に SELECT で取得した timestamp 値を
    @timestamp に設定して where id = @id and timestamp = @timestamp とするのは意味が
    ありません。

    DataSet を使用した楽観的同時実行制御とはどういうことか理解されているでしょうか? 以下の
    ようなステップになっているはずですが、

    (1) DB からデータを DataSet に取得。
    (2) DataSet のデータを編集。
    (3) 編集結果をまとめて DB に反映する(Update する)。

    とすると、where id = @id and timestamp = @timestamp の @timestamp に設定するデータ
    はどの時点のものでなければならないでしょう? (1) の時点ですよね。

    > DataTableにデータを取得して、内容を更新し、DataTableの対応する各列をUpdateの
    > ストアドプロシージャのパラメータとしていたのですが、これでは足りないものがあるということでしょうか。

    未検証ですが、以下のような感じになるはずです。主キーの id を変更しないのであれば、そのあた
    りのコードは不要ですが。

    SqlDataAdapter adapter = new SqlDataAdapter();
    string query = "UPDATE table SET id = @id, a = @a, b = @b WHERE ((id = @Original_id) AND (timestamp = @Original_timestamp)); " +
            "SELECT id, a, b, timestamp FROM table WHERE (id = @id)";
    SqlCommand command = new SqlCommand(query, connection);
    command.Parameters.Add("@id", SqlDbType.Int, 0, "id");
    command.Parameters.Add("@a", SqlDbType.Int, 0, "a");
    command.Parameters.Add("@b", SqlDbType.Int, 0, "b");
    SqlParameter parameter = command.Parameters.Add("@Original_id", SqlDbType.Int, 0, "id");
    parameter.SourceVersion = DataRowVersion.Original;
    parameter = command.Parameters.Add("@Original_timestamp", SqlDbType.Timestamp, 0, "timestamp");
    parameter.SourceVersion = DataRowVersion.Original;
    adapter.UpdateCommand = command;

    • 編集済み SurferOnWww 2010年1月2日 4:45 誤記訂正 SELECT ... → "SELECT ...
    2010年1月2日 4:28
  • 補足です。

    > 現在、TableAdapterに挑戦しております。
    > なかなか、設定することが多く、へこたれそうですが、MSDNも一通り目を通したので、実際に作ってテスト中です。

    以下のページには目を通されたでしょうか? もし答えがノーなら、一度これに従って作業して
    みてはいかがでしょう。

    いかに Visual Studio の自動生成機能がよく考えられていて、たとえ ADO.NET 知識が全くなく
    ても、DB のデータを DataGridView などに表示して、それを編集し、編集した結果で DB を更
    新するという基本的な操作が簡単に実装できるか理解いただけると思います。

    チュートリアル : データベースへのデータの保存 (単一テーブル)
    http://msdn.microsoft.com/ja-jp/library/0f92s97z.aspx

    チュートリアル : データベースへのデータの保存 (複数テーブル)
    http://msdn.microsoft.com/ja-jp/library/4esb49b4.aspx

    2010年1月2日 4:35
  • ご回答ありがとうございます。

    どうも、私の記述が悪く、誤解を招いてしまったようなのですが、比較対象としているTimeStamp値は一番最初に取得した
    データのものを使っています。

    DBからDataSetにデータを取得した際に、TimeStampの列も同時に取得した場合、DataSetの内容をどのように変更しても
    TimeStampの列を取り込んだデータは変更されませんよね。

    ということで、この一番最初にDBから取得した際のTimeStampの値を比較対象とすれば、データの取得時と現在のDBの
    値が同じものが異なるのか判断ができますよね。

    一応、このような考えの下で作成はしております。

    人に文字だけで伝えるということは、非常に難しいですね。反省しています。

    SuferOnWss様にご教授いただいた、TableAdapterを試しているところなのですが、なかなか勝手がわからず、苦戦しております。
    もう少し、時間をいただければ何がしかの結果をお伝えすることができると思います。

    ひろぽん様
    ご提示のリンクを読ませていただきました。非常に興味深い内容ではありますが、現状、半分も理解できていないといったところです。
    思い切って、本を買ってしまいました。

    ロックに関してですが、これらは自分で何か指定をするものなのでしょうか。(一部そのような記載も見受けられましたが)
    ロックの種類、使い分けなどはあったのですが、具体的にどのような場合にどのような記述をすればよいか、といった点が
    よくわからず、現状、理解に努めているところです。

    今、自分がやっているのは、例えば、3個のテーブルを同時に更新する必要がある場合、全てのデータがDBから取得時と変更がなく
    一度に更新が可能な場合のみ、更新を実行するという方法を考えているのですが、このような考えはおかしいのでしょうか。


    トランザクションの開始

     1つ目のテーブルデータが取得時から更新されていないかチェック

      更新されていない場合は、更新し、更新可能フラグを立てる

     2つ目のテーブル以下も、上記と々事を行う

     3つのテーブル全てに対して、更新可能フラグが立った場合  →  トランザクション完了

     1つのテーブルでも更新可能フラグが立たない場合   →  ロールバック

    といったことを考えています。

    2010年1月2日 5:13
  • > もう少し、時間をいただければ何がしかの結果をお伝えすることができると思います。

    いえ、もう結構です。お好きなようにしてください。


    いままで、いろいろとアドバイスありがとうございました。
    2010年1月2日 11:33
  • どうも、私の記述が悪く、誤解を招いてしまったようなのですが、比較対象としているTimeStamp値は一番最初に取得した
    データのものを使っています。

    DBからDataSetにデータを取得した際に、TimeStampの列も同時に取得した場合、DataSetの内容をどのように変更しても
    TimeStampの列を取り込んだデータは変更されませんよね。

    ということで、この一番最初にDBから取得した際のTimeStampの値を比較対象とすれば、データの取得時と現在のDBの
    値が同じものが異なるのか判断ができますよね。

    一応、このような考えの下で作成はしております。

    timeStamp の値でデータが更新されているかどうか判定する必要はありません。

    通常 DataTable の各行が更新・追加・削除されているかどうかは、DataRow.RowState プロパティ で判定します。
    DataRowState 列挙体 のドキュメントも合わせてご覧下さい。

    また DataAdapter.Update メソッド は、RowState プロパティの状態を見て、更新・挿入・削除のどのコマンドを実行するか、自動で行います。
    以下、ドキュメントからの抜粋です。

    アプリケーションが Update メソッドを呼び出すと、DataAdapter は、RowState プロパティを調べ、DataSet に設定されているインデックスの順序に基づいて、要求された INSERT、UPDATE、または DELETE ステートメントを各行に対して反復的に実行します。たとえば、Update が呼び出されたときに、DataTable 内の行の順序に応じて、DELETE ステートメントを実行し、INSERT ステートメントを実行してから、別の DELETE ステートメントを実行することなどがあります。

    ちょっと判りにくいかもしれませんが、要は DataAdapter.Update メソッド を実行した場合、
    DataTable の各行ごとに RowState プロパティを調べ、

    1.DataRow.RowState = DataRowState.Added    なら InsertCommand  を実行。
    2.DataRow.RowState = DataRowState.Deleted  なら DeleteCommand  を実行。
    3.DataRow.RowState = DataRowState.Modified なら UpdateCommand を実行。
    4.DataRow.RowState = DataRowState.Unchanged なら何もしない。

    これを DataTable.Rows.Count の回数・・・すなわち行数分、反復実行することになります。

    2010年1月2日 13:13
    モデレータ
  • またトランザクションの話ですが、「楽観的同時実行制御」を意図して行わないんであれば、
    当面は頭から外しておいても構わないかと思います。

    実は DataAdapter.Update メソッド  はコネクションが接続されていなければ、

    1.自動でコネクションを接続
    2.トランザクションを開始
    3.データを更新
    4.全て正常に完了すればコミット
    5.例外が発生すればロールバック
    6.コネクションを閉じる

    これ等を自動で行ってくれます。

    参考URL
    http://d.hatena.ne.jp/gsf_zero1/20070826/p1

    でもコネクションの自動管理に関することって DataAdapter.Update メソッド のドキュメントに記述がないんですよね。
    これは片手落ちだと思う。
    2010年1月2日 13:28
    モデレータ
  • 皆さんはTableAdapterを使えとおっしゃってますが、実際にデータベースとやり取りしているとTableAdapterの自動生成では満足できなくなって、結局自分でCommandを操作するようになるでしょう。
    その時に今回悩んだことは無駄にならないのでがんばってください。
    ただしMSDNを調べる癖は付けておいてくださいね。

    実は私も TableAdapter 絶対派ではありません(苦笑

    スキルに応じて、もしくは仕事を楽にしたいとのことでしたので TableAdapter を使った方がいいと推奨しましたが、
    今のプロジェクトに於いては、TableAdapter を極力排除するようにしております。

    つーか、不特定多数の環境に配布するアプリケーションのため、
    接続文字列も動的に変更する必要があり、配布先によってはスキーマが変わる可能性があるため
    TableAdapter を使いたくても使えないんですよね。(^ω^;

    本も購入されたようですし、この際徹底的に ADO.NET や SQLServer について極められたいというなら
    それはまた大変素晴らしいことだと思います。

    2010年1月2日 13:59
    モデレータ
  • おはようございます。

    いろいろと資料を読み、皆様のご回答を拝読するに当たり、どうも私が考えていることは「楽観的同時実効制御」を行おうとしているようです。

    スタンドアロン環境にて、一人で利用するアプリケーションであれば、TableAdapterなどに取得したデータの更新は、TimeStampなどを
    考慮する必要はないとは思うのですが、複数名での利用を考える場合、DataSet内の更新ではなく、大元のDBのデータが更新されているかを
    確認する必要があるので、TimeStampを比較対象としております。

    TableAdapterのウィザードはわかりにくいですね。
    以前、試してあきらめたことのある、DataSetのウィザードによく似ており、苦手意識が先行して、かなりてこずっています。
    2010年1月2日 22:20