net core天馬行空系列: 泛型倉儲和聲明式事物實現最優雅的crud操做

系列目錄

1.net core天馬行空系列:原生DI+AOP實現spring boot註解式編程html

        哈哈哈哈,你們好,我就是那個高產似母豬的三合,長久以來,我一直在思考,如何才能實現高效而簡潔的倉儲模式(不是DDD裏的倉儲,更準確的說就是數據庫表的mapper),實現spring boot裏那樣利用註解實現事物操做,日有所思,終有所得,本篇文章濃縮了我對於倉儲模式和工做單元模式理解的精華,但願能對你們有所幫助,若是哪裏說錯了,也但願你們不吝賜教。因爲ef core自己就實現了這2種模式,再在ef core的基礎上進行封裝就失去了學習的意義,因此本文用到的是ORM方案是dapper+dapper.contrib, 這2個庫皆出自名門stackexchange,也就是大名鼎鼎的爆棧啦,他們出品的庫還有StackExchange.Redis,因此品質自不用說,開始正文前,先在nuget上安裝這2個庫。BTW,動態代理,註解式編程,AOP貫穿本系列始終,no bb,正文開始。mysql

1.定義用到的類

上次講飆車,此次咱們講,去加油站加油,加油這個過程呢,存在一個事物操做,那就是,加油站必須給我加足夠的油,我纔給他付錢,有點像銀行轉帳,那麼引伸出2張表,汽車油量表(oilQuantity)和現金餘額表(cashBalance),對應的表結構和實體類以下,都比較簡單,除了主鍵id,oilQuantity表只有一個油量quantity字段,cashBalance表只有一個餘額balance字段,數據庫使用的是mysql,實體類的註解TableAttribute使用的命名空間是System.ComponentModel.DataAnnotations.Schema。spring

