WebSurfer's Home

トップ > Blog 1   |   Login
Filter by APML

DbProviderFactory の利用

by WebSurfer 4. July 2023 17:33

ADO.NET の DbProviderFactory クラスDbConnection クラス, DbCommand クラスなどの抽象クラスを使ってデータベースにアクセスして操作するアプリの例を備忘録として書いておきます。

Windows Forms アプリ

上の画像は、SQLite のテーブルのレコード一覧を DataGridView に表示し、ユーザーがそれを見て編集した後、編集結果をデータベースに書き戻す .NET Framework 4.8 の Windows Forms アプリです。

対象としたデータベースは以下の内容の SQLite の Movie テーブルです。

SQLite の Movie テーブル

普通に作ると、SQLite 専用の SQLiteConnection, SQLiteCommand などを使うと思いますが、それらに代えて DbConnection, DbCommand などの抽象クラスと、DbProviderFactory の CreateConnection、CreateCommand メソッドなど使うようにします。

そうすると何のメリットがあるのかと言うと、例えば SQLite を SQL Server に変更する場合、ハードコーディングした SQLiteConnection, SQLiteCommand などを SqlConnection, SqlCommand などに書き換える必要はなく、app.config の接続文字列だけを SQL Server 用に書き換えれば移行できます。

まず DbProviderFactory の登録を行う必要がありますが、SQLite の場合は NuGet から System.Data.SQLite をインストールすると app.config に以下の DbProviderFactories 要素が追加されるのでこれを利用します。

<DbProviderFactories>
  <remove invariant="System.Data.SQLite.EF6" />
  <add name="SQLite Data Provider (Entity Framework 6)" 
    invariant="System.Data.SQLite.EF6" 
    description=".NET Framework Data Provider for SQLite (Entity Framework 6)" 
    type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
  <remove invariant="System.Data.SQLite" />
  <add name="SQLite Data Provider" 
    invariant="System.Data.SQLite" 
    description=".NET Framework Data Provider for SQLite" 
    type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>

接続文字列とプロバイダ名も app.config から取得できるように、以下のように connectionStrings 要素を設定しておきます。

<connectionStrings>
  <add name="ConnectionInfo"
    connectionString="SQLite 用の接続文字列"
    providerName="System.Data.SQLite"/>
</connectionStrings>

Visual Studio のデザイン画面で、ツールボックスから Form に Button 2 つ、DataGridView、BindingSource をドラッグ&ドロップした後、以下のコードを記述します。コメントアウトしたコードが SQLiteConnection, SQLiteCommand, SQLiteDataAdapter, SQLiteCommandBuilder などを使ったもので、その下が DbProviderFactory を使用したものです。下のコードを実行した結果がこの記事の一番上の画像です。

using System;
using System.Data;
using System.Data.SQLite;
using System.Windows.Forms;
using System.Configuration;
using System.Data.Common;
using System.Configuration.Provider;

namespace WindowsFormsSQLite
{
    public partial class Form1 : Form
    {
        //private SQLiteDataAdapter adapter;
        // ↓↓↓
        private DbDataAdapter adapter;

        private DataTable table;

        public Form1()
        {
            InitializeComponent();

            this.dataGridView1.DataSource = this.bindingSource1;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var connString = ConfigurationManager
                             .ConnectionStrings["ConnectionInfo"]
                             .ConnectionString;            
            var selectQuery = 
                "SELECT Id, Title, ReleaseDate, Genre, Price FROM Movie";

            //var connection = new SQLiteConnection(connString);
            //var command = new SQLiteCommand(selectQuery, connection);
            //this.adapter = new SQLiteDataAdapter();
            //this.adapter.SelectCommand = command;
            //_ = new SQLiteCommandBuilder(this.adapter);
            // ↓↓↓
            var providerName = ConfigurationManager
                               .ConnectionStrings["ConnectionInfo"]
                               .ProviderName;
            var factory = DbProviderFactories.GetFactory(providerName);
            var connection = factory.CreateConnection();
            connection.ConnectionString = connString;
            var command = connection.CreateCommand();
            command.CommandText = selectQuery;
            command.Connection = connection;
            this.adapter = factory.CreateDataAdapter();
            this.adapter.SelectCommand = command;
            var builder = factory.CreateCommandBuilder();
            builder.DataAdapter = this.adapter;

            this.table = new DataTable();
            this.adapter.Fill(this.table);
            this.bindingSource1.DataSource = this.table;

            this.components.Add(connection);
            this.components.Add(command);
        }
               
        private void Update_Click(object sender, EventArgs e)
        {            
            this.adapter.Update(this.table);
        }

        private void Remove_Click(object sender, EventArgs e)
        {
            this.bindingSource1.RemoveCurrent();
        }
    }
}

次に、SQLite のテーブルを下の画像の SQL Server のテーブルに変更する場合、どのようにするかを書きます。

SQL Server の Movie テーブル

System.Data.SqlClient を使う場合、そのプロパイダ情報は machine.config に登録済みのはずです。もし、登録されてなければ app.config の DbProviderFactories 要素に以下のように SQL Server 用のプロパイダ情報を追加してください。

