Npgsql: ユーザマニュアル

Copyright © Npgsql 開発チーム

最終更新日: $Date: 2007/05/31 09:31:53 $ by $Author: fxjr $

範疇: 外部ドキュメント

意図する読者: Npgsql ユーザの皆さん

日本語翻訳著作権 ©: ishikawa[at]postgresql.jp/z-saito[at]guitar.ocn.ne.jp

 

1. Npgsql とは?

Npgsql Postgresql Database Server に対する .Net データプロバイダです。

PostgreSQL サーバがデータを送ったり、受け取ったりするために使用する .Net クライアントアプリケーション(コンソール、WinForms、ASP.NET、Web サービスなど)を提供します。

2. Npgsql を入手し、コンパイルするには

2.1 バイナリパッケージ

MS.Net および Mono 用にコンパイルされた Npgsql はプロジェクトのファイル欄から入手できます。

このパッケージの中は以下のディレクトリ構造になっています。

Npgsql/bin/docs - ドキュメント

Npgsql/bin/docs/apidocs - API ドキュメント

Npgsql/bin/ms1.1 - MS.Net 1.1 用にコンパイルされた Npgsql

Npgsql/bin/mono - Mono 用にコンパイルされた Npgsql

そのほかのプラットフォーム、もしくは別のバージョン用として Npgsql が公開された時は、遅滞なくこのディレクトリ構造の中に追加されます。

2.2 バイナリパッケージのインストール

.Net ランタイムが Npgsql.dll ライブラリを見つけられるようにするには、このファイルはアプリケーションと同一のディレクトリに置く必要があります。但し、構成ファイルから(探査要素 で)ほかのディレクトリを個人コンポーネントのパスとして指定した場合はこの限りではありません。どのようにランタイムが読み込む必要のあるアセンブリを 探査する(場所を探し当てる)かについては .Net のドキュメントを参照ください。

ASP.NET および Web サービス、.Net アプリケーションにおいて、「bin」ディレクトリが ASP.NET ルートアプリケーションディレクトリの下に存在しなければなりません。

Npgsql アセンブリを Global Cache アセンブリ内に置くことも可能です。バージョン 0.4 以降、インストールに gacutil を使用することで認知のさせ方が強固になっています。これを行うには以下のコマンドを発行します:

gacutil -i Npgsql.dll

これ以上のヘルプは MSDN ドキュメントの「Installing an Assembly in the Global Cache Assembly」をぜひ参照してください。

別の手法として、常に Npgsql.dll をコピーする必要なく全てのアプリケーションをアクセス可能にするため、Npgsql を GAC (Global Assembly Cache:グローバルアセンブリキャッシュ) にインストールすることも可能です。Npgsql で設計されている時刻のサポートを VS.Net で使用したい場合、Npgsql を GAC に置く必要があることに注意してください。

Mono 用にコンパイルされた Npgsql は既に Mono ランタイムに Mono.Security.dll が入っていますので、新たに手当ての必要はありません。

これでサンプルをやってみれる準備が整いました。3 章に飛んでください。

2.3 Npgsql を cvs で取得

Npgsql を cvs から取得するには以下の手順を使用している cvs クライアントから実行します:

Server: cvs.pgfoundry.org
Repository: /cvsroot/npgsql
Module name: Npgsql
User: anonymous
Password:

コマンドラインで cvs を使用しているのであれば、以下のように単純です:

cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/npgsql login

パスワードを求められたら Enter キーを押します。
cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/npgsql checkout Npgsql

コードの入手が始まります:

$ cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/npgsql login
Logging in to :pserver:anonymous@cvs.pgfoundry.org:2401/cvsroot/npgsql
CVS password:
$ cvs -d :pserver:anonymous@cvs.pgfoundry.org:/cvsroot/npgsql co Npgsql
cvs checkout: Updating Npgsql
cvs checkout: Updating Npgsql/admin
U Npgsql/admin/release.pl
cvs checkout: Updating Npgsql/docs
U Npgsql/docs/Npgsql.zargo
U Npgsql/docs/NpgsqlConnectionStateMachine.png
U Npgsql/docs/SuggestedReadings.htm
...

2.4 Npgsql のコンパイル

Npgsql をコンパイルするのに公式なサポートを受けたいのであれば NAnt を使用してください。

Npgsql/src/Npgsql フォルダで nant を実行するだけです。

nunit のテスト実行は nant tests を呼ぶだけです。

VS.Net、SharpDevelop、および MonoDevelop のような他の開発プラットフォームに対するソリューションプロジェクトを提供する予定です。既にいくつかのソリューションプロジェクトを入手していますの で、それらが近い将来に使えるよう開発チームは努力しています。

3. Npgsql の使い方

この節では .Net アプリケーションにおける Npgsql の使用法について説明します。以前に Sql サーバ、OleDB 、もしくは ODBC.NET プロバイダーを使用したデータアクセスアプリケーションの開発に携わった方にとって、 Npgsql の使用は非常に似通っているものと思うでしょう。 Npgsql を使用するに当たって、サーバは TCP/IP ポートを監視している必要があります。TCP 接続は 8.0 サーバではデフォルトで有効になっています。それ以前のバージョンでは postmaster が -i オプション付きで起動されなければなりません。詳細については Postgresql ドキュメントを参照してください:http://www.postgresql.org/docs/7.4/static/postmaster-start.html

注意:Npgsql は今でも開発途中です。現在サポートされている特徴のみ説明します。Npgsql が成熟にするにつれ、使用できる機能が増えてゆくでしょう。

ソースファイルに必要となる名前空間を追加する

最初に、Npgsql オブジェクトをより用意に扱うため、コンパイラに Npgsql 名前空間の使用を伝えなければ成りません。同時に、System.Data 名前空間も使用する必要があります。と言うのは、データ操作に使用される数多くのクラスがその中に駐在するからです。これを行うために、C# では次の指示文を使用します:

using System.Data;
using Npgsql;

ASP.NET を使用する場合、.aspx ページの一番初めに以下の行を書き入れます:

<Assembly name="System.Data">
<Assembly name="Npgsql">
これについてのより詳細は、FAQ Mono Page about ASP.NET にあります。

接続の確立

ip 127.0.0.1、port 5432 で稼動しているサーバに、ユーザ joe、パスワード secret、データベース joedata を使用して接続を確立するには以下のような NpgsqlConnection を使用します:

using System;
using System.Data;
using Npgsql;

public class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    conn.Close();
  }
}

接続文字列パラメータ

接続を確立するとき、NpgsqlConnection はその振る舞いを変更するために使用する数多くのパラメータを受け付けます。以下はプログラムの微調整を行う現在のパラメータを一覧表です。

 PostgreSQL データベースに接続するために使用される文字列を取得、もしくは設定します。
	/// 有効な値:
	/// Server:                     Postgresql サーバのアドレス/名称
	/// Port:                       接続するポート
	/// Protocol:                   自動ではなく、使用されるプロトコルバージョン;整数2もしくは3
	/// User Id:                    ユーザ名 
	/// Password:                   平文認証用のパスワード
	/// SSL:                        真、もしくは偽。安全な接続を行うか否かの制御。デフォルト=偽
	/// Pooling:                    真、もしくは偽。接続プールを使用するか否かの制御。デフォルト=真
	/// Encoding:                   使用される符号化方式。ASCII もしくは UNICODE。デフォルトは ASCII。アクセント符号に問題があれば UNICODE を使用してください
	/// CommandTimeout:             例外を投げる前にコマンドが実効を終了するまでの待ち時間。秒単位。デフォルトは20
	/// Sslmode:                    SSL 接続制御モード
	/// ConnectionLifeTime:         秒単位の、プール内非使用接続を閉じるまでの待ち時間。デフォルトは15
	/// SyncNotification:           Npgsql が同期式通知を使用するかどうかの指定
	符号化方式は ASCII もしくは UNICODE です。使用しているアプリケーションがデフォルトでアクセント記号をもった文字の場合動きません。変更を試みてください。
	最小プールサイズが指定された場合、NpgsqlConnection はサーバ接続に際してその数字を事前に割り当てます。
	Sslmode は以下の値の内の一つです: 
            Prefer - 可能であれば ssl を使用して接続します。
            Require - ssl 接続ができない場合、例外が投げられます。
            Allow - まだサポートされていません。ssl なしで接続されます。
            Disable - ssl 接続は行われません。
            Default は Disable です。
        

テーブルに行を挿入するため NpgsqlCommand を使用する

前にあげた例は実用性がありません。単にデータベースに接続し、切断するだけです。エラーがあると NpgsqlException が投げられます。ここで、共に int 型の2つのフィールドを持つ table1 というテーブルがあると仮定します。このテーブルにタプル (1, 1) を追加したい場合は以下のインサート命令を送ります:

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
    NpgsqlCommand command = new NpgsqlCommand("insert into table1 values(1, 1)", conn);
    Int32 rowsaffected;
    
    try
    {
      rowsaffected = command.ExecuteNonQuery();
    }
    
    Console.WriteLine("It was added {0} lines in table table1", rowsaffected);
    
    finally
    {
      conn.Close();
    }
  }
}

NpgsqlCommand.ExecuteScalar() メソッドを使用して単一の結果を取得する

一つの値を単に返す命令を実行するには、コマンドオブジェクトの ExecuteScalar() を使用します:

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
    NpgsqlCommand command = new NpgsqlCommand("select version()", conn);
    String serverversion;
    
    try
    {
      serverversion = (String)command.ExecuteScalar();
    }
    
    Console.WriteLine("PostgreSQL server version: {0}", serverversion);
    
    finally
    {
      conn.Close();
    }
  }
}
ほかの問い合わせも使用できます。たとえば「select count(*) from table1」です。同時に、単一の値を返さない問い合わせも使用できますが、最初の行の最初の列の値のみが返されることになります。

NpgsqlCommand.ExecuteReader() メソッドと NpgsqlDataReader オブジェクトを使用して問い合わせから全ての結果を取得する

問い合わせから全ての結果を戻す命令を実行するには、NpgsqlCommand オブジェクトの ExecuteReader() メソッドを使用します:

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
    NpgsqlCommand command = new NpgsqlCommand("select * from tablea", conn);

    
    try
    {
	NpgsqlDataReader dr = command.ExecuteReader();
	while(dr.Read())
	{
  		for (i = 0; i < dr.FieldCount; i++)
  		{
  			Console.Write("{0} \t", dr[i]);
  		}
  		Console.WriteLine();
	}

    }
    
    finally
    {
      conn.Close();
    }
  }
}
このコードはデータベースに tablea というテーブルが存在することを仮定します。

警告:ExecuteReader と大きなテーブルを呼ぶときには知られた問題が存在します。現時点で Npgsql は全てのデータをテーブルから返す前に取得します。このような場合、性能の悪さを経験したなら、ページから行にわたってサーバカーソルを使用する必要があ ります。それに対しては、以下のようなコードが使用できます:

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
    public static void Main(String[] args)
    {
        NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
        conn.Open();
    
        NpgsqlCommand command = new NpgsqlCommand("declare te cursor for select * from tablea;", _conn);
        
        command.ExecuteNonQuery();
        
        command.CommandText = "fetch forward 3 in te;";
        
        NpgsqlDataReader dr = command.ExecuteReader();
        
        
        while (dr.Read())
        {
            //ここでデータを操作
        }

        dr.Close();


        // さらに3行セット

        dr = command.ExecuteReader();

        while (dr.Read())
        {
        //ここでデータを操作
        }
        
        dr.Close();

    }
}

次のバージョンではこれが自動的に行われるように作業をしています。

問い合わせの中でパラメータを使用する

NpgsqlParamenter および NpgsqlParamenterCollection オブジェクトを使用して、サーバにパラメータ化された問い合わせを送ることもできます。パラメータは問い合わせ文字列の中で : から始まる文字列です。次の例は column1 と呼ばれるパラメータを使用しています。

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
    // 問い合わせ文字列内でパラメータを宣言
    NpgsqlCommand command = new NpgsqlCommand("select * from tablea where column1 = :column1", conn);
    
    // ここでコマンドのパラメータを、パラメータの型を指定してパラメータ蒐集に追加
    command.Parameters.Add(new NpgsqlParameter("column1", DbType.Int32));

    // ここで値を追加し、いつものように後でコマンドを実行
    command.Parameters[0].Value = 4;

    
    try
    {
	NpgsqlDataReader dr = command.ExecuteReader();
	while(dr.Read())
	{
  		for (i = 0; i < dr.FieldCount; i++)
  		{
  			Console.Write("{0} \t", dr[i]);
  		}
  		Console.WriteLine();
	}

    }
    
    finally
    {
      conn.Close();
    }
  }
}
このコードは tablea という名前のテーブルに、少なくとも一つの int4 型を持つ column1 という列が存在することを想定しています。

プリペアード命令を使用する

上の例は、サーバにプリペアード命令リクエストを送信するために使用することもできます。このようにして、問い合わせ計画はたった一度だけ作成さ れ、それ以降の呼び出しに対し再利用されます。この機能は 7.3 以降のサーバで有効である点を覚えておいてください。7.3 以前のこの機能をサポートしていないサーバ内で呼び出した場合、Npgsql は警告を出さず、単に無視します。呼び出しを行うコードを作成した場合でも、サポートしているサーバのみに効力を及ぼすという点で好ましいものです。これ を行うには、単にコマンドの Prepare() メソッドを呼び出します。

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
    // 問い合わせ文字列内でパラメータを宣言
    NpgsqlCommand command = new NpgsqlCommand("select * from tablea where column1 = :column1", conn);
    
    // ここでコマンドのパラメータを、パラメータの型を指定してパラメータ蒐集に追加
    command.Parameters.Add(new NpgsqlParameter("column1", DbType.Int32);

    // ここで命令を準備
    command.Prepare();

    // ここで値を追加し、いつものように後でコマンドを実行
    command.Parameters[0].Value = 4;

    
    try
    {
	NpgsqlDataReader dr = command.ExecuteReader();
	while(dr.Read())
	{
  		for (i = 0; i < dr.FieldCount; i++)
  		{
  			Console.Write("{0} \t", dr[i]);
  		}
  		Console.WriteLine();
	}

    }
    
    finally
    {
      conn.Close();
    }
  }
}
このコードは tablea という名前のテーブルに、少なくとも一つの int4 型を持つ column1 という列が存在することを想定しています。

問い合わせの中で出力パラメータを使用する

出力パラメータを Npgsql で使用することができます。Npgsql は、問い合わせの実行による最初の結果セットを構文解析し、出力パラメータの値に翻訳することにより、出力パラメータを「シミュレート」します。これは2 つの方法、つまりマップするか、しないかです。マップ構文解析は、結果セットでパラメータに返された列名を、同じ名前で合致するか試みます。合致すると、 合致した出力パラメータのみ更新されます。もしマップが見つからない場合、出力パラメータは、コマンドパラメータ蒐集に加えられた順に基づいて更新されま す。このマッピングは自動的に行われます。結果セットを構文解析する場合、Npgsql はマッチを見つける試みを行います。出力、および入出力パラメータの方向は共にサポートされています

using System;
using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
    public static void Main(String[] args)
    {
        NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
        conn.Open();
        
        // バックエンドに問い合わせを送る
        NpgsqlCommand command = new NpgsqlCommand("select * from tablea where column1 = 2", conn);
        
        // ここで tablea の最初の列を受け取る出力パラメータを宣言
        
        NpgsqlParameter firstColumn = new NpgsqlParameter("firstcolumn", NpgsqlDbType.Integer);
        firstColumn.Direction = ParameterDirection.Output;
        
        command.Parameters.Add(firstColumn);
    
            
        try
        {
            command.ExecuteNonQuery();
            
            // ここで firstcolumn パラメータは結果セットの最初の列の値を所有する
            Console.WriteLine(firstColumn.Value);
            
    
        }
        
        finally
        {
            conn.Close();
        }
    }
}

関数呼び出し

関数を呼び出すためには、NpgsqlCommand オブジェクトの CommandType 所有権を CommandType.StoredProcedure に設定し、呼び出したい関数の名前を問い合わせ文字列として渡すだけです。

