【C#】DataAdapterを使ってFillとUpdateしてみる

みなさんこんにちは!ヒロポンです!

今回はアプリ制作と切っては切り離せない関係にあるDatabaseからデータを取得する方法の中から、DataAdapterを使ったDataTableを取得する方法について書いていきたいと思います!

割とベーシックな取得方法なので、是非この機会に覚えてくれればと思います!

では早速

DataAdapter の全フロー: 接続 / Fill / RowState 4分岐 / Transaction
目次

参考コード

💡 DataAdapter.Update で DBNull 例外に詰まった場合は、別記事 C# DataAdapter.Update() で DBNull 例外が出た時の最短対処 で対処パターンを書いてます。本番障害になりがちな罠なので押さえとくと安心。

コードの全貌

    public partial class Form2 : Form
    {
        private SqlConnection SqlCon;
        private SqlDataAdapter adapter;
        private SqlCommandBuilder commandBuilder;
    public Form2()
    {
        InitializeComponent();
    }

    private void Form2_Load(object sender, EventArgs e)
    {
        SqlSettingInitialize();
    }

    private void SqlSettingInitialize()
    {
        var sqlConStrBuilder = new SqlConnectionStringBuilder();
        sqlConStrBuilder.InitialCatalog = @"testDb";
        sqlConStrBuilder.DataSource = @"DESKTOP-B9V2BO1";
        sqlConStrBuilder.UserID = "sa";
        sqlConStrBuilder.Password = "pass";
        sqlConStrBuilder.ConnectTimeout = 10;

        var selectQuery = @"select * from dbo.test";

        SqlCon = new SqlConnection(sqlConStrBuilder.toString());
        adapter = new SqlDataAdapter(selectQuery, SqlCon);
        commandBuilder = new SqlCommandBuilder(adapter);
        adapter.UpdateCommand = commandBuilder.GetUpdateCommand();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        var dt = new DataTable();
        SqlCon.Open();
        adapter.Fill(dt);
        SqlCon.Close();
        dataGridView1.DataSource = dt;
    }

    private void button2_Click(object sender, EventArgs e)
    {
        var updateDt = (DataTable) dataGridView1.DataSource;
        SqlCon.Open();
        adapter.Update(updateDt);
        SqlCon.Close();
    }
}</code></pre></div>

Formロード時にインスタンスを生成

dataAdapterを使ってFillとUpdateを行うのに重要なのは、同じインスタンスを使わないといけないということ。

