码迷,mamicode.com
首页 > Web开发 > 详细

net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作

时间:2020-04-24 12:51:45      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:mapper   dispose   request   其他   思考   stp   def   初始   无法   

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

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

net core天马行空系列: 泛型仓储和声明式事物实现最优雅的crud操作

标签:mapper   dispose   request   其他   思考   stp   def   初始   无法   

原文地址:https://www.cnblogs.com/lonelyxmas/p/12766603.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!