前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#中的工作单元(Unit Of Work)

C#中的工作单元(Unit Of Work)

原创
作者头像
软件架构师Michael
发布2023-05-26 09:30:22
9230
发布2023-05-26 09:30:22
举报
文章被收录于专栏:软件工程师Michael

什么是Unit Of Work

Unit of Work: Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems. —— Martin Fowler

按照Martin Fowler的说法,Unit Of Work实际也就是其字面意思,工作单元。在业务上,需要一个工作单元的稳定性,完整性。类似于数据库中的事务,以防在业务操作单元中出了意外,可以回滚。

更为直白的意思,就是在一个业务操作的方法中,可能对数据库的多个实体对象进行了删除,修改,新增等操作;那么我们希望它们的改动是统一,一致的。不能在在改一部分的情况下,另一部分没有被改到。类似数据库事务的经典场景:一个人去银行转钱的问题,不能钱在对方账户到账了,而自己的账户余额还没有减少。这样就造成了数据的不一致,也就可能造成了不可预期的后果。

C#中Unit Of Work的实现(基于EF)

UnitOfWorkAttribute(特性的定义)

代码语言:javascript
复制
 public sealed class UnitOfWorkAttribute : Attribute
    {
        public UnitOfWorkAttribute()
        {
        }

        public UnitOfWorkAttribute(bool ensureTransaction)
        {
            EnsureTransaction = ensureTransaction;
        }

        /// <summary>
        /// 确保事务可用
        /// <para>此方法为了解决静态类方式操作数据库的问题</para>
        /// </summary>
        public bool EnsureTransaction { get; set; } = false;
    }

UnitOfWorkFilter(AOP Filter的定义)

在这里使用UnitOfWorkFilter的目的是为了使用AOP,面向切面编程的思想。在具体的业务逻辑中,不直接在逻辑中使用数据库的事务代码,而在业务的入口使用Filter将逻辑进行包裹,以达到Uinit Of Work的目的。

代码语言:javascript
复制
public sealed class UnitOfWorkFilter : IAsyncActionFilter
    {
        private readonly DbContext? _dbContext;

        public UnitOfWorkFilter(DbContext? dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // 获取动作方法描述器
            var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;
            var method = actionDescriptor?.MethodInfo;

            // 判断是否贴有工作单元特性
            if (method == null || !method.IsDefined(typeof(UnitOfWorkAttribute), true))
            {
                // 调用方法
                var resultContext = await next();
            }
            else
            {
                // 获取工作单元特性
                var unitOfWorkAttribute = method.GetCustomAttribute<UnitOfWorkAttribute>();
                // 判断,以决定是否使用数据库事务。
                if (unitOfWorkAttribute != null && unitOfWorkAttribute.EnsureTransaction)
                {
                    if (_dbContext == null)
                    {
                        throw new Exception($"{nameof(DbContext)} is null.");
                    }
                    using (var tran = _dbContext.Database.BeginTransaction())
                    {
                        try
                        {
                            // 调用方法
                            var resultContext = await next();
                            await tran.CommitAsync();
                        }
                        catch (Exception ex)
                        {
                            await tran.RollbackAsync();
                            throw new Exception(ex.Message);
                        }
                    }
                }
            }
        }
    }

最后,UnitOfWorkFilter的使用方式同其它的Attribute一样,可以全局注册,也可以在相应的Action或者Controller上使用。

代码语言:javascript
复制
// services中注册
    services.AddScoped<UnitOfWorkFilter>();
    // Controller上使用
    [ServiceFilter(typeof(UnitOfWorkFilter))]
    public class TestControllerBase : ControllerBase
    {
        ...
    }

【小结】

任何一门编程语言,需要在实践中千锤百炼,方能真正掌握。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是Unit Of Work
  • C#中Unit Of Work的实现(基于EF)
    • UnitOfWorkAttribute(特性的定义)
      • UnitOfWorkFilter(AOP Filter的定义)
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档