いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

ADO.NET入門記-010 (CommandBuilderの使用(Insert,Update,Delete) (CommandBuilder, DataAdapter, Fill, Update))

前回は、手動で各変更系のコマンドオブジェクトを作成していましたが、ADO.NETにはCommandBuilderという
クラスが存在しており、それを使用することにより以下の効果を得ることが出来ます。

SelectCommandのみを作成し、それ以外の変更系のコマンドについてはCommandBuilderに自動生成させる。

ただし、上記の方法にもいくつかの制限があります。
CommandBuilderが各変更系のコマンドを自動生成するには、以下の条件を満たさないといけません。

  1. クエリがただ一つのテーブルからデータを取得するようになっていること。
  2. そのテーブルに主キーが設定されていること。
  3. その主キーのカラムがクエリに設定されていること。


つまり、複数テーブルをJOINしているようなクエリ、または主キーが存在しないテーブルなどに
関しては、各変更系コマンドは自動生成されません。あくまでも1テーブルに対してのクエリの場合に対して
CommandBuilderは対応します。


で、使い方ですがCommandBuilderはDataAdapterと密に関連するので、インスタンスを作成後
DataAdapterプロパティに、使用するデータアダプタオブジェクトを設定し、双方を関連付け
ます。後は、普通にデータアダプタを使えばオッケイです。内部で関連付いているので
自動的にコマンドオブジェクトが生成されます。CommandBuilderは、動作する際に
関連対象のデータアダプタのSelectCommandを利用しますので、予めSelectCommandを設定する
事が必要になります。

using(DbCommand command = conn.CreateCommand){
    //
    // selectクエリを設定.
    //
    command.CommandText = "select id, name, age from xxxx";

    using(DbDataAdapter adapter = factory.CreateDataAdapter()){

        adapter.SelectCommand = command;

        //
        // CommandBuilderを作成し、データアダプタと関連付ける。
        //
        DbCommandBuilder builder = factory.CreateCommandBuilder();
        builder.DataAdapter = adapter;

        //
        // 通常通り、アダプタを用いて処理を行なう。
        //
    }
}

尚、関連付けを行なえば良いだけなので、実はインスタンスを保持する必要も実はありません。
以下のようにも出来ます。

factory.CreateCommandBuilder().DataAdapter = adapter;

以下、サンプルです。
使用しているテーブルは、前回と同じテーブルとなっています。

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Transactions;

using NUnit.Framework;

using Gsf.Lib.UnitTests;

namespace Gsf.Samples.AdoNet {

    [TestFixture()]
    public class AdoNetSample011 : TestBase{

        [Test()]
        public void コマンドビルダを使用してコマンドオブジェクトを構築してみる(){

            using(TransactionScope tx = new TransactionScope()){

                DbProviderFactory factory = DbProviderFactories.GetFactory(_settings.ProviderName);
                using(DbConnection conn = factory.CreateConnection()){

                    conn.ConnectionString = _settings.ConnectionString;
                    conn.Open();

                    using(DbCommand command = conn.CreateCommand()){

                        command.CommandText = "select id, name, age from AdoNetSample009Table";

                        using(DbDataAdapter adapter = factory.CreateDataAdapter()) {

                            adapter.SelectCommand = command;

                            //
                            // DbCommandBuilderオブジェクトを作成し、アダプターにディスパッチする。
                            // 今回は、どのようなSQLが作成されるかのサンプルなので明示的にインスタンスを
                            // 作成するが、実際には、アダプターに関連付けられれば良いので以下のように書いても良い。
                            //
                            // factory.CreateCommandBuilder().DataAdapter = adpater
                            //
                            DbCommandBuilder builder = factory.CreateCommandBuilder();
                            builder.DataAdapter = adapter;

                            //
                            // どのようなSQLが作成されているかの確認.
                            // GetXXXXCommandメソッドにtrueを渡すと、内部で生成されるパラメータ名が出来る限り
                            // 実際のカラム名から作成されるようになる。falseを指定すると
                            // パラメータ名が@1,@2のようになる。
                            //
                            Console.WriteLine(string.Format("INSERT-SQL:{0}", builder.GetInsertCommand(true).CommandText));
                            Console.WriteLine(string.Format("UPDATE-SQL:{0}", builder.GetUpdateCommand(true).CommandText));
                            Console.WriteLine(string.Format("DELETE-SQL:{0}", builder.GetDeleteCommand(true).CommandText));

                            //
                            // 対象テーブルのスキーマ情報を取得し、データテーブルを構築.
                            //
                            DataTable table = new DataTable("AdoNetSample009Table");
                            adapter.FillSchema(table, SchemaType.Source);

                            /////////////////////////////////////////////////////////////////////
                            //
                            // Insertをしてみる.
                            //
                            DataRow newRow = table.NewRow();
                            newRow["id"] = 999;
                            newRow["name"] = "gsf_zero999";
                            newRow["age"] = 99;
                            table.Rows.Add(newRow);

                            adapter.Update(table);

                            //
                            // 結果確認
                            //
                            table.Clear();
                            adapter.Fill(table);
                            Assert.AreEqual(1, table.Select("id = 999").Length);

                            /////////////////////////////////////////////////////////////////////
                            //
                            // updateをしてみる.
                            //
                            adapter.Fill(table);
                            DataRow updateTargetRow = table.Select("id = 999")[0];
                            updateTargetRow["name"] = "gsf_zero_updated";

                            adapter.Update(table);

                            //
                            // 結果確認
                            //
                            table.Clear();
                            adapter.Fill(table);
                            Assert.AreEqual(1, table.Select("name = 'gsf_zero_updated'").Length);

                            /////////////////////////////////////////////////////////////////////
                            //
                            // updateをしてみる.
                            //
                            adapter.Fill(table);
                            table.Select("id = 999")[0].Delete();

                            adapter.Update(table);

                            //
                            // 結果確認
                            //
                            table.Clear();
                            adapter.Fill(table);
                            Assert.AreEqual(0, table.Select("id = 999").Length);

                            // データが登録されると面倒なのでわざとロールバックさせる。
                            //tx.Complete();

                        } // END using(DbDataAdapter adapter = factory.CreateDataAdapter()){

                    } // END using(DbCommand command = conn.CreateCommand()){

                } // END using(DbConnection conn = factory.CreateConnection()){

            } // END using(TransactionScope tx = new TransactionScope()){
           
        } // END METHOD

    }

}


実行すると、以下のように表示されます。

INSERT-SQL:INSERT INTO [AdoNetSample009Table] ([id], [name], [age]) VALUES (@id, @name, @age)
UPDATE-SQL:UPDATE [AdoNetSample009Table] SET [id] = @id, [name] = @name, [age] = @age WHERE *1
DELETE-SQL:DELETE FROM [AdoNetSample009Table] WHERE *2


実際には、SelectCommandしかセットしていないのに、各変更系のコマンドがCommandBuilderによって生成されています。

*1:[id] = @Original_id) AND ([name] = @Original_name) AND ([age] = @Original_age

*2:[id] = @Original_id) AND ([name] = @Original_name) AND ([age] = @Original_age