CREATE TABLE test.oilQuantity (
    id INT NOT NULL AUTO_INCREMENT,
    quantity DECIMAL NULL,
    CONSTRAINT caroil_pk PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8
COLLATE=utf8_general_ci;
CREATE TABLE test.cashBalance (
    id INT NOT NULL AUTO_INCREMENT,
    balance DECIMAL NOT NULL,
    CONSTRAINT cashbalance_pk PRIMARY KEY (id)
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8
COLLATE=utf8_general_ci;

 

 [Table("OilQuantity")]
    public class OilQuantity
    {
        [Key]
        public int Id { set; get; }
        /// <summary>
        /// 油量
        /// </summary>
        public decimal Quantity { set; get; }
    }
 [Table("CashBalance")]
    public class CashBalance
    {
        [Key]
        public int Id { set; get; }
        /// <summary>
        /// 餘額
        /// </summary>
        public decimal Balance { set; get; }
    }

定義數據庫連接工廠類接口IDbFactory和他的實現類DbFactory,這個類主要負責數據庫連接的建立,連接分爲2種,一種是短連接,不開啓事物的時候使用,用完即毀,每次得到都是全新的連接,另外一種是長連接,用在事物操做中,DbFactory自己註冊爲scope級別,長連接建立後會保存在DbFactory的屬性中,因此變相的實現了scope級別,同理,長連接的事務開啓後也會被保存,用來在倉儲中實現事物操做。sql

 public interface IDbFactory:IDisposable
    {
        /// <summary>
        /// 長連接
        /// </summary>
        IDbConnection LongDbConnection { get; }

        /// <summary>
        /// 長連接的事物
        /// </summary>
        IDbTransaction LongDbTransaction { get; }

        /// <summary>
        /// 短連接
        /// </summary>
        IDbConnection ShortDbConnection { get; }

        /// <summary>
        /// 開啓事務
        /// </summary>
        void BeginTransaction();
    }
  /// <summary>
    /// 負責生成和銷燬數據庫連接
    /// </summary>
    public class DbFactory:IDbFactory
    {
        [Value("MysqlConnectionStr")]
        public string MysqlConnectionStr { set; get; }

        /// <summary>
        /// 長鏈接
        /// </summary>
        public IDbConnection LongDbConnection { private set; get; }

        /// <summary>
        /// 長鏈接的事物
        /// </summary>
        public IDbTransaction LongDbTransaction { private set; get; }

        /// <summary>
        /// 短連接
        /// </summary>
        public IDbConnection ShortDbConnection
        {
            get
            {
                var dbConnection = new MySqlConnection(MysqlConnectionStr);
                dbConnection.Open();
                return dbConnection;
            }
        }

        /// <summary>
        /// 開啓事務
        /// </summary>
        /// <returns></returns>
        public void BeginTransaction()
        {
            if (LongDbConnection == null)
            {
                LongDbConnection = new MySqlConnection(MysqlConnectionStr);
                LongDbConnection.Open();
                LongDbTransaction = LongDbConnection.BeginTransaction();
            }
        }

        public void Dispose()
        {
            LongDbTransaction?.Dispose();
            if (LongDbConnection?.State != ConnectionState.Closed)
            {
                LongDbConnection?.Close();
            }
            LongDbConnection?.Dispose();
            LongDbTransaction = null;
            LongDbConnection = null;
        }
    }

 定義工做單元接口IUnitOfWork和他的實現類UnitOfWork,能夠看到,IUnitOfWork中有一個引用次數ActiveNumber的屬性,這個屬性的做用主要是,若是一個標註了[Transactional]的方法裏嵌套了另外一個標註了[Transactional]的方法,咱們就能夠經過計數來確認,具體誰纔是最外層的方法,從而達到不在內層方法裏開啓另外一個事物,而且在內層方法結束時不會提早提交事務的效果。同時呢,UnitOfWork只負責與事務有關的操做,其餘建立連接,建立事物等操做,都是由注入的IDbFactory完成的。數據庫

 public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// 引用次數,開啓一次事物加+1,當次數爲0時提交,主要是爲了防止事物嵌套
        /// </summary>
        int ActiveNumber { get; set; }

        /// <summary>
        /// 開啓事務
        /// </summary>
        void BeginTransaction();
        
        /// <summary>
        /// 提交
        /// </summary>
        void Commit();

        /// <summary>
        /// 事物回滾
        /// </summary>
        void RollBack();
    }
 public class UnitOfWork : IUnitOfWork
    {
        /// <summary>
        /// 工做單元引用次數,當次數爲0時提交,主要爲了防止事物嵌套
        /// </summary>
        public int ActiveNumber { get; set; } = 0;

        [Autowired]
        public IDbFactory DbFactory { set; get; }

        public void BeginTransaction()
        {
            if (this.ActiveNumber == 0)
            {
                DbFactory.BeginTransaction();
                Console.WriteLine("開啓事務");
            }
            this.ActiveNumber++;
        }

        public void Commit()
        {
            this.ActiveNumber--;
            if (this.ActiveNumber == 0)
            {
                if (DbFactory.LongDbConnection != null)
                {
                    try
                    {
                        DbFactory.LongDbTransaction.Commit();
                    }
                    catch (Exception e)
                    {
                        DbFactory.LongDbTransaction.Rollback();
                        Console.WriteLine(e);
                        throw;
                    }
                    finally
                    {
                        this.Dispose();
                    }
                }

                Console.WriteLine("提交事務");
            }

        }

        public void Dispose()
        {
            DbFactory.Dispose();
        }

        public void RollBack()
        {
            if (this.ActiveNumber > 0 && DbFactory.LongDbTransaction != null)
            {
                try
                {
                    DbFactory.LongDbTransaction.Rollback();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    throw;
                }
            }

            Console.WriteLine("回滾事務");
        }
    }

泛型倉儲接口IRepository<T>和他的實現類BaseRepository<T>,爲了偷懶,只寫了同步部分,異步同理,若使用異步,攔截器也要使用異步攔截器。BaseRepository中經過屬性注入了IUnitOfWork和IDbFactory,IUnitOfWork主要負責告訴倉儲,該使用長鏈接仍是短連接,IDbFactory負責提供具體的連接和事物,而更細節的crud操做,則都是由dapper和dapper.contrib完成的。看代碼var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;能夠看到經過判斷uow的引用計數ActiveNumber 來判斷使用的是長連接仍是短連接,而且若是ActiveNumber==0的話,在數據庫操做結束後就會釋放掉連接。編程

 public interface IRepository<T> where T : class
    {
        IList<T> GetAll();
        T Get(object id);

        T Insert(T t);
        IList<T> Insert(IList<T> t);

        void Update(T t);
        void Update(IList<T> t);

        void Delete(IList<T> t);
        void Delete(T t);
    }
 public class BaseRepository<T> : IRepository<T> where T : class
    {
        [Autowired]
        public IUnitOfWork Uow { set; get; }

        [Autowired]
        public IDbFactory DbFactory { set; get; }

        public IList<T> GetAll()
        {
            var dbcon = DbFactory.ShortDbConnection;

            var result = dbcon.GetAll<T>().ToList();

            dbcon.Close();
            dbcon.Dispose();

            return result;
        }

        public T Get(object id)
        {
            var dbcon = DbFactory.ShortDbConnection;

            var result = dbcon.Get<T>(id);

            dbcon.Close();
            dbcon.Dispose();

            return result;
        }

        public T Insert(T t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Insert(t, DbFactory.LongDbTransaction);

            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

            return t;
        }

        public IList<T> Insert(IList<T> t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Insert(t, DbFactory.LongDbTransaction);

            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

            return t;
        }

        public void Delete(T t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Delete(t, DbFactory.LongDbTransaction);

            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

        }

        public void Delete(IList<T> t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Delete(t, DbFactory.LongDbTransaction);

            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

        }

        public void Update(T t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Update(t, DbFactory.LongDbTransaction);

            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

        }

        public void Update(IList<T> t)
        {
            var dbcon = Uow.ActiveNumber == 0 ? DbFactory.ShortDbConnection : DbFactory.LongDbConnection;
            dbcon.Update(t, DbFactory.LongDbTransaction);
            if (Uow.ActiveNumber == 0)
            {
                dbcon.Close();
                dbcon.Dispose();
            }

        }
    }
View Code

事物攔截器TransactionalInterceptor,在方法開始前,若是攔截到的方法具備[TransactionalAttribute]註解,則經過uow開啓事務,在方法結束後,若是攔截到的方法具備[TransactionalAttribute]註解,則經過uow結束事務。緩存

/// <summary>
    /// 事物攔截器
    /// </summary>
    public class TransactionalInterceptor : StandardInterceptor
    {
        private IUnitOfWork Uow { set; get; }

        public TransactionalInterceptor(IUnitOfWork uow)
        {
            Uow = uow;
        }

        protected override void PreProceed(IInvocation invocation)
        {
            Console.WriteLine("{0}攔截前", invocation.Method.Name);

            var method = invocation.MethodInvocationTarget;
            if (method != null && method.GetCustomAttribute<TransactionalAttribute>() != null)
            {
                Uow.BeginTransaction();
            }
        }

        protected override void PerformProceed(IInvocation invocation)
        {
            invocation.Proceed();
        }

        protected override void PostProceed(IInvocation invocation)
        {
            Console.WriteLine("{0}攔截後, 返回值是{1}", invocation.Method.Name, invocation.ReturnValue);

            var method = invocation.MethodInvocationTarget;
            if (method != null && method.GetCustomAttribute<TransactionalAttribute>() != null)
            {
                Uow.Commit();
            }
        }
    }

IServiceCollection靜態擴展類SummerBootExtentions.cs,和上一篇比較,主要就是添加了AddSbRepositoryService方法,這個方法主要經過反射得到由[TableAttribute]標註的實體類,並向IServiceCollection中添加相應的的倉儲接口和相應的倉儲實現類,爲何不用services.AddScoped(typeof(IRepository<>),typeof(BaseRepository<>));這種方法注入泛型倉儲呢?由於net core原生DI並不支持泛型注入的工廠委託建立,那麼就沒法實現動態代理了,因此採用變通的方法,將通用泛型接口,轉成具體的泛型接口,SummerBootExtentions.cs的另外一個變更就是將ProxyGenerator註冊成單例了,這樣就能夠利用緩存,提升建立動態代理的性能,SummerBootExtentions.cs代碼以下:cookie

  public static class SummerBootExtentions
    {
        /// <summary>
        /// 瞬時
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <typeparam name="TImplementation"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbTransient<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Transient, interceptorTypes);
        }

        /// <summary>
        /// 瞬時
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbTransient(this IServiceCollection services, Type serviceType,
            Type implementationType, params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, implementationType, ServiceLifetime.Transient, interceptorTypes);
        }

        /// <summary>
        /// 請求級別
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <typeparam name="TImplementation"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbScoped<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Scoped, interceptorTypes);
        }

        /// <summary>
        /// 請求級別
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbScoped(this IServiceCollection services, Type serviceType,
            Type implementationType, params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, implementationType, ServiceLifetime.Scoped, interceptorTypes);
        }

        /// <summary>
        /// 單例
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <typeparam name="TImplementation"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbSingleton<TService, TImplementation>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), typeof(TImplementation), ServiceLifetime.Singleton, interceptorTypes);
        }

        /// <summary>
        /// 單例
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="implementationType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbSingleton(this IServiceCollection services, Type serviceType,
            Type implementationType, params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, implementationType, ServiceLifetime.Singleton, interceptorTypes);
        }

        public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType, Type implementationType,
            ServiceLifetime lifetime, params Type[] interceptorTypes)
        {
            services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime));

            object Factory(IServiceProvider provider)
            {
                var target = provider.GetService(implementationType);
                var properties = implementationType.GetTypeInfo().DeclaredProperties;

                foreach (PropertyInfo info in properties)
                {
                    //屬性注入
                    if (info.GetCustomAttribute<AutowiredAttribute>() != null)
                    {
                        var propertyType = info.PropertyType;
                        var impl = provider.GetService(propertyType);
                        if (impl != null)
                        {
                            info.SetValue(target, impl);
                        }
                    }

                    //配置值注入
                    if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute)
                    {
                        var value = valueAttribute.Value;
                        if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService)
                        {
                            var pathValue = configService.GetSection(value).Value;
                            if (pathValue != null)
                            {
                                var pathV = Convert.ChangeType(pathValue, info.PropertyType);
                                info.SetValue(target, pathV);
                            }
                        }

                    }
                }

                List<IInterceptor> interceptors = interceptorTypes.ToList()
                    .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor);

                var proxyGenerator = provider.GetService<ProxyGenerator>();
                var proxy = proxyGenerator.CreateInterfaceProxyWithTarget(serviceType, target, interceptors.ToArray());

                return proxy;
            };

            var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime);
            services.Add(serviceDescriptor);

            return services;
        }

        /// <summary>
        /// 瞬時
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbTransient<TService>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), ServiceLifetime.Transient, interceptorTypes);
        }

        /// <summary>
        /// 瞬時
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbTransient(this IServiceCollection services, Type serviceType, params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, ServiceLifetime.Transient, interceptorTypes);
        }

        /// <summary>
        /// 請求
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbScoped<TService>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), ServiceLifetime.Scoped, interceptorTypes);
        }

        /// <summary>
        /// 請求
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbScoped(this IServiceCollection services, Type serviceType,
             params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, ServiceLifetime.Scoped, interceptorTypes);
        }

        /// <summary>
        /// 單例
        /// </summary>
        /// <typeparam name="TService"></typeparam>
        /// <param name="services"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbSingleton<TService>(this IServiceCollection services, params Type[] interceptorTypes)
        {
            return services.AddSbService(typeof(TService), ServiceLifetime.Singleton, interceptorTypes);
        }

        /// <summary>
        /// 單例
        /// </summary>
        /// <param name="services"></param>
        /// <param name="serviceType"></param>
        /// <param name="interceptorTypes"></param>
        /// <returns></returns>
        public static IServiceCollection AddSbSingleton(this IServiceCollection services, Type serviceType, params Type[] interceptorTypes)
        {
            return services.AddSbService(serviceType, ServiceLifetime.Singleton, interceptorTypes);
        }

        public static IServiceCollection AddSbService(this IServiceCollection services, Type serviceType,
            ServiceLifetime lifetime, params Type[] interceptorTypes)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services));
            if (serviceType == (Type)null)
                throw new ArgumentNullException(nameof(serviceType));

            object Factory(IServiceProvider provider)
            {
                List<IInterceptor> interceptors = interceptorTypes.ToList()
                    .ConvertAll<IInterceptor>(interceptorType => provider.GetService(interceptorType) as IInterceptor);

                var proxyGenerator = provider.GetService<ProxyGenerator>();

                var proxy = proxyGenerator.CreateClassProxy(serviceType, interceptors.ToArray());

                var properties = serviceType.GetTypeInfo().DeclaredProperties;

                foreach (PropertyInfo info in properties)
                {
                    //屬性注入
                    if (info.GetCustomAttribute<AutowiredAttribute>() != null)
                    {
                        var propertyType = info.PropertyType;
                        var impl = provider.GetService(propertyType);
                        if (impl != null)
                        {
                            info.SetValue(proxy, impl);
                        }
                    }

                    //配置值注入
                    if (info.GetCustomAttribute<ValueAttribute>() is ValueAttribute valueAttribute)
                    {
                        var value = valueAttribute.Value;
                        if (provider.GetService(typeof(IConfiguration)) is IConfiguration configService)
                        {
                            var pathValue = configService.GetSection(value).Value;
                            if (pathValue != null)
                            {
                                var pathV = Convert.ChangeType(pathValue, info.PropertyType);
                                info.SetValue(proxy, pathV);
                            }
                        }
                    }
                }

                return proxy;
            };

            var serviceDescriptor = new ServiceDescriptor(serviceType, Factory, lifetime);
            services.Add(serviceDescriptor);

            return services;
        }

        /// <summary>
        /// 添加summer boot擴展
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static IMvcBuilder AddSB(this IMvcBuilder builder)
        {
            if (builder == null)
                throw new ArgumentNullException(nameof(builder));
            ControllerFeature feature = new ControllerFeature();
            builder.PartManager.PopulateFeature<ControllerFeature>(feature);
            foreach (Type type in feature.Controllers.Select<TypeInfo, Type>((Func<TypeInfo, Type>)(c => c.AsType())))
                builder.Services.TryAddTransient(type, type);
            builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, SbControllerActivator>());

            return builder;
        }

        public static IServiceCollection AddSbRepositoryService(this IServiceCollection services, params Type[] interceptorTypes)
        {
            var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(it => it.GetTypes());

            var tableType = types.Where(it => it.GetCustomAttribute<TableAttribute>() != null);

            foreach (var type in tableType)
            {
                var injectServiceType = typeof(IRepository<>).MakeGenericType(type);
                var injectImplType = typeof(BaseRepository<>).MakeGenericType(type);
                services.AddSbScoped(injectServiceType, injectImplType, interceptorTypes);
            }

            return services;
        }
    }
View Code

定義一個加油的服務類接口IAddOilService和接口的實現類AddOilService,能夠從代碼中看到,咱們經過屬性注入添加了CashBalanceRepository和OilQuantityRepository,經過[Transactional]標註AddOil方法,使其成爲事物性操做,AddOil主要就是初始化金額和油量,而後進行加減操做,最後更新。app

   public interface IAddOilService
    {
        void AddOil();
    }
public class AddOilService : IAddOilService
    {
        [Autowired]
        public IRepository<CashBalance> CashBalanceRepository { set; get; }

        [Autowired]
        public IRepository<OilQuantity> OilQuantityRepository { set; get; }

        [Transactional]
        public void AddOil()
        {
            //初始化金額
            var cashBalance = CashBalanceRepository.Insert(new CashBalance() { Balance = 100 });
            //初始化油量
            var oilQuantity = OilQuantityRepository.Insert(new OilQuantity() { Quantity = 5 });

            cashBalance.Balance -= 95;
            oilQuantity.Quantity += 50;

            CashBalanceRepository.Update(cashBalance);
            //throw new Exception("主動報錯");
            OilQuantityRepository.Update(oilQuantity);
        }
    }

修改Startup.cs中的ConfigureServices方法,代碼以下:異步

public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });


            services.AddMvc()
                .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
                .AddSB();

            services.AddSingleton<ProxyGenerator>();

            services.AddSbScoped<Engine>(typeof(TransactionalInterceptor));

            services.AddSbScoped<IUnitOfWork, UnitOfWork>();
            services.AddScoped(typeof(TransactionalInterceptor));

            services.AddSbScoped<ICar, Car>(typeof(TransactionalInterceptor));

            services.AddSbScoped<IDbFactory, DbFactory>();
            services.AddSbRepositoryService(typeof(TransactionalInterceptor));
            services.AddSbScoped<IAddOilService, AddOilService>(typeof(TransactionalInterceptor));
        }

 控制器HomeController

    public class HomeController : Controller
    {
        [Autowired]
        public ICar Car { set; get; }

        [Autowired]
        public IDistributedCache Cache { set; get; }

        [Value("description")]
        public string Description { set; get; }

        [Autowired]
        public IAddOilService AddOilService { set; get; }

        public IActionResult Index()
        {

            var car = Car;

            AddOilService.AddOil();

            Car.Fire();

            Console.WriteLine(Description);

            return View();
        }
    }

  2.效果圖

2.1 清空2張表裏的數據,在AddOil末尾打斷點。

雖然前面執行了insert操做,可是咱們查詢2張表,發現裏面並無新增數據,由於事物還未提交,符合預期。從斷點處繼續執行,而後查詢數據庫。

 

執行完事物後,數據正確,符合預期。

2.2 清空2張表裏的數據,註釋掉AddOil方法的[Transactional]註解,在AddOil末尾打斷點。

查看數據庫,由於沒開啓事務,因此數據已經正確插入到表中,而且因爲oilQuantity倉儲未更新,因此數值正確,從斷點處繼續執行

 

oilQuantity倉儲更新,數值正確,符合預期。

2.3 清空2張表裏的數據,開啓AddOil方法的[Transactional]註解,並在方法中主動拋出一個錯誤。

表中並未添加數據,由於事物未提交,回滾了,符合預期。

BTW,事物的開啓,除了使用[Transactional]註解外,也能夠經過注入uow,手動開啓和提交。

3. 寫在最後

        只須要在數據庫實體類上註解[Table("表名")]就能夠直接使用倉儲了,是否是很簡潔優雅呢?這裏實現的倉儲都是通用的,若是有特殊需求的倉儲,則須要自定義接口和實現類,接口繼承IRepository<T>,實現類繼承BaseRepository<T>,而後注入本身的特殊倉儲就好了。

        若是這篇文章對你有所幫助,不妨點個贊咯。

相關文章
相關標籤/搜索