2019-06-25に投稿

【WPF】PostgreSQL に接続してデータを取得して表示する

読了目安:14分

おはようございます。

今回は、PostgreSQLを使ってデータを検索、データグリッドに表示してみます。
お決まりですが、プログラムは前回までのものを流用します。

【WPF】ProgressRingを使って時間のかかる処理を分かりやすくする

また、PostgreSQLをインストールしていない場合は次の記事を参照して、パソコンにインストールしてください。

PostgreSQL 9.6.3 をインストールしてテーブルを作成する

Nuget でパッケージをダウンロード

NuGetパッケージ管理を開く

ソリューションエクスプローラーからプロジェクトを選択、右クリックし
「Nuget パッケージの管理」を選択

NuGetパッケージ管理

Nuget パッケージ管理画面が表示されるので、
検索窓に「Npgsql 6」を入力し、「EntityFramework6.Npgsql」を選択、
インストールボタンをクリックします。

プレビュー画面

プレビュー画面が表示される場合は「OK」ボタンをクリックします。

出力ビュー

出力ビューに「終了」が表示されれば完了です。

スキーマの作成

PostgreSQL ではスキーマ単位の管理をしておかないと困ることになります。

スキーマ指定しないでテーブル作成すると「public」スキーマに作成されるのですが、
C#からの接続時には必ず(やってみた限りでは)スキーマ指定しないといけないので、まずはスキーマを作成しておきます。

CREATE SCHEMA dora;

テーブルの作成

作成したスキーマににテーブルを作成
SQLite の時と同じテーブル、データを用意します。

このとき、テーブル名やカラム名は小文字にしておくこと。

CREATE TABLE IF NOT EXISTS dora.mstkind (
        kind_cd CHAR(2) NOT NULL
        , kind_name VARCHAR(20)
        , primary key (kind_cd)
);


CREATE TABLE IF NOT EXISTS dora.tblcat (
        no INTEGER NOT NULL
        , name VARCHAR(20) NOT NULL
        , sex CHAR(3) NOT NULL
        , age INTEGER DEFAULT 0 NOT NULL
        , kind_cd CHAR(2) DEFAULT '00' NOT NULL
        , favorite VARCHAR(40)
        , PRIMARY KEY (no)
);

INSERT INTO DORA.MSTKIND VALUES ('01', 'キジトラ');
INSERT INTO DORA.MSTKIND VALUES ('02', '長毛種(不明)');
INSERT INTO DORA.MSTKIND VALUES ('03', 'ミケ(っぽい)');
INSERT INTO DORA.MSTKIND VALUES ('04', 'サビ');
INSERT INTO DORA.MSTKIND VALUES ('09', 'その他');
INSERT INTO DORA.TBLCAT VALUES('1','そら','♂','6','01','犬の人形');
INSERT INTO DORA.TBLCAT VALUES('2','りく','♂','5','02','人間');
INSERT INTO DORA.TBLCAT VALUES('3','うみ','♀','4','03','高級ウェットフード');
INSERT INTO DORA.TBLCAT VALUES('4','こうめ','♀','2','04','横取りフード');

設定ファイルの修正

次のようにします。
重要な部分は、「entityFramework」タグと「system.data」タグ。

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v13.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="Npgsql" type="Npgsql.NpgsqlServices, EntityFramework6.Npgsql" />
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <remove invariant="Npgsql" />
      <add name="Npgsql Data Provider" invariant="Npgsql" support="FF" description=".Net Framework Data Provider for Postgresql" type="Npgsql.NpgsqlFactory, Npgsql" />
    </DbProviderFactories>
</system.data>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Npgsql" publicKeyToken="5d8b90d52f46fda7" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.2.5.0" newVersion="3.2.5.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

プログラム修正

新規クラスの追加

DbContextを継承したクラスを作成します。
このクラスでデータベースへの接続、Entityへのマッピングなどを行います。
デフォルトのスキーマを変更する場合、「OnModelCreating」メソッドにて設定可能です。

PgDbContext.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Data.Entity;
using Npgsql;

namespace WpfApp1
{
    class PgDbContext : DbContext
    {
        private const string ConnectionString = "Server=localhost;User ID=USER01;Password=USER01;Database=DB01;port=5432";

        // コンストラクタにて接続文字列を設定
        public PgDbContext() : base(new NpgsqlConnection(ConnectionString), true) { }

        public DbSet<Kind> Kinds { get; set; }
        public DbSet<Cat> Cats { get; set; }

        // スキーマを変更する場合にはここに設定
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //Configure default schema
            modelBuilder.HasDefaultSchema("dora");
        }
    }
}

Entityクラスの修正

アノテーションが今までのとちょっと違うので修正します。

Kind.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace WpfApp1
{
    [Table("mstkind")]
    class Kind
    {
        [Key]
        [Column("kind_cd")]
        public String KindCd { get; set; }
        [Column("kind_name")]
        public String KindName { get; set; }
    }
}

Cat.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace WpfApp1
{
    [Table("tblcat")]
    class Cat
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Column("no")]
        public int No { get; set; }
        [Column("name")]
        public String Name { get; set; }
        [Column("sex")]
        public String Sex { get; set; }
        [Column("age")]
        public int Age { get; set; }
        [Column("kind_cd")]
        public String Kind { get; set; }
        [Column("favorite")]
        public String Favorite { get; set; }
    }
}