なので、Formにインスタンスを持たせておきます。

    public partial class Form2 : Form
    {
        private SqlConnection SqlCon;
        private SqlDataAdapter adapter;
        private SqlCommandBuilder commandBuilder;

またフォームロード時にインスタンスをまとめて生成します。

        private void SqlSettingInitialize()
        {
            var sqlConStrBuilder = new SqlConnectionStringBuilder();
            sqlConStrBuilder.InitialCatalog = @"testDb";
            sqlConStrBuilder.DataSource = @"DESKTOP-B9V2BO1";
            sqlConStrBuilder.UserID = "sa";
            sqlConStrBuilder.Password = "pass";
            sqlConStrBuilder.ConnectTimeout = 10;
        var selectQuery = @&quot;select * from dbo.test&quot;;

        SqlCon = new SqlConnection(sqlConStrBuilder.toString());
        adapter = new SqlDataAdapter(selectQuery, SqlCon);
        commandBuilder = new SqlCommandBuilder(adapter);
        adapter.UpdateCommand = commandBuilder.GetUpdateCommand();
    }</code></pre></div>

Fill

データを取得したいときはFillで取得します。

        private void button1_Click(object sender, EventArgs e)
        {
            var dt = new DataTable();
            SqlCon.Open();
            adapter.Fill(dt);
            SqlCon.Close();
            dataGridView1.DataSource = dt;
        }

Update

更新はそのままUpdateメソッドを使います。

        private void button2_Click(object sender, EventArgs e)
        {
            var updateDt = (DataTable) dataGridView1.DataSource;
            SqlCon.Open();
            adapter.Update(updateDt);
            SqlCon.Close();
        }

重要なのはインスタンスを破棄しないこと

今回のソースで重要なのはAdapterクラスのインスタンスを破棄しないことです。

usingとかを使って破棄してしまうと、データ取得時のインスタンスと、update時のインスタンスが別々になるので、実行時エラーではじかれてしまいます!

お気を付けください!

💡 補足: 業務系の現場でよくハマるパターン

俺もこの DataAdapter の Fill / Update パターン、過去案件で何度もやりましたが、ハマるところは大体決まってます。3つ並べておきます。

① Update 時の DBNull 例外で本番障害

DataTable 経由で INSERT/UPDATE するとき、NULL 値を持つカラムで SqlException: Column does not allow nullsConstraintException が出るパターン。原因は DataColumn 側の制約設定や、 DBNull.Valuenull の混在。詳細対処は DataAdapter.Update DBNull 例外の最短対処 でコード付きでまとめてます。

② PrimaryKey 設定忘れで Update が効かない

DataTable に PrimaryKey を明示しないと、 DataAdapter.Update が「どの行が更新行か」を判定できず、新規 INSERT 扱いになります。Fill した DataTable は自動で PrimaryKey が設定されないので、必ず table.PrimaryKey = new[] { table.Columns["id"] } を呼びます。これも初学者の頻出罠です。

Update リファクタ: foreach 個別 SqlCommand → adapter.Update + Transaction で一括

③ 大量行 Update で Transaction を明示しないとパフォーマンスが死ぬ

DataAdapter.Update はデフォルトで1行ずつ別 Transaction として実行されます。1万件 Update するときに 1万回の commit が走る。これは Transaction を 明示的にラップする必要あり: SqlConnection.BeginTransaction() で開始 → DataAdapter.SelectCommand.Transaction = tx でアタッチ → Update → Commit。性能差は10倍以上出ます。 DataReader vs DataAdapter — メモリ消費と性能の使い分け で性能比較してます。

❓ よくある質問

Q1. Insert / Update / Delete のコマンドを別々に作る必要はありますか?

A. SqlCommandBuilder を使えば自動生成してくれます。 new SqlCommandBuilder(adapter) で 4種のコマンドが揃う。ただし複雑なクエリや JOIN 入り SELECT だと自動生成が失敗するので、業務系では手動で書くケースも多いです。手動の場合は SELECT 文と PrimaryKey が肝。

Q2. 大量データの BulkInsert は DataAdapter でできますか?

A. DataAdapter.Update は単行ベースなので 万件規模だと不向き。 SqlBulkCopy を使うのが定石です。DataTable をそのまま bulk.WriteToServer(table) で送れる。10万行が数秒で入るレベル。バッチ系の処理で必須のクラスです。

Q3. DataTable と DataSet の使い分けは?

A. 1テーブル分のデータなら DataTable で十分。複数テーブル + リレーションシップ持つなら DataSet。業務系は単一 DataTable で完結することが多いので、 DataSet はあまり使いません。 DataGridView と DataTable でも DataTable 単独で扱う前提で書いてます。

Q4. Fill のパフォーマンスを改善したい

A. 3つ効果的な手段があります。(1)SELECT 文で必要列だけ取得( SELECT * 禁止)、(2) BeginLoadData / EndLoadData で DataTable のイベント発火を抑制、(3)大量レコードなら DataReader 直叩きに切替。読み込み専用なら DataReader の方が早い。 DataReader vs DataAdapter で判断軸まとめてます。

Q5. EF Core への移行を検討してます。判断基準は?

A. 新規プロジェクトなら EF Core 一択。既存の DataAdapter ベースの保守案件は、置き換えコストが大きいので「機能追加部分から EF Core に寄せる」のが現実解です。Form 間の値受け渡しは FormとFormの間で値の共有・受け渡し の DataTable パターンを参考に、DTO を共通化しておくと EF Core 移行時にスムーズです。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメント一覧 (2件)

  • 私は本当の初心者です。上記のコードを学ばねばと見よう見まねで、C#にコードを書いています。そこで上記のコードの中で
    SqlCon = new SqlConnection(SqlSetting.ConnectionString);の SqlSetting の文字列でエラーが発生するので教えていただけますでしょうか。お忙しいところ恐縮です。個人的にですが、データベースプログラムに興味がありますが、独学・高年齢でなかなか理解が追いつきませんが、好きです。今後ともよろしくお願いいたします。

    • コメントありがとうございます!
      最近放置気味だったため、返信が遅れてしまいすみません。。。

      こちらコードを確認したところ、
      SqlCon = new SqlConnection(SqlSetting.ConnectionString);
      は誤りですね!
      SqlCon = new SqlConnection(sqlConStrBuilder.toString());
      でコンパイルエラーは消えると思われます!!

      高年齢で独学でもプログラミングができるようになった方はいるので、頑張ってください!!

コメントする

CAPTCHA


目次