Unit Of Work--工做單元(一)

簡介html

  最近忙着新項目的架構,已經有一段時間沒有更新博客了,一直考慮着要寫些什麼,直到有一天跟朋友談起他們公司開發數據層遇到的一些問題時,我想應該分享一些項目中使用的數據訪問模式。web

  雖然最近一直都在使用Go語言開發數據服務器,可是本篇文章用到的語言仍然是C#,文章內提供的代碼僅僅是分享如何使用工做單元,至於如何將這個模式引入到項目中去,就須要各位本身去實現了,畢竟每一個項目都是不同的,須要根據項目具體的環境來進行組合。sql

  本篇文章包括如下內容:數據庫

  • 什麼是工做單元
  • 基於ADO.NET的實現
  • 總結

什麼是工做單元服務器

  該模式用來維護一個由已經被業務事務修改(CRUD除了R)的業務對象組成的列表並負責協調這些修改的持久化工做以及全部標記的併發問題。架構

  在web應用中,因爲每一個用戶的請求都是屬於不一樣線程的,須要保持每次請求的全部數據操做都成功的狀況下提交數據,只要有一個失敗的操做,則會對用戶的這次請求的全部操做進行回滾,以確保用戶操做的數據始終處於有效的狀態。併發

  須要更詳細的瞭解,能夠查看此篇文章框架

基於ADO.NET的實現ui

  在不使用任何數據層框架,僅僅使用ADO.NET的狀況下,通常流程的方式以下:this

  • 實例化IDbConnection
  • 而後實例化IDbCommand
  • 設置IDbCommand的Text和Parameters
  • 執行IDbCommand.ExecuteNoQuery
  • 釋放IDbCommand、IDbConnection

  通常狀況下,咱們會給每一個數據庫表建立對應的數據庫交互類並提供CRUD方法,除了R之外,其餘的方法都會實現以上的流程。

  然而若是用戶一次請求會進行屢次CUD操做的狀況下,只能在用戶開始進行數據操做以前使用TransactionScope將屢次操做包裹在內,這樣實現相對麻煩的。

  其實咱們能夠將用戶進行的CUD的SQL語句與參數如今保存起來,到最後再一併進行提交,有點相似存儲過程的樣子,這其實就是工做單元模式了,首先建立一個用來存儲SQL語句和參數的類,代碼以下:

class SQLEntity
{
    private string m_SQL = null;
    public string SQL
    {
        get { return m_SQL; }
    }

    private IDataParameter[] m_Parameters = null;
    public IDataParameter[] Parameters
    {
        get { return m_Parameters; }
    }

    public Entity(string sql, params IDataParameter[] parameters)
    {
        this.m_SQL = sql;
        this.m_Parameters = parameters;
    }
}

  若是將全部CUD的操做都放在一個List<SQLEntity>內,在須要提交的時候只須要遍歷List<SQLEntity>內的元素,並經過重複開始的流程就行(這裏須要稍微重構一下),代碼以下:

class SQLUnitOfWork
{
    private IDbConnection m_connection = null;
    private List m_operations = new List();

    public SQLUnitOfWork(IDbConnection connection)
    {
        this.m_connection = connection;
    }

    public void RegisterAdd(string sql, params IDataParameter[] parameters)
    {
        this.m_operations.Add(new SQLEntity(sql, parameters));
    }

    public void RegisterSave(string sql, params IDataParameter[] parameters)
    {
        this.m_operations.Add(new SQLEntity(sql, parameters));
    }

    public void RegisterRemove(string sql, params IDataParameter[] parameters)
    {
        this.m_operations.Add(new SQLEntity(sql, parameters));
    }

    public void Commit()
    {
        using (IDbTransaction trans = this.m_connection.BeginTransaction())
        {
            try
            {
                using (IDbCommand cmd = this.m_connection.CreateCommand())
                {
                    cmd.Transaction = trans;
                    cmd.CommandType = CommandType.Text;
                    this.ExecuteQueryBy(cmd);
                }
                trans.Commit();
            }
            catch (Exception ex)
            {
                trans.Rollback();
            }
        }
    }

    private void ExecuteQueryBy(IDbCommand cmd)
    {
        foreach (var entity in this.m_operations)
        {
            cmd.CommandText = entity.SQL;
            cmd.Parameters.Clear();
            foreach (var parameter in entity.Parameters)
            {
                cmd.Parameters.Add(parameter);
            }
            cmd.ExecuteNonQuery();
        }
		this.m_operations.Clear();
    }
}

  有了以上的SQLUnitOfWork,咱們的數據庫類在調用CUD方法的時候,就只要調用相應RegisterXXX方法了,數據層實現代碼以下:

class SchoolRepository
{
    private SQLUnitOfWork m_uow = null;

    public SchoolRepository(SQLUnitOfWork uow)
    {
        this.m_uow = uow;
    }

    public void Add(School school)
    {
        this.m_uow.RegisterAdd("insert school values(@id, @name)", new IDbDataParameter[]{
            new SqlParameter("@id", school.Id),
            new SqlParameter("@name", school.Name)
        });
    }

    public void Save(School school)
    {
        this.m_uow.RegisterSave("update school set name = @name where id = @id", new IDbDataParameter[]{
            new SqlParameter("@id", school.Id),
            new SqlParameter("@name", school.Name)
        });
    }

    public void Remove(School school)
    {
        this.m_uow.RegisterRemove("delete from school where id = @id", new IDbDataParameter[]{
            new SqlParameter("id", school.Id)
        });
    }
}

class StudentRepository
{
    //代碼相似,所以省略
}

  有了以上的準備,再使用如下的代碼來看看工做單元的效果,代碼以下:

SQLUnitOfWork uow = new SQLUnitOfWork(connection);
SchoolRepository schoolRepositry = new SchoolRepository(uow);
StudentRepository studentRepository = new StudentRepository(uow);

School school = new School
{
    Id = Guid.NewGuid().ToString(),
    Name = "一中",
};
schoolRepositry.Add(school);

for (int i = 0; i < 7; i++)
{
    Student student = new Student
    {
        Id = Guid.NewGuid().ToString(),
        Name = string.Format("學生{0}號", i),
        Age = 7 + i,
        SchoolId = school.Id
    };
    studentRepository.Add(student);
}

school.Name = "二中";
schoolRepositry.Save(school);

uow.Commit();

  接着會看到數據庫中會看到圖中的數據,如圖:

總結

  這樣就完成了基於ADO.NET的簡單工做單元實現了,可能有些人會有疑問,跟其餘人實現的工做單元有不少不一樣的地方,這是由於該版本只是一個大體思路的實現,並無跟其餘框架、庫的結合,爲了能使其餘人理解工做單元的原理,所以實現相對比較簡單。

  因爲模式是一種解決方案,一種思路,每一個項目的環境、功能、結構都不同,所以實現的方式會有不一樣。對於模式不能死記硬背,要理解其中的原理,能夠經過自我實踐或者參考他人的代碼來實現,若是每一個項目都是照抄模式的話,那麼就失去了它該有的做用了。

  在下一篇文章中,我會重構以上的代碼,實現以兼容ADO.NET和ORM框架的工做單元,那麼文章就到這裏了,若是有問題和疑問歡迎留言,代碼僅供參考請勿應用到實際項目中,謝謝。

相關文章
相關標籤/搜索