事务的考虑
写博文不容易,坚持写更不容易,写得快更加不容易。
如果要写,必定是自己肚子里有货;坚持写,是学习能力的体现;又要工作又要持续学习,不写得快怎么能行呢。
原本想把我的数据访问层也一起公开的,但是有朋友劝我不要发布。
他告诉我:网上发布的多数是贴贴Demo,框架只有在实战中才有存在价值,发布出来一般也没人有那个耐心去看,早就沉没在信息的海洋里了。
但我还是想把业务层都写完了。
事务管理应该放在哪一层做
事务的开始与提交应该放在哪里呢?业务逻辑层还是数据访问层?
我以为,数据访问层需要,业务逻辑层也需要。
数据访问层的仓储(Repository)做细粒度的事务,业务逻辑层做相对较粗的事务。
那么具体如何实现呢?
Repository中得事务,下面分别是3个版本的事务,原生SQL,NHibernate,Entity Framework,我之所以写3个版本,是想证明数据访问层随时可以切换,而业务层绝不能受制于它,无论拼接SQL还是HQL,都不是一个很好的习惯。
public class UserRepository : BaseRepository,IUserRepository { public void SaveUser(User user) { //ADO.NET事务开始 //省略执行SQL语句的操作…… //ADO.NET事务提交 } }
public void SaveUser(User user) { ITransaction tran = Session.BeginTransaction(); Session.Save(user); tran.Commit(); tran.Dispose(); }
public void SaveUser(User user) { using (TransactionScope tx = new TransactionScope()) { context.AddObject("User",user); tx.Complete(); context.AcceptAllChanges(); } }
业务层的事务,这里引入了System.Transactions命名空间,userRep.SaveUser(user2);这段就是刚才看到的,包含了一个事务的仓储,
执行tx.Complete();才会走完这个事务,如果在using中出现了异常,那么所有事务都将回滚。其实是一个嵌套事务。
[Test] public void TranTest() { IDataContext context = DataContextFactory.GetDataContext(); Department dept = context.GetByID<Department>(1); User user1 = new User() { LoginName = "adama1", Name = "adama1", Password = "123123", Department = dept }; User user2 = new User() { LoginName = "adama2", Name = "adama2", Password = "123123", Department = dept }; IUserRepository userRep = RepostoryFactory.GetFor<User>() as IUserRepository; using (TransactionScope tx = new TransactionScope()) { context.Add(user1); userRep.SaveUser(user2); tx.Complete(); } }
上面的context不是EF的上下文,是DAL封装起来的上下文,可以给NHibernate和EF切换的统一接口。
无论如何,架构设计者都不能让业务逻辑层感觉到ORM工具的存在,如果无法做到,那么定义了IRepository接口就成了摆设。
在实际使用中,上面的例子完全可以加入上一篇提到的验证业务规则,它可以应付更加复杂的逻辑的。
结尾:这几篇博文真的非常偏重于领域驱动,但有许多公司甚至连ORM是什么都不知道,用EF的公司只能说明技术总监很潮。
下一次试着用一些简单传统的设计,也就不需要这么多的前置技能,设计更加随意。
本篇开始出现许多绝对性的词语(无论,绝对,肯定),通常绝对性的话语都是错的,因为世事无绝对。
但我还是那句话“写技术博文已经很理性了,为什么不能多加入一些感性的元素”。
不知道还有没有机会写下去,心里有个声音:英语已经等不急了,快去看它吧。