zoukankan      html  css  js  c++  java
  • net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作

    系列目录

    1.net core天马行空系列:原生DI+AOP实现spring boot注解式编程

            哈哈哈哈,大家好,我就是那个高产似母猪的三合,长久以来,我一直在思考,如何才能实现高效而简洁的仓储模式(不是DDD里的仓储,更准确的说就是数据库表的mapper),实现spring boot里那样利用注解实现事物操作,日有所思,终有所得,本篇文章浓缩了我对于仓储模式和工作单元模式理解的精华,希望能对大家有所帮助,如果哪里说错了,也希望大家不吝赐教。由于ef core本身就实现了这2种模式,再在ef core的基础上进行封装就失去了学习的意义,所以本文用到的是ORM方案是dapper+dapper.contrib, 这2个库皆出自名门stackexchange,也就是大名鼎鼎的爆栈啦,他们出品的库还有StackExchange.Redis,所以品质自不用说,开始正文前,先在nuget上安装这2个库。BTW,动态代理,注解式编程,AOP贯穿本系列始终,no bb,正文开始。

    1.定义用到的类

    上次讲飙车,这次我们讲,去加油站加油,加油这个过程呢,存在一个事物操作,那就是,加油站必须给我加足够的油,我才给他付钱,有点像银行转账,那么引申出2张表,汽车油量表(oilQuantity)和现金余额表(cashBalance),对应的表结构和实体类如下,都比较简单,除了主键id,oilQuantity表只有一个油量quantity字段,cashBalance表只有一个余额balance字段,数据库使用的是mysql,实体类的注解TableAttribute使用的命名空间是System.ComponentModel.DataAnnotations.Schema。

    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级别,同理,长链接的事务开启后也会被保存,用来在仓储中实现事物操作。

     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代码如下:

      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主要就是初始化金额和油量,然后进行加减操作,最后更新。

       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>,然后注入自己的特殊仓储就行了。

            如果这篇文章对你有所帮助,不妨点个赞咯。

  • 相关阅读:
    【JAVA
    【Android
    【开发工具
    【开发工具
    【开发工具
    【Android
    【Android
    【JavaEE】之MyBatis查询缓存
    【JavaEE】之MyBatis逆向工程的使用
    新的起点 Entry KINGSOFT
  • 原文地址:https://www.cnblogs.com/hezp/p/11434046.html
Copyright © 2011-2022 走看看