宣言の追加

MainWindow.xaml.cs

using Npgsql;
using System.ComponentModel;


接続処理の変更

テーブル作成以外、全ての箇所を変更します。

MainWindow.xaml.cs

//using (var conn = new SQLiteConnection("Data Source=SampleDb.sqlite"))
using (var context = new PgDbContext())

テーブル作成は今まで通りコマンドで行います。
(また別途、DbContext でのやり方は紹介します)

テーブル作成クエリの変更

SQLite とは微妙に違うところ(型、長さ)を変更。

MainWindow.xaml.cs

// テーブルが存在しなければ作成する
// 種別マスタ
StringBuilder sb = new StringBuilder();
sb.Append("CREATE TABLE IF NOT EXISTS mstkind (");
sb.Append(" kind_cd CHAR(2) NOT NULL");
sb.Append(" , kind_name VARCHAR(20)");
sb.Append(" , PRIMARY KEY (kind_cd)");
sb.Append(")");
command.CommandText = sb.ToString();
command.ExecuteNonQuery();

// 猫テーブル
sb.Clear();
sb.Append("CREATE TABLE IF NOT EXISTS tblcat (");
sb.Append(" no INTEGER NOT NULL");
sb.Append(" , name VARCHAR(20) NOT NULL");
sb.Append(" , sex CHAR(3) NOT NULL");
sb.Append(" , age INTEGER DEFAULT 0 NOT NULL");
sb.Append(" , kind_cd CHAR(2) DEFAULT '00' NOT NULL");
sb.Append(" , favorite VARCHAR(40)");
sb.Append(" , PRIMARY KEY (no)");
sb.Append(")");

command.CommandText = sb.ToString();
command.ExecuteNonQuery();

検索処理の修正

MainWindow.xaml.cs

        /// <summary>
        /// 検索処理(非同期)
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SearchProcess(object sender, DoWorkEventArgs e)
        {
            // 猫データマスタを取得してコンボボックスに設定する
            //using (var conn = new SQLiteConnection("Data Source=SampleDb.sqlite"))
            // PgDbContext に変更
            using (var context = new PgDbContext())
            {
                String searchName = this.search_name.Text;
                String searchKind = (this.search_kind.SelectedValue as Kind).KindCd;

                // データを取得
                // context から DbSet を取得
                //Table<Cat> tblCat = con.GetTable<Cat>();
                var tblCat = context.Cats;

                // サンプルなので適当に組み立てる
                IQueryable<Cat> result;
                if (searchKind == "")
                {
                    // 名前は前方一致のため常に条件していしても問題なし
                    result = from x in tblCat
                             where x.Name.StartsWith(searchName)
                             orderby x.No
                             select x;
                }
                else
                {
                    result = from x in tblCat
                             where x.Name.StartsWith(searchName) &amp; x.Kind == searchKind
                             orderby x.No
                             select x;

                }
                this.dataGrid.ItemsSource = result.ToList();
            }
        }

コンバーターの修正

データグリッドに表示する際に値を変換している箇所も、DB接続しているため変更します。

KindConverter.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Data.Linq;
using System.Data.SQLite;
using System.Data.SQLite.Linq;

// Converter 用
// IValueConverter、CultureInfo
using System.Windows.Data;
using System.Globalization;


namespace WpfApp1
{
    /// <summary>
    /// 種別コンバータークラス.
    /// </summary>
    public class KindConverter : IValueConverter
    {
        /// <summary>
        /// データ変換処理
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //using (var conn = new SQLiteConnection("Data Source=SampleDb.sqlite"))
            // PgDbContext に変更
            using (var context = new PgDbContext())
            {
                // データを取得
                //Table<Kind> tblCat = con.GetTable<Kind>();
                //Kind k = tblCat.Single(c => c.KindCd == value as String);
                var kind = context.Kinds.SingleOrDefault(c => c.KindCd == (String)value);
                if (kind != null)
                {
                    return kind.KindName;
                }
                return "";
            }
        }

        /// <summary>
        /// データ復元処理
        /// </summary>
        /// <param name="value"></param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns></returns>
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            using (var context = new PgDbContext())
            {
                // データを取得
                //Table<Kind> tblCat = con.GetTable<Kind>();
                //Kind k = tblCat.Single(c => c.KindCd == value as String);
                var kind = context.Kinds.SingleOrDefault(c => c.KindName == value as String);
                if (kind != null)
                {
                    return kind.KindCd;
                }

            }
            return "";
        }
    }
}

 

起動してみる

検索結果

無事に起動、検索することができました。

まとめ

ひとまず、元々の処理(SQLite、MySQL)とあまり変更せずに実装することができました。

ちょっと長くなってしまったので、
登録、更新、削除はまた次回とさせていただきます。

ではでは。

 

Originally published at www.doraxdora.com

view_list WPFでPostgreSQLを使う
第1回 【WPF】PostgreSQL に接続してデータを取得して表示する
第2回 【WPF】PostgreSQL に接続してデータを操作(登録、更新、削除)してみる

doraxdora

IT関係の仕事をしています/1985年生まれの東京在住/便利なサービスやツール漁りや料理などが好き/2017年~ブログやってます/自分でサービスとか作ってリリースしたい/何かありましたらお気軽にDMどうぞ

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント