一、引言
经过两章的铺垫,我们现在对SmartSql已经有了一定的了解,那么今天我们的主题是事务处理。事务处理是常用的一种特性,而SmartSql至少提供了两种使用事务的方法。一种是通过Repository(动态仓储)或者ITransaction的常规调用,一种是基于AOP提醒的动态代理方式。接下来我们一个个说。
上图是这一章的项目结构,这次的结构略微有点复杂,我一一解释。
项目结构分为3个部分,Api部分分成了3个.NetCore MVC项目,三个项目分别是常规调用;基于.NetCore原生DI的AOP调用;基于Autofac的AOP调用,AOP部分的区别只是在DI的配置部分。
DomainService也就是业务逻辑层,这个没什么好说的。
Data Access部分是实体与动态仓储,而在这一章中。我们的动态仓储项目有一个小的变动。先放图
通过图片可以看到,原来我们放在Api项目中的Map和Config都放到了动态仓储的项目。这个因为在当前项目中,我们有3个输出项目。如果每个项目中都写一套Maps就显得很多此一举。所以把它们统一的放到仓储类库里来,是一个很好的办法(虎哥提供)。注:别忘记把文件设置成始终复制哦
二、 常规使用
用法写在上面忽略了的DomainService中
1 using System; 2 using SmartSql.DbSession; 3 using SmartSqlSampleChapterThree.Entity; 4 using SmartSqlSampleChapterThree.Repository; 5 6 namespace SmartSqlSampleChapterThree.DomainService 7 { 8 public class NormalUserDomainService : IUserDomainService 9 { 10 private const string DEFAULT_AVATAR = "https://smartsql.net/logo.png"; 11 12 private readonly IUserRepository _userRepository; 13 private readonly IUserDetailRepository _userDetailRepository; 14 private readonly ITransaction _transaction; 15 16 public NormalUserDomainService(IUserRepository userRepository, IUserDetailRepository userDetailRepository, ITransaction transaction) 17 { 18 _userRepository = userRepository; 19 _userDetailRepository = userDetailRepository; 20 _transaction = transaction; 21 } 22 23 public User Register(string loginName, string password, string nickname) 24 { 25 try 26 { 27 _transaction.BeginTransaction(); 28 var user = new User 29 { 30 LoginName = loginName, 31 Password = password, 32 Status = 1, 33 CreateTime = DateTime.Now, 34 ModifiedTime = DateTime.Now 35 }; 36 37 user.Id = _userRepository.Insert(user); 38 39 _userDetailRepository.Insert(new UserDetail 40 { 41 UserId = user.Id, 42 Nickname = nickname, 43 Avatar = DEFAULT_AVATAR, 44 Sex = null, 45 CreateTime = DateTime.Now, 46 ModifiedTime = DateTime.Now 47 }); 48 49 _transaction.CommitTransaction(); 50 return user; 51 } 52 catch 53 { 54 _transaction.RollbackTransaction(); 55 throw; 56 } 57 } 58 59 // use transaction on repository's sql mapper 60 public User RegisterUseRepository(string loginName, string password, string nickname) 61 { 62 try 63 { 64 _userRepository.SqlMapper.BeginTransaction(); 65 66 var user = new User 67 { 68 LoginName = loginName, 69 Password = password, 70 Status = 1, 71 CreateTime = DateTime.Now, 72 ModifiedTime = DateTime.Now 73 }; 74 75 user.Id = _userRepository.Insert(user); 76 77 _userDetailRepository.Insert(new UserDetail 78 { 79 UserId = user.Id, 80 Nickname = nickname, 81 Avatar = DEFAULT_AVATAR, 82 Sex = null, 83 CreateTime = DateTime.Now, 84 ModifiedTime = DateTime.Now 85 }); 86 87 _userRepository.SqlMapper.CommitTransaction(); 88 return user; 89 } 90 catch 91 { 92 _userRepository.SqlMapper.RollbackTransaction(); 93 throw; 94 } 95 } 96 } 97 }
在这个类中,我实现了两次事务调用。在第一个方法中我们使用ITransaction接口提供的方法,调用了Begin-Commit-Rollback的事务过程。
第二个方法中,我直接使用了Repository.SqlMap.BeginTransaction(),这是因为IRepository包含了一个ISqlMap,而ISqlMap同时继承了ITransaction。所以本质上这两种方式是等价的。
三、AOP
1. Nuget依赖
SmartSql有一个独立的Nuget包来支持AOP实现。名字就叫“SmartSql.AOP”
2. DomainService
使用了AOP后,我们的业务代码就可以干净很多。只需要在方法前加上[Transaction]特性就可以了。只要方法体中抛出异常,事务即会回滚。另外需要注意的地方是,AOP方法是需要用上virtual虚方法标识的。
[Transaction] public virtual User Register(string loginName, string password, string nickname)
{
var user = new User { LoginName = loginName, Password = password, Status = 1, CreateTime = DateTime.Now, ModifiedTime = DateTime.Now };
user.Id = _userRepository.Insert(user);
_userDetailRepository.Insert(new UserDetail { UserId = user.Id, Nickname = nickname, Avatar = DEFAULT_AVATAR, Sex = null, CreateTime = DateTime.Now, ModifiedTime = DateTime.Now });
return user;
}
3. 原生配置
在Startup中稍稍修改一下ConfigureServices即可。
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); /// 服务注册 Begin /// 服务注册 End return services.BuildAspectInjectorProvider(); }
看一下上面代码你会发现,只需要为ConfigureServices方法加一个IServiceProvider返回值。并在方法最后加一句return就可以了。
4. Autofac配置
在Autofac中与原生相比,略微有一些不同。
public IServiceProvider ConfigureServices(IServiceCollection services) { services.AddMvc(); /// 服务注册 Begin /// 服务注册 End // autofac var builder = new ContainerBuilder(); builder.RegisterDynamicProxy(config => { config.Interceptors .AddTyped<TransactionAttribute>(Predicates.ForNameSpace("SmartSqlSampleChapterThree.DomainService")); }); builder.Populate(services); var container = builder.Build(); return new AutofacServiceProvider(container); }
我在项目中很少使用Autofac,所以对它的特性也不是很了解。只是按照文档做了最简单的实现,这里还要特别感谢交流群的小伙伴(QQ群号:604762592)。是群里的一个小伙伴在使用Autofac的时候分享了他的使用方式。
这里在原生的基础上,创建一个Autofac容器构建器,并在构建器中注册一个动态代理。然后在拦截器中加上TransactionAttribute就可以了。
三、结语
以上就是SmartSql中事务处理的一些方法,希望可以帮助到你。