65.9K
CodeProject 正在变化。 阅读更多。
Home

使用IOC和DI设计模式的C#乐观并发

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.23/5 (6投票s)

2009 年 2 月 9 日

CPOL

2分钟阅读

viewsIcon

39671

downloadIcon

236

讨论使用 IOC 和 DI 设计模式的并发,以及 PostgreSQL 数据库。

引言

我之前的文章展示了使用 PostgreSQL 数据库的乐观并发。我想编写一个 C# 程序,使用 IOC 和 DI 设计模式,以便支持多种数据库。这个版本还使用了参数化查询和事务。

背景

Shivprasad Koirala 编写的 IOC 设计模式,可以在 此处 详细阅读,文章地址为 IOC 和 DI 设计模式

Using the Code

正如 Shivprasad Koirala 的文章中所述,我有一个 abstract iDatabase,实际的数据库类都从中派生。我只实现了 Postgres,其他可以是 SQLServer 或任何其他数据库。

classIOCDI.gif

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 日 - 更新了参数化查询和事务
© . All rights reserved.