How to listen to PostgreSQL events using .NET?

In a recent project I needed to monitor PostgreSQL database. Needless to say I never worked with this database and I had a hard time finding anything interesting on the net. Finally after asking a question on StackOverflow I was directed to SQL notify mechanism. I’ve spend some time going through the documentation and I want to share what I’ve found out.

Before you begin you will need .NET data provider for PostgreSQL which you can download here.

Some theory

Many databases support something that is called triggers – it’s like .NET event, you tell it when it should execute a function. To add a trigger you need to execute some SQL code which you can throw at you database from you favourite database management tool or by executing NpgsqlCommand.ExecuteNonQuery() from you .NET application.

The code

Before you create your trigger you need to define a function that you want execute based on the event.

CREATE OR REPLACE FUNCTION notify_demo() RETURNS TRIGGER AS '
BEGIN
	NOTIFY demoApp;
	RETURN NULL;
END;
' LANGUAGE plpgsql;

The only thing to remember is that function cannot take any arguments and that it needs to return TRIGGER. In the function itself we use notify and set the name of the notification (later we will listen for a notification with the same name).
At this point we are ready to create the trigger.

CREATE TRIGGER demo AFTER UPDATE
   ON users FOR EACH ROW
   EXECUTE PROCEDURE notify_demo();

It easy to see that we create a trigger named demo which will execute function notify_demo() after update on table users. We can set the trigger to execute before or after any of the fallowing events: insert, update, delete (all of which work on rows) and truncate (which works on statements).

In my project function and the trigger are installed by the setup process by executing NpgsqlCommand.ExecuteNonQuery().

When we have our trigger in place it’s time to write some .NET code :)

using System;
using Npgsql;

namespace TriggerDemo
{
    class Program
    {
        static void Main(string[] args)
        {

            string connString = "Server=127.0.0.1;Port=5432;User Id=postgres;Password=pass;SyncNotification=true";
            NpgsqlConnection conn = new NpgsqlConnection(connString);

            try
            {
                conn.Open();
                NpgsqlCommand cmd = new NpgsqlCommand("listen demoApp;", conn);
                cmd.ExecuteNonQuery();
                conn.Notification += new NotificationEventHandler(conn_Notification);
                Console.ReadLine();
                conn.Close();
            }
            catch (NpgsqlException ex)
            {
                Console.WriteLine(ex.Message);
                Console.ReadLine();
            }
        }

        static void conn_Notification(object sender, NpgsqlNotificationEventArgs e)
        {
            Console.WriteLine("Row was updated");
        }
    }
}

In the code you can see standard usage of ADO.NET (if you don’t know what it is ask uncle Google – there are some great tutorials out there). The things you should notice are the fallowing:

  • SyncNotification=true – what it does is opens another thread and using TCP based connection will regularly ask the database if anything interesting happen. This means that you can easily put your database monitoring application on a different machine until the network connection is present and both machines can see each other. One thing you need to remember is that when connection has SyncNotification set to true you cannot execute other queries inside of the notification handler function or it will hang npgsql – you need to create a new connection.
  • listen demoApp - this command listens to particular named trigger (in our case “demoApp”)
  • conn.Notification += new NotificationEventHandler(conn_Notification); – well till now you should know this drill ;) we connect a delegate to an event.

Congratulations – your program is now monitoring changes in PostgreSQL database.

Tags: , , , , ,

This entry was posted on Monday, February 15th, 2010 at 10:46 pm and is filed under coding. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

7 Responses to “How to listen to PostgreSQL events using .NET?”

Vincent October 12th, 2011 at 6:57 am

Thanks for this well documented article, saved me a lot of time !

jye goodlet February 14th, 2012 at 6:49 am

Ill just like to add one thing to this. If your not getting notifications check the connection string and add your database name

Eg:
string connString = “Server=127.0.0.1;Port=5432;User Id=postgres;Password=pass;SyncNotification=true;Database=YOURDATABASENAME”;

Gheorghe March 16th, 2012 at 4:14 pm

Thank You for this small piece of code.
As Vincent said … “saved me a lot of time !”.

What about the case when the trigger and the function is defined in other schema within database ?

DK April 20th, 2012 at 6:17 pm

What are the requirements to get this to work? I am running PostgreSQL 1.9.2 on freeBSD. I am able to run a Liten/Notify in pgAdmin and receive the notification. I am able to query from the db and update the db in .Net code. But when I setup a simple listener in .Net, and run the notify console app, i never receive a notification back. I have tried notify demoapp and select pg_notify(‘demoapp’, ‘message’).

I can run the listener, and even run a NOTIFY demoapp; query directly in pgadmin and still get nothing.

Code can be viewed here:
http://pastebin.com/Z2srhiSV

DK April 20th, 2012 at 6:18 pm

I forgot to mention i am using Npgsql.dll v2.0.11.93 with included Mono.Security.dll v2.0.0.0

DK April 20th, 2012 at 9:33 pm

If you could turn on logging and send me a successful log so I can troubleshoot where it is not working that would be great.

Add the following to code:
// Enable logging.
NpgsqlEventLog.Level = LogLevel.Debug;
NpgsqlEventLog.LogName = “Npgsql_debug.log”;
NpgsqlEventLog.EchoMessages = true;

My log is available here:
http://pastebin.com/kcuUVCj5

Adasko June 29th, 2012 at 7:31 am

Good job with this article. But i have a question. How send in Notify a variable? I want to do something like this:

CREATE OR REPLACE FUNCTION notify_demo()
RETURNS trigger AS
$BODY$
DECLARE
n_text text;
BEGIN
n_text:=’sda’;

Notify demoApp, n_text;
RETURN null;
END;
$BODY$

But it not works ;/ Of course this text is example

Leave a Reply