Last update: $Date: 2004/11/19 01:30:45 $ by $Author: fxjr $
Category: External documentation
Intended Audience: Npgsql Users
Npgsql is a .Net Data Provider for Postgresql Database Server.
It allows a .Net client application (Console, WinForms, ASP.NET, Web Services...) to use a PostgreSQL server to send and receive data. It is developed using the guidelines specified in the .Net docs.
This section explains Npgsql usage in a .Net Application. If you are used to developing Data Access applications using the Sql Server, OleDB or ODBC.NET providers, using Npgsql is very similar.
Note: Npgsql is still under development. Only features currently supported will be demonstrated. As Npgsql matures, more functionality will be available to be used.
First, to use Npgsql objects more easily, you have to tell the compiler to use the Npgsql namespace. It will also be needed to use the System.Data namespace as a lot of classes used for data manipulation reside in it. To do that, in C#, you use the using directive:
using System.Data; using Npgsql;
To establish a connection to a server located at ip 127.0.0.1, port 5432, as the user joe, with the password secret, using the database joedata, you use the NpgsqlConnection as:
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(); } }
When establishing a connection, NpgsqlConnection accepts many parameters which can be used to modify its behavior. Here is the list of current parameters you can tweak: (From NpgsqlConnection source)
Gets or sets the string used to connect to a PostgreSQL database. /// Valid values are: /// Server: Address/Name of Postgresql Server; /// Port: Port to connect to; /// Protocol: Protocol version to use, instead of automatic; Integer 2 or 3; /// Database: Database name. Defaults to user name if not specified; /// User: User name; /// Password: Password for clear text authentication; /// SSL: True or False. Controls whether to attempt a secure connection. Default = False; /// Pooling: True or False. Controls whether connection pooling is used. Default = True; /// MinPoolSize: Min size of connection pool; /// MaxPoolSize: Max size of connection pool; /// Encoding: Encoding to be used; /// Timeout: Time to wait for connection open in seconds. Encoding can the ASCII or UNICODE. If your application uses characters with accents and with default settings it doesn't work, try changing that. Min pool size when specified will make NpgsqlConnection pre allocates this number of connections with the server.
The previous example doesn't do anything useful. It only connects to the database and disconnects. If there is an error, a NpgsqlException is thrown. Now, suppose there is a table called table1 with two fields a and b both of type int. If you want to add the tuple (1, 1) in this table you could send the insert statement as follows:
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(); } } }
To execute statements which return just one value you use the ExecuteScalar() method of the Command object:
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(); } } }Other queries could also be used, for example "select count(*) from table1". Also, queries that don't return single values could be used, but only the first column of the first row would be returned.
To execute statements which return full results from a query you use the ExecuteReader() method of the NpgsqlCommand object:
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(); } } }This code assumes there is a table called tablea in the database.
You can also send a parameterized query to the server using NpgsqlParamenter and NpgsqlParamenterCollection objects. A parameter is a string prefixed by : in the query string. The example below uses a parameter called 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(); // Declare the parameter in the query string NpgsqlCommand command = new NpgsqlCommand("select * from tablea where column1 = :column1", conn); // Now add the parameter to the parameter collection of the command specifying its type. command.Parameters.Add(new NpgsqlParameter("column1", DbType.Int32)); // Now, add a value to it and later execute the command as usual. 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(); } } }This code assumes a table called tablea with at least a column named column1 of type int4.
The previous example can also be used to send prepared statements request to server. This way, the query plan is create just once and reused by subsequent calls. Note that this feature is only available in server 7.3+ versions. If you call it in a server which doesn't support it, Npgsql will silently ignore it. This is good as you can create code which calls it but it will only have effect when connected to the supported servers. To do it, you just call the Prepare() method of the command.
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(); // Declare the parameter in the query string NpgsqlCommand command = new NpgsqlCommand("select * from tablea where column1 = :column1", conn); // Now add the parameter to the parameter collection of the command specifying its type. command.Parameters.Add(new NpgsqlParameter("column1", DbType.Int32); // Now, prepare the statement. command.Prepare(); // Now, add a value to it and later execute the command as usual. 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(); } } }This code assumes a table called tablea with at least a column named column1 of type int4.
To call a function, you just set the CommandType property of the NpgsqlCommand object to CommandType.StoredProcedure and pass the name of the function you want to call as the query string.
using System; using System.Data; using Npgsql; // This example uses a function called funcC() with the following definition: // create function funcC() returns int8 as ' // select count(*) from tablea; // ' language 'sql'; // Note that the return type of select count(*) changed from int4 to int8 in 7.3+ versions. To use this function // in a 7.2 server, change the return type from int8 to 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(); } } }
In the same way as clear queries, you can specify parameters to function calls.
using System; using System.Data; using Npgsql; // This example uses a function called funcC() with the following definition: // 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(); } } }
This code assumes a table called tablea with at least a field called field_int4 of type int4.
You can use Npgsql when working with Datasets. This allows you to do changes in the dataset and have them propagated back to backend. The below example shows how to add a row using the Dataset:
// This method expects the following table in the backend: // // 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(); }
This example will show how to use a strong typed dataset generated with xsd. To do so, we need a .xsd file specifing the schema to the strong dataset. You can generate this file by hand, or you can use xsd to generate it for you. To have xsd generate the .xsd for you, you have to suppy it with a .xml file which it will infer the .xsd from. We can use a .xml file generated with a DataAdapter.WriteXml() method:
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"); }
This will give us a file which looks similar to this:
<?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>
This file will be used with xsd to generate the .xsd file with the following command:
xsd StrongDataSetFeed.xml
xsd will produce an xml schema which will have all types specified as string, we just change the xsd to use the correct types and have a .xsd file similar to this:
<?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>
With this file we issue the following command to have xsd generate the strong dataset:
xsd StrongDataSetFeed.xsd /dataset
Which will generate a file we can compile to get an assembly with the strong dataset. To use it, we do the following:
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); } } }
This sample takes a filename as argument, inserts its contents to a table called tableByteA which contains a field called field_bytea of type bytea and a field called field_serial of type serial. Later gets its contents and write a new file with the sufix "database" in its end.
table schema: 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(); } }
This sample is the same as the bytea code support above. The difference is that we will use the large object support of Postgresql. This sample stores the file in postgresql and later removes it. As bytea sample, it writes a file with a "database" suffix.
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(); } }
Another example, contributed by Mirek (mirek at mascort dot com dot pl), uses the large object support to get an image from database and shows it in a form.
using System; using Npgsql; using NpgsqlTypes; using System.Drawing; using System.IO; //metod whos take picture oid from database public int takeOID(int id) { //it's a metod whos connect to database and return picture 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; } //take a picture from database and convert to Image type 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; } //next I just use this metod pictureBox1.Image = Image pobierzRysunek(1);
In order for the .Net Runtime to locate the Npgsql.dll library, this file must be put in the same directory the application is, unless you specify another directory as a path to private components through a configuration file (using the probing element). Please, see the .Net docs for information on how the runtime probes (locates) assemblies to be loaded. There is also a section called "Path to Private Components"
In ASP.NET and Web Services .Net Applications, there must be a directory called "bin" below the ASP.NET root application directory. So, for example the application directory is called "ASPNETApplication", Npgsql.dll must be placed in the "ASPNETApplication/bin" directory. If it's not, you will get a lot of compiling errors when trying to use Npgsql.
You can also put the Npgsql assembly in the Global Cache Assembly. Since 0.4 version, Npgsql is strongly signed which means you can use gacutil to install it. Issue the following command to do it:
gacutil -i Npgsql.dll
Please, refer to "Installing an Assembly in the Global Cache Assembly" section of MSDN docs for more help.
Sometimes it is necessary to trace Npgsql's behaviour to track errors. Npgsql can log messages to a specified file or to the console or both.
There are three levels of logging:
The following NpgsqlEventLog static properties may also be used:
The example below shows how to log to the console and a file using the level Debug:
using System.Data; using Npgsql; public static class NpgsqlUserManual { public static void Main(String[] args) { // Enable logging. 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(); } }
Running this code gives the following output:
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()
I used the Debug level to show that a lot of information can be obtained. Of course, using the Normal level would be less verbose. Also, this same information was added to the file NpgsqlTests.LogFile.
Npgsql 0.6 and higher provide initial design time support. This means that you can drag and drop a NpgsqlConnection in the Form-Designer of Visual Studio .NET as you are used to with SqlConnections or OleDbConnections.
There is also a Dialog to edit and validate the ConnectionString
To do so you must:
Npgsql supports the following data types: