手工搭建基于ABP的框架 - 工作单元以及事务管理 (2)

UnitOfWork,管理数据库Session的生命期。数据库Session和事务的创建、销毁都封装在这里。这里的一个问题是何时创建数据库Session。一个自然的想法是在BeginUow创建。然而这并不是一个很好的方式,会产生如下问题:1、默认情况下,Controllers、Application Services和Repositories都会开启工作单元,也就是说,一次HTTP请求可能会多次开启工作单元,导致过多地创建数据库Session,甚至导致数据库被锁;2、即使某个接口不需要访问数据库,工作单元仍然会创建数据库Session,浪费资源。正确的做法是在需要获取数据库Session的时候才进行创建。在下面的实现中,我们将在UnitOfWork实现一个GetOrCreateSession方法来获取数据库Session。该方法在第一次调用时创建一个数据库Session并开启事务,后续调用则返回前面已创建的数据库Session。后面UnitOfWorkLocalDbSessionProvider将调用这个方法来获取数据库Session。

public class UnitOfWork : UnitOfWorkBase, ITransientDependency { public ILocalDbSessionConfiguration DbSessionConfiguration { get; } private ISession _session; public UnitOfWork( IConnectionStringResolver connectionStringResolver, IUnitOfWorkDefaultOptions defaultOptions, IUnitOfWorkFilterExecuter filterExecuter, ILocalDbSessionConfiguration localDbSessionConfiguration) : base(connectionStringResolver, defaultOptions, filterExecuter) { DbSessionConfiguration = localDbSessionConfiguration; } public ISession GetOrCreateSession() { if (_session == null) { _session = DbSessionConfiguration.SessionFactory.OpenSession(); _session.BeginTransaction(); } return _session; } public override void SaveChanges() { _session?.Flush(); } public override Task SaveChangesAsync() { // 我们不用异步Action,就不实现这个方法了。 throw new NotImplementedException(); } protected override void CompleteUow() { SaveChanges(); _session?.Transaction?.Commit(); } protected override Task CompleteUowAsync() { // 我们不用异步Action,就不实现这个方法了。 throw new NotImplementedException(); } protected override void DisposeUow() { _session?.Transaction?.Dispose(); _session?.Dispose(); } } internal static class UnitOfWorkExtensions { public static ISession GetSession(this IActiveUnitOfWork unitOfWork) { if (unitOfWork == null) { throw new ArgumentNullException(nameof(unitOfWork)); } if (!(unitOfWork is UnitOfWork)) { throw new ArgumentException("unitOfWork is not type of " + typeof(UnitOfWork).FullName, nameof(unitOfWork)); } return (unitOfWork as UnitOfWork).GetOrCreateSession(); } }

UnitOfWorkLocalDbSessionProvider,单例。通过当前的工作单元来提供数据库Session。

public class UnitOfWorkLocalDbSessionProvider : ISessionProvider, ISingletonDependency { private readonly ICurrentUnitOfWorkProvider _unitOfWorkProvider; public UnitOfWorkLocalDbSessionProvider(ICurrentUnitOfWorkProvider currentUnitOfWorkProvider) { _unitOfWorkProvider = currentUnitOfWorkProvider; } public ISession Session => _unitOfWorkProvider.Current?.GetSession(); }

最后,TweetRepository和TweetQueryService的构造函数用到了旧的LocalDbSessionProvider,这两处也需要改一下:

public TweetRepository() : base(IocManager.Instance.Resolve<UnitOfWorkLocalDbSessionProvider>()) { } public TweetQueryService() : base(IocManager.Instance.Resolve<UnitOfWorkLocalDbSessionProvider>()) { } 使用NHProfiler进行验证

上面实现了工作单元并封装了数据库事务管理。我们需要有方法验证数据库访问时确实开启了事务。NHProfiler是一个能够监视NHibernate生成的SQL语句的工具。我们将使用NHProfiler查看生成的SQL,确认实现了工作单元后确实开启了事务管理。

NHProfiler由两个部分组成:

一个嵌入到我们应用的DLL。这个DLL会在NHibernate访问数据库时往本地socket发送生成的SQL语句。

客户端。这个客户端通过socket接收上面所说的DLL发送的数据并展示。

接下来我们的程序需要做一些小改动。首先MyTweet.Web项目需要引用NHProfiler包里的HibernatingRhinos.Profiler.Appender.dll。或者从Nuget添加NHibernateProfiler.Appender包。如果你从NuGet添加的,则需要确认NuGet包的版本和客户端的版本一致。

添加引用后,我们还需要在入口函数Application_Start加上HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize()这句语句来开启NHProfiler的监听:

protected override void Application_Start(object sender, EventArgs e) { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); IocManager.Instance.IocContainer.AddFacility<LoggingFacility>( f => f.UseAbpLog4Net().WithConfig("log4net.config")); base.Application_Start(sender, e); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxjjx.html