using System;
using System.Data;
using Npgsql;


// この例は以下で定義された func() と呼ばれる関数を使用します:
// create function funcC() returns int8 as '
// select count(*) from tablea;
// ' language 'sql';

// バージョン 7.3 以降、select count(*) の戻り値型は int4 から int8 に変更されたことを覚えておいてください。
// この関数を、バージョン 7.2 サーバで使用するときは、戻り値型を int8 から int4 に変更してください。

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
      
    try
    {
        NpgsqlCommand command = new NpgsqlCommand("funcC()", _conn);
        command.CommandType = CommandType.StoredProcedure;
  					
        Object result = command.ExecuteScalar();
  		
        Console.WriteLine(result);


    }
    
    finally
    {
      conn.Close();
    }
  }
}

明瞭な問い合わせと同様に、パラメータを関数呼び出しに指定できます。

using System;
using System.Data;
using Npgsql;


// T
// create function funcC(int4) returns int8 as '
// select count(*) from tablea where field_int4 = $1;
// ' language 'sql';

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    
      
    try
    {
        NpgsqlCommand command = new NpgsqlCommand("funcC(:a)", _conn);
        command.CommandType = CommandType.StoredProcedure;
        
        command.Parameters.Add(new NpgsqlParameter("a", DbType.Int32));
  					
        command.Parameters[0].Value = 4;
		
        Object result = command.ExecuteScalar();
  		
        Console.WriteLine(result);


    }
    
    finally
    {
      conn.Close();
    }
  }
}

このコードは tablea という名前のテーブルに、少なくとも一つの int4 型を持つ field_int4 という列が存在することを想定しています。

データセットを扱う

データセットを扱うときに Npgsql を使用できます。このことにより、データセットを変更し、バックエンドに戻して反映させることができます。以下の例はデータセットを使用してどのように行を追加するかを示しています。

// このメソッドはバックエンドに次のテーブルが存在することを想定します:
//
//	create table tableb(field_int2 int2, field_timestamp timestamp, field_numeric numeric);
//
//	
void AddWithDataSet(NpgsqlConnection conn)
{	
	conn.Open();
			
	DataSet ds = new DataSet();

	NpgsqlDataAdapter da = new NpgsqlDataAdapter("select * from tableb", conn);
	
	da.InsertCommand = new NpgsqlCommand("insert into tableb(field_int2, field_timestamp, field_numeric) " + 
							" values (:a, :b, :c)", conn);
			
	da.InsertCommand.Parameters.Add(new NpgsqlParameter("a", DbType.Int16));
	
	da.InsertCommand.Parameters.Add(new NpgsqlParameter("b", DbType.DateTime));
			
	da.InsertCommand.Parameters.Add(new NpgsqlParameter("c", DbType.Decimal));
	
	da.InsertCommand.Parameters[0].Direction = ParameterDirection.Input;
	da.InsertCommand.Parameters[1].Direction = ParameterDirection.Input;
	da.InsertCommand.Parameters[2].Direction = ParameterDirection.Input;
	
	da.InsertCommand.Parameters[0].SourceColumn = "field_int2";
	da.InsertCommand.Parameters[1].SourceColumn = "field_timestamp";
	da.InsertCommand.Parameters[2].SourceColumn = "field_numeric";
	
	da.Fill(ds);
	
	DataTable dt = ds.Tables[0];
	
	DataRow dr = dt.NewRow();
	dr["field_int2"] = 4;
	dr["field_timestamp"] = new DateTime(2003, 03, 03, 14, 0, 0);
	dr["field_numeric"] = 7.3M;
			
	dt.Rows.Add(dr);
			
	DataSet ds2 = ds.GetChanges();
	
	da.Update(ds2);
	
	ds.Merge(ds2);
	ds.AcceptChanges();
}
		

Strong と型づけされたデータセットを扱う

この例は xsd で生成された Strong と型づけされたデータセットの扱いを示しています。このためには、Strong データセットに対するスキーマを指定した .xsd ファイルが必要になります。このファイルを手作業で生成するか、もしくは手作業に代わって xsd に .xsd を生成させることもできます。xsd に .xsd を生成させるには、どこに .xsd があるのかを推測させる手立てを .xml ファイルで提供する必要があります。DataAdapter.WriteXml() メソッドにより生成される .xml ファイルが使用できます:

public void GenerateXmlFromDataSet(NpgsqlConnection conn)
{
	conn.Open();
			
						
	NpgsqlDataAdapter da = new NpgsqlDataAdapter("select * from tablea", conn);
		
	DataSet ds = new DataSet();
			
	da.Fill(ds);
			
	ds.WriteXml("StrongDataSetFeed.xml");
}

これにより、以下とよく似たファイルができます:

<?xml version="1.0" standalone="yes"?>
<NewDataSet>
  <Table>
    <field_serial>1</field_serial>
    <field_text>Random text</field_text>
  </Table>
  <Table>
    <field_serial>2</field_serial>
    <field_int4>4</field_int4>
  </Table>
  <Table>
    <field_serial>3</field_serial>
    <field_int8>8</field_int8>
  </Table>
  <Table>
    <field_serial>4</field_serial>
    <field_bool>true</field_bool>
  </Table>
  <Table>
    <field_serial>5</field_serial>
    <field_text>Text with ' single quote</field_text>
  </Table>
</NewDataSet>

このファイルは以下のコマンドを使用し、.xsd ファイルを生成する xsd で使用されます:

xsd StrongDataSetFeed.xml