<remove invariant="System.Data.SqlClient" />
<add name="SqlClient Data Provider"
  invariant="System.Data.SqlClient"
  description=".Net Framework Data Provider for SqlServer"
  type="System.Data.SqlClient.SqlClientFactory, System.Data,
    Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

あとは、以下のように接続文字列とプロバイダ名の設定を SQL Server 用に差し替えるだけで済みます。connectionString のみでなく、providerName も SQL Server 用に変更しているところに注意してください。

<connectionStrings>
  <add name="ConnectionInfo"
    connectionString="SQL Server 用の接続文字列"
    providerName="System.Data.SqlClient"/>
</connectionStrings>

その上でアプリを実行すれば、SQL Server に接続されて以下の画像の通り Movie テーブルのレコード一覧が表示され、編集操作も同様に可能になります。

Windows Forms アプリ

SQLite と SQL Server のテーブルにはデータの型の違いがありますが、DbDataAdapter と DataTable を使う非接続型のアプリの場合は、その違いはプロバイダと DataTable が吸収してくれるようです。

ただ、上のようなことを考えなくても、SQL Server の場合は Visual Studio のデータソース構成ウィザードが利用できますので、それを使って作り直した方が簡単かつ確実かもしれません。ドラッグ&ドロップ操作だけで自力では一行もコードを書かずにアプリを作成できますので。


【2023/7/5 追記】

プロバイダを、System.Data.SqlClient に代えて Microsoft.Data.SqlClient とする場合について以下に追記します。

Microsoft.Data.SqlClient 用のプロバイダは machine.config には登録されてないので、Microsoft のドキュメント「SqlClientFactory の取得」に書いてあるように、app.config に DbProviderFactory の登録を行う必要があります。以下の通りです。

<add name="Microsoft SqlClient Data Provider" 
  invariant="Microsoft.Data.SqlClient" 
  description="Microsoft SqlClient Data Provider for SQL Server" 
  type="Microsoft.Data.SqlClient.SqlClientFactory, Microsoft.Data.SqlClient, 
        Version=5.0.0.0, Culture=neutral, PublicKeyToken=23ec7fc2d6eaa4a5" />

上の Version は、使用する Microsoft.Data.Sqlclient のバージョンと合わせる必要があるので注意してください。Microsoft.Data.Sqlclient は NuGet からインストールしますが、インストール後 Visual Studio のソリューションエクスプローラーの「参照」に Microsoft.Data.Sqlclient が追加されるので、そのバージョンに合わせてください。

あとは、接続文字列の設定の内 providerName を Microsoft.Data.Sqlclient 用に変更すれば OK です。

<add name="ConnectionInfo" 
  connectionString="SQL Server 用の接続文字列" 
  providerName="Microsoft.Data.SqlClient" />

Tags: , , , ,

ADO.NET

SQLite で Movie チュートリアル (CORE)

by WebSurfer 10. October 2021 14:50

データベースに SQLite を利用して、Micorsoft の ASP.NET Core MVC のチュートリアル「ASP.NET Core MVC の概要」および「パート 4、ASP.NET Core MVC アプリにモデルを追加する」に従ってアプリを作る方法を書きます。

Movie アプリ

先の記事「MySQL で Movie チュートリアル (CORE)」ではデータベースに MySQL を使った例を書きましたが、この記事はその SQLite 版です。

(1) プロジェクトの作成

チュートリアル「ASP.NET Core MVC の概要」の通り、Visual Studio 2019 のテンプレートを利用して対象のフレームワークは .NET 5.0認証「なし」の ASP.NET Core MVC アプリを作成します。

対象のフレームワークは .NET Core 3.1 でも良いですが、そうした場合は NuGet パッケージのバージョンの選び方に注意してください。間違うとスキャフォールディングでエラーになると思います。

また、認証を「個別のアカウント」にすると SQL Server を利用した Entity Framework 関係のパッケージがインストールされ話がややこしくなりますので、まずは認証は「なし」でやってみることをお勧めします。

(2) モデルの定義とスキャフォールディングの実行

チュートリアル「パート 4、ASP.NET Core MVC アプリにモデルを追加する」に従って、モデル(エンティティ)クラスの定義をプロジェクトに既存の Models フォルダに追加します。

using System;
using System.ComponentModel.DataAnnotations;

namespace SQLiteMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

次に、Microsoft.EntityFrameworkCore.Design を NuGet からインストールします。この記事では、この記事を書いた時点での最新版 5.0.10 を使いました。対象のフレームワークを .NET Core 3.1 にした場合は、Microsoft.EntityFrameworkCore.Design のバージョンは 3.1.18 にするのが良さそうです。5.0.10 を使うとスキャフォールディングでエラーになると思います。

