分享我们项目中基于EF事务机制的架构(4)_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 分享我们项目中基于EF事务机制的架构(4)

分享我们项目中基于EF事务机制的架构(4)

 2013/8/14 22:43:53  蒋叶湖  博客园  我要评论(0)
  • 摘要:1.本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库。2.本文中的单元测试都是正确通过的。要理解EF的事务机制,首先要理解这2个类:TransactionScope和DbContext。DbContext是我们的数据库,通常我们会建一个类MyProjectDbContext继承自DbContext,里面包含所有的数据库表。这个类相当于定义了一个完整的数据库。下面通过一些单元测试来看看这2个类是如何工作的
  • 标签:我们 项目 架构

class="p0">1. 本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库。

2. 本文中的单元测试都是正确通过的。

理解EF的事务机制,首先要理解这2个类:TransactionScopeDbContext

DbContext是我们的数据库,通常我们会建一个类MyProjectDbContext继承自DbContext,里面包含所有的数据库表。这个类相当于定义了一个完整的数据库。

下面通过一些单元测试来看看这2个类是如何工作的。

1 [Test] 2 public void Can_Rollback_On_Errors_In_Different_Context() 

 { 

var user1 = Mock.Users.Random(); 

    var user2 = Mock.Users.Random(); 

     user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; 

     var userCount = 0; 

    try    {

         using (var scope = new TransactionScope())

        {

            using (var db = new MyProjectDbContext())

           {

                db.Users.Add(user1);

                db.SaveChanges();

                 userCount = db.Users.Count();

            }

             using (var db = new MyProjectDbContext())

           {

                db.Users.Add(user2);

                db.SaveChanges();//will throw exception22            }23            scope.Complete();

        }

    }

     catch(Exception)

    {  }

     Assert.AreEqual(1, userCount);

     using (var db = new MyProjectDbContext())

    {

         Assert.AreEqual(0, db.Users.Count());

    }

 }

注意第一个assertuserCount是等于1的,也就是说第一个db.SaveChanges()是顺利执行了的。但是看看第二个assert,数据库里面却没有user记录。这就是使用TransactionScope得到的真正的事务机制。

 public void Cannot_Rollback_Without_Scope() { 

     var user1 = Mock.Users.Random(); 

    var user2 = Mock.Users.Random(); 

    

user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";

     var userCount = 0; 

    try 

   {       

  using (var db = new MyProjectDbContext())

      {

           db.Users.Add(user1);        

   db.SaveChanges();    

         userCount = db.Users.Count();    

    }      

  using (var db = new MyProjectDbContext())        {            

db.Users.Add(user2);            

 db.SaveChanges();//will throw exception   

     }   

 }

catch (Exception) 

   {   }

  Assert.AreEqual(1, userCount);   

 using (var db = new MyProjectDbContext())    {

        Assert.AreEqual(1, db.Users.Count());

}

 }

这个测试跟上面的测试差不多,唯一的区别就是没有使用TransactionScope把两个DbContext包起来。于是每个DbContext成为独立的事务。

再来看一个测试:

[Test] 

 public void Shouldnot_SaveToDB_As_ScopeNotComitted()  { 

     var user1 = Mock.Users.Random(); 

var userCount = 0;  

   try 

   { 

        using (var scope = new TransactionScope())

{

using(var db = new MyProjectDbContext())

 {

 db.Users.Add(user1);

 db.SaveChanges();

 userCount = db.Users.Count();

}

 //scope.Complete();

 }

}

catch (Exception)

 { }

Assert.AreEqual(1, userCount);

using (var db = new MyProjectDbContext())

 {

 Assert.AreEqual(0, db.Users.Count());

 }

}

}

这个测试表明,一旦DbContextTransactionScope包起来之后,那么scope必须要调用scope.Complete()才能将数据更新到数据库。

基于上面的这些知识,我们可以很容易为EF搭建支持真正事务的框架。下面我简单介绍下我们的项目架构EF CodeFirst, MVC)。

基于EF事务机制的架构

Domain层:

定义数据实体类,即数据库中的表。定义继承自DbContextMyProjectDbContext

Service层:

主要用于封装所有对数据库的访问。例子代码如下:

public List<User> GetAllUsers() {

     using (var db = new MyProjectDbContext())

{        return db.Users.ToList();

    }

}

上面这段代码中注意要使用using,否则DbContext的延迟加载功能会在controller层被调用。加了using之后,可以避免在controller层对数据库的直接访问。

Controller层:

调用service层的代码从数据库中得到数据,返回给UI。例子:

 public ActionResult GetAllUsers()

 {

var users = IoC.GetService<IUserService>().GetAll();

     return View(users);

 }

同时将UI传回来的数据更新到数据库,这时如果需要调用多个service来更新数据库,那么就需要用到事务。例子:

 public ActionResult DeleteUser(int userId) 

 {  

try 

using(var scope = new TransactionScope()) 

 

 

 {

 

 IoC.GetService<IUserService>().DeleteLogs(userId);

  IoC.GetService<IUserService>().DeleteUser(userId); 

 scope.Complete();

 return View();

 

}

 catch(Exception)

 

{

 

return View();

 

}

通常情况下,我们会在MyControllerBase里面加一个 ActionResult TryScope(Action action)的方法,这样在子类里面就可以不用写try-catch了。

发表评论
用户名: 匿名