xsd は文字列として指定される全ての型を所有する xml スキーマを作成します。間違いのない型を使用するように xsd を変更するだけで、以下と類似した .sxd ファイルを得ることができます。

<?xml version="1.0" encoding="utf-8"?>
<xs:schema id="NewDataSet" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
  <xs:element name="NewDataSet" msdata:IsDataSet="true" msdata:Locale="pt-BR">
    <xs:complexType>
      <xs:choice maxOccurs="unbounded">
        <xs:element name="Table">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="field_serial" type="xs:int" minOccurs="0" />
              <xs:element name="field_text" type="xs:string" minOccurs="0" />
              <xs:element name="field_int4" type="xs:int" minOccurs="0" />
              <xs:element name="field_int8" type="xs:long" minOccurs="0" />
              <xs:element name="field_bool" type="xs:boolean" minOccurs="0" />
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:choice>
    </xs:complexType>
  </xs:element>
</xs:schema>

このファイルを使って、xsd が Strong データセットを生成させる以下のコマンドを発行します:

xsd StrongDataSetFeed.xsd /dataset

これは、Strong データセットを持ったアセンブリを得るためにコンパイルできるようなファイルを生成させます

using System;
using Npgsql;


public class t
{
	public static void Main(String[] args)
	{
		NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");

		conn.Open();
		
		NpgsqlDataAdapter da = new NpgsqlDataAdapter("Select * from tablea", conn);
	

		NewDataSet n = new NewDataSet();

		da.Fill(n);

		foreach (NewDataSet._TableRow tr in n._Table)
		{
			Console.WriteLine(tr.field_serial);
		}
	}
}

バイナリデータと bytea データ型を扱う

この例はファイル名を引数として、その内容を、bytea 型の field_bytea というフィールドと、シリアル型の field_serial というフィールドを所有した、tableByteA と呼ばれるテーブルに挿入します。後でその内容を取得し、終わりに「database」と付けられた新規ファイルを書き出します。

テーブルスキーマ: create table tableBytea (field_serial serial, field_bytea bytea)

using System;
using System.Data;
using Npgsql;
using System.IO;


public class t
{
	public static void Main(String[] args)
	{
		//NpgsqlEventLog.Level = LogLevel.Debug;
		//NpgsqlEventLog.LogName = "NpgsqlTests.LogFile";
		NpgsqlConnection conn = new NpgsqlConnection("server=localhost;user id=npgsql_tests;password=npgsql_tests");
		conn.Open();

		FileStream fs = new FileStream(args[0], FileMode.Open, FileAccess.Read);

		BinaryReader br = new BinaryReader(new BufferedStream(fs));

		Byte[] bytes = br.ReadBytes((Int32)fs.Length);
		
		Console.WriteLine(fs.Length);

		br.Close();
		fs.Close();
				
		NpgsqlCommand command = new NpgsqlCommand("insert into tableBytea(field_bytea) values(:bytesData)", conn);
		
		NpgsqlParameter param = new NpgsqlParameter(":bytesData", DbType.Binary);

		param.Value = bytes;
		
		command.Parameters.Add(param);
		command.ExecuteNonQuery();
		command = new NpgsqlCommand("select field_bytea from tableBytea where field_serial = (select max(select field_serial) from tableBytea);", conn);
		

		Byte[] result = (Byte[])command.ExecuteScalar();
		fs = new FileStream(args[0] + "database", FileMode.Create, FileAccess.Write);

		
		BinaryWriter bw = new BinaryWriter(new BufferedStream(fs));

		bw.Write(result);

		bw.Flush();

		fs.Close();
		bw.Close();		



		conn.Close();

	}
}

ラージオブジェクトサポートを扱う

この例は bytea コードをサポートする上記と同じです。異なる部分は PostgreSQL のラージオブジェクトサポートを使用することです。この例は postgresql にファイルを挿入し、あとで削除します。bytea の例のように、「database」を後ろに付けてファイルを書き出します。

using System;
using System.Data;
using Npgsql;
using NpgsqlTypes;
using System.IO;

public class c
{
    public static void Main(String[] args)
    {
        NpgsqlConnection newconn = new NpgsqlConnection("server=localhost;user id=npgsql_tests;password=npgsql_tests");

        newcon.Open();
        NpgsqlTransaction t = newcon.BeginTransaction();
        LargeObjectManager lbm = new LargeObjectManager(newcon);

        int noid = lbm.Create(LargeObjectManager.READWRITE);
        LargeObject lo =  lbm.Open(noid,LargeObjectManager.READWRITE);

        FileStream fs = File.OpenRead(args[0]);

        byte[] buf = new byte[fs.Length];
        fs.Read(buf,0,(int)fs.Length);

        lo.Write(buf);
        lo.Close();
        t.Commit();
        
        
        t = newcon.BeginTransaction();
        
        lo =  lbm.Open(noid,LargeObjectManager.READWRITE);
        
        FileStream fsout = File.OpenWrite(args[0] + "database");
        
        buf = lo.Read(lo.Size());
        
        fsout.Write(buf, 0, (int)lo.Size());
        fsout.Flush();
        fsout.Close();
        lo.Close();
        t.Commit();
        
        
        DeleteLargeObject(noid);
        
        Console.WriteLine("noid: {0}", noid);
        newcon.Close();
    }
    
    public static void DeleteLargeObject(Int32 noid)
    {
        NpgsqlConnection conn = new NpgsqlConnection("server=localhost;user id=npgsql_tests;password=npgsql_tests");

        newcon.Open();
        NpgsqlTransaction t = newcon.BeginTransaction();
        LargeObjectManager lbm = new LargeObjectManager(newcon);
        lbm.Delete(noid);
        
        t.Commit();
        
        newcon.Close();

    }
}

Mirek(mirek at mascort dot com dot pl)が寄稿した他の例では、データベースから画像を取り出して、フォームとして表示するラージオブジェクトを使います。

using System;
using Npgsql;
using NpgsqlTypes;
using System.Drawing;
using System.IO;

//データベースから画像の oid を取得するメソッド

public int takeOID(int id)

{

    //データベースに接続し、画像 oid を返すメソッド
    
    BazySQL pir = new BazySQL(Login.DaneUzera[8]);
    
    string pytanko = String.Format("select rysunek from k_rysunki where idtowaru = " + idtowaru.ToString());
    
    string[] wartosci = pir.OddajSelectArray(pytanko);
    
    int liczba = int.Parse(wartosci[0].ToString());
    
    return liczba;

}

//データベースから画像を取得し、それを Image 型に変換

public Image pobierzRysunek(int idtowaru)

{

    NpgsqlConnection Polacz = new NpgsqlConnection();
    
    Polacz.ConnectionString = Login.DaneUzera[8].ToString();  //its metod whos return connection string
    
    Polacz.Open();
    
    NpgsqlTransaction t = Polacz.BeginTransaction();
    
    LargeObjectManager lbm = new LargeObjectManager(Polacz);
    
    LargeObject lo = lbm.Open(takeOID(idtowaru),LargeObjectManager.READWRITE); //take picture oid from metod takeOID
    
    byte[] buf = new byte[lo.Size()];
    
    buf = lo.Read(lo.Size());
    
    MemoryStream ms = new MemoryStream();
    
    ms.Write(buf,0,lo.Size());
    
    lo.Close();
    
    t.Commit();
    
    Polacz.Close();
    
    Polacz.Dispose();
    
    Image zdjecie = Image.FromStream(ms);
    
    return zdjecie;

}

//次はこのメソッドを使用するだけです。

pictureBox1.Image = Image pobierzRysunek(1); 

refcursor を扱う

この例は refcursor を返す関数からデータを取得する方法を示しています。Npgsql で提供されるサポートにより、refcursor をどのように扱うかを考慮せず、これらの関数から数多くの結果セットを取得できます。

例を示す目的として、以下の refcursor 返還関数を検討します。

CREATE OR REPLACE FUNCTION testrefcursor() RETURNS SETOF refcursor AS

'DECLARE ref1 refcursor;
ref2 refcursor;
BEGIN

OPEN ref1 FOR SELECT 1;

RETURN NEXT ref1;

OPEN ref2 FOR SELECT 2;

RETURN next ref2;

RETURN;
END;'
LANGUAGE plpgsql;

この関数は単に一行の resultset を返すだけという事実に注目してください。好きなように select 1 をどのような select にも変更できます。

では、これらの関数を呼ぶのとデータを抽出するのに以下のコードを使ってみます:

using System;
using System.Data;
using Npgsql;
using NpgsqlTypes;

public class c
{
    public static void Main(String[] args)
    {
                
        NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Initial Catalog=eeeeee;User id=npgsql_tests;password=npgsql_tests;");
        conn.Open();

        NpgsqlTransaction t = conn.BeginTransaction();          
        NpgsqlCommand command = new NpgsqlCommand("testrefcursor", conn);
        command.CommandType = CommandType.StoredProcedure;
        
        NpgsqlDataReader dr = command.ExecuteReader();
    
        while(dr.Read())
        {
        
            Console.WriteLine(dr.GetValue(0));
        }

        dr.NextResult();
    
        while(dr.Read())
        {
    
            Console.WriteLine(dr.GetValue(0));
        }
        dr.Close();
        t.Commit();
        conn.Close();
    }
}

これで全てです。最後に一つ注意しておくことは、これを動作させるにはトランザクションを使用しなければならないことです。これは、関数呼び出しを行った後、明示的なトランザクションを閉じることによって refcursor 関数から返されるカーソルを保全する必要があるためです。

関数の中にパラメータがある場合、コマンドテキストで関数名だけをそれでも入れて、いつものように NpgsqlCommand.Parameters 蒐集にパラメータを追加する必要があります。Npgsql は使用するパラメータを正しく処理します。

シリアル値を持つテーブル上の最後に挿入された識別子を抽出する

この例は Npgsql Forums において user question に回答する形で Josh Cooley により寄稿されたものです。このコードは、データベースに以下のようなテーブルと関数があることを想定しています。

 create table test_seq (field_serial serial, test_text text);

 CREATE OR REPLACE FUNCTION ins_seq("varchar")
        RETURNS test_seq AS
        'insert into test_seq (test_text) values ($1);
        select * from test_seq where test_text = $1'
        LANGUAGE 'sql' VOLATILE;

そしてこれがコードです:

using System;
using System.Data;
using Npgsql;
using NpgsqlTypes;

public class c
{
    public static void Main(String[] args)
    {
        //NpgsqlEventLog.Level = LogLevel.Debug;
        //NpgsqlEventLog.LogName = "NpgsqlTests.LogFile";
        //NpgsqlEventLog.EchoMessages = true;

        NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;User id=npgsql_tests;password=npgsql_tests;");


        using (NpgsqlDataAdapter adapter = new NpgsqlDataAdapter("select * from test_seq", conn))
        {
            
            DataTable table = new DataTable();
            adapter.Fill(table);
            adapter.InsertCommand = new NpgsqlCommand("ins_seq", adapter.SelectCommand.Connection);
            adapter.InsertCommand.Parameters.Add("foo", NpgsqlTypes.NpgsqlDbType.Varchar, 100, "test_text");
            adapter.InsertCommand.CommandType = CommandType.StoredProcedure;

            DataRow row = table.NewRow();
            row["test_text"] = "asdfqwert";
            table.Rows.Add(row);
            adapter.Update(table);

            foreach (DataRow rowItem in table.Rows)
            {
                Console.WriteLine("key {0}, value {1}", rowItem[0], rowItem[1]);
            }

            Console.ReadLine();
        }
        
    }
}

処理中のコマンドを中止する

Npgsql は処理中のコマンドを中止するようサーバに請求できます。これを行うためには、NpgsqlCommand の Cancel メソッドを呼び出す必要があります。メインスレッドはコマンドの終了を待っていることでブロックされるため、別のスレッドで行わなければならないことに注 意してください。同時に、このメインスレッドはユーザによるキャンセルで例外を引き起こすことがあります。エラーコードは 57014 です。以下にどのようにするかのサンプルコードを示します。

using System;
using System.Data;
using Npgsql;
using NpgsqlTypes;
using System.Threading;


public class c
{

    // このメソッドは以下のテーブルがバックエンドに存在することを前提にします:
    //
    /*      CREATE OR REPLACE FUNCTION funcwaits() returns integer as
    '
    declare t integer;
    begin
    
    t := 0;
    
    while t < 1000000 loop
    t := t + 1;
    end loop;
    
    return t;
    end;
    '
    */



    static NpgsqlConnection conn = null;
    static NpgsqlCommand command = null;

    public static void Main(String[] args)
    {
        //NpgsqlEventLog.Level = LogLevel.Debug;
        //NpgsqlEventLog.LogName = "NpgsqlTests.LogFile";
        //NpgsqlEventLog.EchoMessages = true;

        try
        {

        conn = new NpgsqlConnection("Server=127.0.0.1;User id=npgsql_tests;password=npgsql_tests;");
        conn.Open();

        NpgsqlCommand d = new NpgsqlCommand();

        Thread t = new Thread(new ThreadStart(CancelRequest));


        command = new NpgsqlCommand("select * from funcwaits()", conn);

        Console.WriteLine("Cancelling command...");
        t.Start();

        Console.WriteLine(command.ExecuteScalar());
        conn.Close();
        }
        catch(NpgsqlException e)
        {
                if (e.Code == "57014")
                        Console.WriteLine("Command was cancelled");
        }
    }

    public static void CancelRequest()
    {
        command.Cancel();
        Console.WriteLine("command cancelled");
    }
}

通知を扱う

Npgsql はバックエンドサーバによって送られる通知に基づいた事象の受け取りをユーザに許可します。Npgsqlでそれらを受け取るには2つの方法があります:非 同期方式と同期方式です。同期式通知のみが Npgsql 1.0 およびそれ以降でサポートされています。

非同期式通知

これは Npgsql で使用されるデフォルトの通知機構です。なぜ非同期と呼ばれるかは以下の理由によります。Npgsql はバックエンドサーバ内部でそれを生成した事象が処理され直後、直ぐに通知を受け取りません。Npgsql は次のサーバの反復時に受け取ります。次の反復は、数秒から何時間に渡った後に Npgsql が次のコマンドを送ったときに発生します。これを頭に入れておいて、ユーザは通知をできるだけ直ぐに受け取るよう、サーバにポーリングをかけ続ける必要が あります。これを行うにはサーバに対し空のコマンド、たとえば「;」を送出します。

同期式通知

Npgsql 1.0 より同期式通知がサポートされました。この方式を扱う場合、サーバが通知を送るや否や、Npgsql はそれを受け取ることができ、クライアントに渡すことが可能です。全てはクライアントからのほかのどんな反復も必要なく行われます。通知を受け取るコード はどちらの方式でも同じです。
using System;
using System.Data;
using Npgsql;
using NpgsqlTypes;
using System.Threading;


public class c
{

    public static void Main(String[] args)
    {

        conn = new NpgsqlConnection("Server=127.0.0.1;User id=npgsql_tests;password=npgsql_tests;");
        conn.Open();
    
        NpgsqlCommand command = new NpgsqlCommand("listen notifytest;", conn);
        command.ExecuteNonQuery();
        
        conn.Notification += new NotificationEventHandler(NotificationSupportHelper);
        
        
        command = new NpgsqlCommand("notify notifytest;", _conn);
        command.ExecuteNonQuery();

        Console.ReadLine();  // 通知が処理される以前にプログラムが停止することを避けます。
    }
    
    private void NotificationSupportHelper(Object sender, NpgsqlNotificationEventArgs args)
    {
        // ここで通知を処理します。
    }
}

このコードは通知を監視することを登録し、NotificationSupportHelper メソッドに通知が渡されるかのテストのために通知を出します。

3.1 Npgsql ログサポートを使用する

エラーを追跡するため、Npgsql の振る舞いをたどる必要がしばしばあります。Npgsql にはログメッセージを指定されたファイル、もしくはコンソール、またはその両方に落とすことができます。

3種類のログ取得レベルがあります:

以下の NpgsqlEventLog の静的特性も同時に使用できます:

以下の例は、デバッグレベルでどのようにコンソールとファイルにログを取るかを示しています:

using System.Data;
using Npgsql;

public static class NpgsqlUserManual
{
  public static void Main(String[] args)
  {
    // ログ取得を有効にする。
    NpgsqlEventLog.Level = LogLevel.Debug;
    NpgsqlEventLog.LogName = "NpgsqlTests.LogFile";
    NpgsqlEventLog.EchoMessages = true;
	  
    NpgsqlConnection conn = new NpgsqlConnection("Server=127.0.0.1;Port=5432;User Id=joe;Password=secret;Database=joedata;");
    conn.Open();
    conn.Close();
  }
}

このコードを実行すると次の出力が得られます:

Set NpgsqlEventLog.EchoMessages = True
Entering NpgsqlConnection.NpgsqlConnection()
Entering NpgsqlConnection.ParseConnectionString()
Connection string option: DATABASE = joedata
Connection string option: SERVER = 127.0.0.1
Connection string option: USER ID = joe
Connection string option: PASSWORD = secret
Entering NpgsqlConnection.Open()
Connected to: 127.0.0.1:5432
Entering NpgsqlConnection.WritestartupPacket()
Entering NpgsqlStartupPacket.NpgsqlStartupPacket()
Entering NpgsqlStartupPacket.WriteToStream()
Entering PGUtil.WriteLimString()
Entering PGUtil.WriteLimString()
Entering PGUtil.WriteLimString()
Entering PGUtil.WriteLimString()
Entering PGUtil.WriteLimString()
Entering NpgsqlConnection.HandleStartupPacketResponse()
AuthenticationRequest message from Server
Server requested cleartext password authentication.
Entering NpgsqlPasswordPacket.NpgsqlPasswordPacket()
Entering NpgsqlPasswordPacket.WriteToStream()
Entering PGUtil.WriteString()
Listening for next message
AuthenticationRequest message from Server
Listening for next message
BackendKeyData message from Server
Entering NpgsqlBackEndKeyData.ReadFromStream()
Got ProcessID. Value: 3116
Got SecretKey. Value: -132883070
Listening for next message
ReadyForQuery message from Server
Listening for next message
Connection completed
Entering NpgsqlConnection.Close()

多くの情報が取得できることを示すためにデバッグレベルを使用しました。もちろん、通常レベルでは出力はより少なくなります。同時に、同じ情報は NpgsqlTests.LogFile ファイル追加されます。

3.2 Npgsql 設計時刻サポート  -  VS.Net サポート

Npgsql 0.6 およびそれ以上のバージョンは初期デザイン時刻サポートを提供します。これが意味するところは、SqlConnections もしくは OleDbConnections を使用している限り、Visual Studio .NET の Form-Designer に NpgsqlConnection をドラッグ・アンド・ドロップできるということです。
同時に、ConnectionString を編集し検証する Dialog もあります。

そのようにするためには、以下が必要です:

  1. gac に Npgsql.dll をインストールします
  2. 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\AssemblyFolders' 以下に新規 Registry-Key を追加し設定します。 それは使用する Npgsql.dll へのパスへのデフォルト値です
  3. Visual Studio .NET を開きます
  4. Toolbox の「Data」タブを右クリックします
  5. add/remove 項目をクリックします
  6. .Net-タブで NpgsqlConnection を選択します
VS.Net 2005 に対しては、「HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\v2.0.50727 \AssemblyFoldersEx 以下に、好きな名前で(私は PostGresql としました)、アセンブリが駐在するデフォルトのディレクトリ値を付けて(終わりに逆スラッシュ)、レジストリキーを付け加える必要があります」。ヒント に関し、Edward Diener に感謝。

結果として、Toolbox の Data タブに NpgsqlConnection というアイコンができます。

3.3 接続プールの考察

Npgsql は接続に問題を発見したときはいつでも全ての接続をプールから削除します。これは接続がプールにある間起こりうるいかなる不安定性問題からの回復を容易に します。ほかの接続が依然としてOKなので、この戦略は最善の性能をもたらさないとは言え、この振る舞いはプールが問題に際しても一貫性を保つことを保障 します。NpgsqlConnection を通じてプールを削除する2つの方法があります:ClearPool と ClearAllPools です。これらを使用して手作業でプールを削除可能です。

4. Npgsql の現状

サポートされているデータ型

Npgsql は以下のデータ型をサポートしています:

Postgresql 型 NpgsqlDbType System.DbType Enum .Net システム型
int8 Bigint Int64 Int64
bool Boolean Boolean Boolean
Box, Circle, Line, LSeg, Path, Point, Polygon Box, Circle, Line, LSeg, Path, Point, Polygon Object Object
bytea Bytea Binary Byte[]
date Date Date DateTime
float8 Double Double Double
int4 Integer Int32 Int32
money Money Decimal Decimal
numeric Numeric Decimal Decimal
float4 Real Single Single
int2 Smallint Int16 Int16
text Text String String
time, timetz Time Time DateTime
timestamp Timestamp DateTime DateTime
timestamptz TimestampTZ DateTime DateTime
varchar Varchar String String
inet Inet Object NpgsqlInet、IPAddress
(IPAddress を使用する必要があって、NpgsqlInet のみ保有するのであれば、NpgsqlInet オブジェクトを IPAddress に変換する暗黙的キャスト演算子があります。)
bit Bit Boolean Boolean、Int32
(Int32 値を使う場合、奇数値はビット 1 に翻訳され、偶数値はビット 0 に翻訳されます。)

機能