チュートリアルの通り Visual Studio でスキャフォールディングを実行すると以下の操作が自動的に行われます。(コードジェネレータ関係のエラーが出る場合がありますが、その際は下の追加 NuGet パッケージがインストールされていることを確認してから再度スキャフォールディングを行うと成功すると思います)

  1. 他に必要な NuGet パッケージ(Microsoft.EntityFrameworkCore.SqlServer, Microsoft.EntityFrameworkCore.Tools, Microsoft.VisualStudio.Web.CodeGeneration.Design)の追加
  2. Data フォルダにコンテキストクラスの作成
  3. Startup.cs ファイルの ConfigureServices メソッドにコンテキストの登録
  4. appsettings.json ファイルへの接続文字列の追加
  5. CRUD 操作に必要な Controller / View 一式の生成

SQL Server の場合は上記でプロジェクトは完成ですが、SQLite を利用する場合は上の 1, 3, 4 に以下の変更を行う必要があります。

(3) NuGet パッケージの変更

スキャフォールディングで自動的に追加された NuGet パッケージ Microsoft.EntityFrameworkCore.SqlServer はこの先の操作には無くても問題ないです。残しておいても問題はありませんが、無くてもアプリは作成できることを確認するため削除してみました。 代わりに Microsoft.EntityFrameworkCore.Sqlite をインストールします。その結果が以下の画像です。

NuGet パッケージ

スキャフォールディングで自動的に追加される Microsoft.EntiryFrameworkCore.Tools のバージョンがランタイムのバージョン(この記事を書いた時点では 5.0.10)より古い場合は Migration 操作の際警告が出るのと思いますので、更新してください。

注意: Microsoft.EntityFrameworkCore.SqlServer はモデルクラスの定義によってはスキャフォールディングで必要になるケースがあるようです。具体的にどのようなケースで必要になるかは調べ切れてませんが、データアノテーション属性が関係しているような感じです。なので、この先モデルの定義を変更して Migration ⇒ スキャフォールディング操作を繰り返すなら、Microsoft.EntityFrameworkCore.SqlServer は残しておいた方が良さそうです。

(4) Startup.cs ファイルの修正

スキャフォールディング操作で Startup.cs ファイルの ConfigureServices メソッドに自動的にコンテキストが登録されますが、それは SQL Server 用なので、以下のように UseSqlServer を UseSqlite に変更します。

UseSqlServer を UseSqlite に変更

コンテキストクラスの登録はコントローラーへの DI に必要です。登録してあれば、フレームワークがクライアントからの要求を受けてコントローラーを初期化する際、コンテキストクラスを初期化してコンストラクタ経由で渡してくれます。

(5) 接続文字列を SQLite 用に変更

スキャフォールディング操作で appsettings.json ファイルに接続文字列が自動生成されますが、それは SQL Server (LocalDB) 用なので SQLite 用に変更します。

接続文字列の変更

上の画像の接続文字列では、プロジェクトのフォルダに Movie.db という名前でデータベースファイルを置くように設定しました。この時点ではデータベースファイルは存在しませんが、 Migration 操作を行うと EF Code First の機能を使って Movie.db というデータベースファイルを新たに生成し、そこに必要なテーブルを生成してくれます。

(6) Add-Migration の実行

ソリューションをリビルドしてから、Visual Studio のパッケージマネージャーコンソールで Add-Migration InitialCreate コマンドを実行します。結果、以下の画像の通り InitialCreate クラスが Migrations フォルダに自動生成されます。

Add-Migration の実行結果

Movie クラスの ReleaseDate プロパティ、Price プロパティの型はそれぞれ DateTime、deciaml ですが、SQLite にはそれらに該当する型がないので type: "TEXT" となっている点に注目してください。

Add-Migration InitialCreate の InitialCreate という名前は任意に指定できます。指定した名前で xxxxx_InitialCreate.cs (xxxxx は作成日時) という名前のファイルが作成され、それに InitialCreate という名前のクラスが定義されます。

(7) Update-Database の実行

Visual Studio のパッケージマネージャーコンソールで Update-Database コマンドを実行します。これにより以下の画像の通り SQLite データベースが生成されます。

Update-Database の実行結果

接続文字列の設定通り Movie.db という名前でデータベースファイルが生成され、InitialCreate クラスの name: "Movie" で指定された名のテーブルが生成されています。

Movie クラスの ReleaseDate プロパティ、Price プロパティに該当する Movie テーブルのフィールドの型は、上のコードの InitialCreate クラスの指定通り TEXT となっています。

(8) プロジェクトの実行

上記 (7) まででアプリは完成です。プロジェクトを実行して Create 画面を表示して 2 つレコードを追加し、Index 画面でその一覧を表示したのがこの記事の一番上の画像です。

SQLite の Movie テーブルにも追加結果が反映されています。

レコードの Create 結果

Movie クラスの ReleaseDate プロパティ、Price プロパティの型はそれぞれ DateTime、deciaml で、SQLite の当該フィールドの型は TEXT ですが、フレームワークが型変換をしてくれているようです。(どこでどのように変換しているかは不明です。今後の検討課題ということで)

Tags: , , , ,

CORE

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  July 2024  >>
MoTuWeThFrSaSu
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

View posts in large calendar