使用IOC和DI设计模式的C#乐观并发
讨论使用 IOC 和 DI 设计模式的并发,以及 PostgreSQL 数据库。
引言
我之前的文章展示了使用 PostgreSQL 数据库的乐观并发。我想编写一个 C# 程序,使用 IOC 和 DI 设计模式,以便支持多种数据库。这个版本还使用了参数化查询和事务。
背景
Shivprasad Koirala 编写的 IOC 设计模式,可以在 此处 详细阅读,文章地址为 IOC 和 DI 设计模式。
Using the Code
正如 Shivprasad Koirala 的文章中所述,我有一个 abstract
类 iDatabase
,实际的数据库类都从中派生。我只实现了 Postgres,其他可以是 SQLServer 或任何其他数据库。

在 abstract
类中,我设置了派生类需要实现的方法。这个类中包含一些代码,因为它是一个 abstract
类,而不是一个 interface
。
public abstract class iDatabase
{
string strdbName; // the database name
string strServer; // the database name
string struserName; // the user name
string strpassword; // the password
string strSAName; // the SA user name
string strSApassword; // the SA password
public iDatabase()
{
strdbName = ""; // the database name
strServer = ""; // the database name
struserName = ""; // the user name
strpassword = ""; // the password
strSAName = ""; // the SA user name
strSApassword = ""; // the SA password
}
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);
}
在 postgres
类中的更新用户代码是一段正常的代码,正如你所期望的那样。如果选择了 PostreSQL,这段代码将运行并更新用户表。主要区别在于这段代码处理乐观并发,并将整个数据类作为数据库记录返回,而不是每个数据项。
public override int UpdateUser(UserData ud)
{
string sqlstring;
int iret = -1;
bool errflg = false;
Object result;
//
// get the connection string
//
sqlstring = GetConnectionString();
NpgsqlConnection conn = new NpgsqlConnection(sqlstring);
conn.Open();
// create a transaction object
NpgsqlTransaction trano = conn.BeginTransaction();
// set the sql command
// uuid, int, str, str, str
sqlstring = "Update_MinUser(:Arg1, :Arg2, :Arg3, :Arg4, :Arg5)";
NpgsqlCommand command = new NpgsqlCommand(sqlstring, conn, trano);
command.CommandType = CommandType.StoredProcedure;
try
{
// Now add the parameter to the parameter collection of the command
// specifying its type.
command.Parameters.Add(new NpgsqlParameter("Arg1", DbType.Guid));
// Now, add a value to it and later execute the command as usual.
command.Parameters[0].Value = ud.UserId;
// Now add the parameter to the parameter collection of the command
// specifying its type.
command.Parameters.Add(new NpgsqlParameter("Arg2", DbType.Int16));
// Now, add a value to it and later execute the command as usual.
command.Parameters[1].Value = ud.UserConcur;
// Now add the parameter to the parameter collection of the command
// specifying its type.
command.Parameters.Add(new NpgsqlParameter("Arg3", DbType.String));
// Now, add a value to it and later execute the command as usual.
command.Parameters[2].Value = ud.UserName;
// Now add the parameter to the parameter collection of the command
// specifying its type.
command.Parameters.Add(new NpgsqlParameter("Arg4", DbType.String));
// Now, add a value to it and later execute the command as usual.
command.Parameters[3].Value = ud.UserPassword;
// Now add the parameter to the parameter collection of the command
// specifying its type.
command.Parameters.Add(new NpgsqlParameter("Arg5", DbType.String));
// Now, add a value to it and later execute the command as usual.
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);
}
我在 clsUser
类中选择数据库类型,如下所示。如果我想在运行时更改数据库类型,我会检查某个设置,并使用 if
语句来创建所需的类。
public ClsUser()
{
idb = new Postgres();
idb.DBName = "CONNC";
idb.Server = "localhost";
idb.SAUserName = "postgres";
idb.SAPassword = "Password1";
}
使用这段代码非常简单和清晰。在这个程序中,我添加了一个用户,以防它尚未添加。然后我获取用户数据记录。然后我更新密码并调用更新过程。由于我仍然拥有原始数据,我再次调用更新过程来查看会发生什么。正如你所期望的那样,我收到一个错误,因为记录已经被更新了。
UserData ud1;
int ires;
ClsUser ccus;
ccus = new ClsUser();
// do a try catch since we may have run this once before
try
{
ires = ccus.AddUser("User1", "User1p", "Don");
}
catch (Exception ex)
{
MessageBox.Show("Error from {0}" + ex.Message, "test");
}
// get the user data
ud1 = ccus.GetUser("User1");
// change the password.
ud1.UserPassword = "NewPassword";
// update the data.
ires = ccus.UpdateUser(ud1);
if (ires != 0)
{
MessageBox.Show("Error updating the user record.");
}
// now with the original data try and update it again. It should give us an error.
// this will happen since the Optimistic Concurrency has already been updated.
// change the password.
ud1.UserPassword = "NewPassword1";
// update the data.
ires = ccus.UpdateUser(ud1);
if (ires != 0)
{
MessageBox.Show("Error updating the user record the second time.");
}
关注点
从这个设计模式中需要注意的一点是,支持使用两种或多种数据库需要大量的精力和代码。如果你知道你不会支持其他数据库,那么其他模式可能更好。zip 文件中的 Db1.sql 文件是创建此项目所需的 SQL 文件。作为响应用户输入的一部分,我修改了代码以支持参数化查询和事务。在这样做的时候,我发现 Npgsql 版本 1 对 UUID 的支持不太好。我不得不下载较新的版本 2。
历史
- 2009 年 2 月 9 日 - 初始发布
- 2009 年 2 月 23 日 - 更新了参数化查询和事务