Introduction
My last article showed Optimistic Concurrency using the PostgreSQL database. I wanted to write a C# program with the Design pattern IOC and DI so we can support multiple databases. This version also uses parameterized queries and transactions.
Background
The IOC design pattern written by Shivprasad Koirala can be read in detail at Design Pattern IOC and DI.
Using the Code
Just as described in Shivprasad Koirala's article, I have an abstract
class iDatabase
that the actual database classes derive from. I have implemented Postgres only, the other could be SQLServer or any other database.
In the abstract
class, I have set the methods that need to be implemented in the derived classes. There is some code in this class since it is an abstract
class and not an interface
.
public abstract class iDatabase
{
string strdbName;
string strServer;
string struserName;
string strpassword;
string strSAName;
string strSApassword;
public iDatabase()
{
strdbName = "";
strServer = "";
struserName = "";
strpassword = "";
strSAName = "";
strSApassword = "";
}
public abstract void CheckDBConnectionInfo();
public abstract string GetUserPassword(string userName);
public abstract int UpdateUser(UserData ud);
public abstract int AddUser(string userName, string Password, string ModUser);
public abstract UserData GetUser(string userName);
}
The update user code in the postgres
class is a normal piece of code as you would expect. It is this code that runs and updates the user table if PostreSQL is selected. The main difference is this code does handle Optimistic Concurrency and sends back the entire data class for the database record instead of each data item.
public override int UpdateUser(UserData ud)
{
string sqlstring;
int iret = -1;
bool errflg = false;
Object result;
sqlstring = GetConnectionString();
NpgsqlConnection conn = new NpgsqlConnection(sqlstring);
conn.Open();
NpgsqlTransaction trano = conn.BeginTransaction();
sqlstring = "Update_MinUser(:Arg1, :Arg2, :Arg3, :Arg4, :Arg5)";
NpgsqlCommand command = new NpgsqlCommand(sqlstring, conn, trano);
command.CommandType = CommandType.StoredProcedure;
try
{
command.Parameters.Add(new NpgsqlParameter("Arg1", DbType.Guid));
command.Parameters[0].Value = ud.UserId;
command.Parameters.Add(new NpgsqlParameter("Arg2", DbType.Int16));
command.Parameters[1].Value = ud.UserConcur;
command.Parameters.Add(new NpgsqlParameter("Arg3", DbType.String));
command.Parameters[2].Value = ud.UserName;
command.Parameters.Add(new NpgsqlParameter("Arg4", DbType.String));
command.Parameters[3].Value = ud.UserPassword;
command.Parameters.Add(new NpgsqlParameter("Arg5", DbType.String));
command.Parameters[4].Value = ud.ModUser;
}
catch (NpgsqlException nex)
{
trano.Rollback();
throw new Exception(nex.Message);
}
try
{
result = command.ExecuteScalar();
}
catch (NpgsqlException nex)
{
trano.Rollback();
throw new Exception(nex.Message);
}
finally
{
if (!errflg)
{
trano.Commit();
}
conn.Close();
}
iret = (int)result;
return (iret);
}
Where I choose the type of database is in the clsUser
class, as shown below. If I wanted to change the type of database at runtime I would have checked a setting somewhere and done an if
statement to create the required class.
public ClsUser()
{
idb = new Postgres();
idb.DBName = "CONNC";
idb.Server = "localhost";
idb.SAUserName = "postgres";
idb.SAPassword = "Password1";
}
Using the code is extremely easy and clean. In this program I added a user in case it is not already added. I then get the user data record. Then I update the password and call the update procedure. Since I still have the original data, I call the update procedure again to see what will happen. As you would expect, I get an error since the record has already been updated.
UserData ud1;
int ires;
ClsUser ccus;
ccus = new ClsUser();
try
{
ires = ccus.AddUser("User1", "User1p", "Don");
}
catch (Exception ex)
{
MessageBox.Show("Error from {0}" + ex.Message, "test");
}
ud1 = ccus.GetUser("User1");
ud1.UserPassword = "NewPassword";
ires = ccus.UpdateUser(ud1);
if (ires != 0)
{
MessageBox.Show("Error updating the user record.");
}
ud1.UserPassword = "NewPassword1";
ires = ccus.UpdateUser(ud1);
if (ires != 0)
{
MessageBox.Show("Error updating the user record the second time.");
}
Points of Interest
One item to take from this design pattern is it is a lot of work and code to support the possibility of using two or more databases. If you know you are not going to support other databases, there are other patterns that might be better. The file Db1.sql in the zip file is the SQL file necessary to create the tables for this project. As part of responding to user input, I modified the code to support parameterized queries and transactions. In doing this, I found that Npgsql version 1 did not support UUIDs all that well. I had to download the newer version 2.
History
- 9th February, 2009 - Initial release
- 23rd February, 2009 - Updated parameterized queries, transactions