Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

简化UnitOfWork的设计,整合UnitOfWorkManager #215

Closed
gmf520 opened this issue Mar 18, 2021 · 0 comments
Closed

简化UnitOfWork的设计,整合UnitOfWorkManager #215

gmf520 opened this issue Mar 18, 2021 · 0 comments
Labels
Breaked Changes ⚡ 更新有破坏性,对现有业务实现有较大影响 Feature 🔨 新功能,新特性 Finished ✔️ 实现并完工
Milestone

Comments

@gmf520
Copy link
Member

gmf520 commented Mar 18, 2021

您的功能请求与现有问题有关吗?请描述

同为ScopedIUnitOfWorkManagerIUnitOfWorkDbContextBase的设计存在重复,参考 #120 的建议进行简化

描述您想要的解决方案

需求分析

  • 在一个Scoped生命周期中,使用一个IUnitOfWork管理所有的DbContext实例
  • 多个DbContext实例以DbConnection分组,同一个连接对象DbConnection的多个上下文共享事务
  • 建立相同DbConnection的数据上下文DbContext缓存,获取数据上下文实例时,优先从缓存获取,不存在再从IServiceProvider中解析
  • IUnitOfWork需要调用IUnitOfWork.EnableTransaction()手动开启事务,才能执行手动事务流程,否则使用 EFCore 默认的自动事务
  • 事务业务需要使用IUnitOfWork.EnableTransaction()IUnitOfWork.Commit()进行包裹
  • 调用IUnitOfWork.EnableTransaction()时,给事务层次打标记,以处理事务嵌套的问题,IUnitOfWork.Commit()提交事务时只提交最外一层的事务

工作单元设计 IUnitOfWork

   /// <summary>
    /// 定义一个单元操作内的功能,管理单元操作内涉及的所有上下文对象及其事务
    /// </summary>
    public interface IUnitOfWork : IDisposable
    {
        /// <summary>
        /// 获取 是否已提交
        /// </summary>
        bool HasCommitted { get; }

        /// <summary>
        /// 启用事务,事务代码写在 UnitOfWork.EnableTransaction() 与 UnitOfWork.Commit() 之间
        /// </summary>
        void EnableTransaction();

        /// <summary>
        /// 获取指定数据上下文类型的实例
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TKey">实体主键类型</typeparam>
        /// <returns><typeparamref name="TEntity"/>所属上下文类的实例</returns>
        IDbContext GetEntityDbContext<TEntity, TKey>() where TEntity : IEntity<TKey>;
        
        /// <summary>
        /// 获取指定数据实体的上下文实例
        /// </summary>
        /// <param name="entityType">实体类型</param>
        /// <returns>实体所属上下文实例</returns>
        IDbContext GetEntityDbContext(Type entityType);

        /// <summary>
        /// 获取指定类型的上下文实例
        /// </summary>
        /// <param name="dbContextType">上下文类型</param>
        /// <returns></returns>
        IDbContext GetDbContext(Type dbContextType);

        /// <summary>
        /// 对数据库连接开启事务或应用现有同连接对象的上下文事务
        /// </summary>
        /// <param name="context">数据上下文</param>
        void BeginOrUseTransaction(IDbContext context);

        /// <summary>
        /// 提交当前上下文的事务更改
        /// </summary>
        void Commit();

        /// <summary>
        /// 回滚所有事务
        /// </summary>
        void Rollback();

#if NET5_0

        /// <summary>
        /// 对数据库连接开启事务
        /// </summary>
        /// <param name="context">数据上下文</param>
        /// <param name="cancellationToken">异步取消标记</param>
        /// <returns></returns>
        Task BeginOrUseTransactionAsync(IDbContext context, CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步提交当前上下文的事务更改
        /// </summary>
        /// <returns></returns>
        Task CommitAsync(CancellationToken cancellationToken = default);

        /// <summary>
        /// 异步回滚所有事务
        /// </summary>
        /// <returns></returns>
        Task RollbackAsync(CancellationToken cancellationToken = default);
#endif
    }

工作单元使用

1. 使用ServiceLifetime.Scoped生命周期将IUnitOfWork注册到DI

//注册IUnitOfWork
services.TryAddScoped<IUnitOfWork, UnitOfWork>();
//注册数据上下文
services.AddOsharpDbContext<DefaultDbContext>();

2. 从DI解析IUnitOfWork对象,将事务代码包裹在 IUnitOfWork.EnableTransction()IUnitOfWork.Commit() 之间

IUnitOfWork unitOfWork = serviceProvider.GetService<IUnitOfWork>();
// 如果需要事务操作,要手动启用事务
unitOfWork.EnableTransaction();

// do something 事务的业务操作

unitOfWork.Commit();

框架内提供了一个简化的获取IUnitOfWork的扩展方法

/// <summary>
/// 从服务提供者获取 <see cref="IUnitOfWork"/>
/// </summary>
/// <param name="provider">服务提供者</param>
/// <param name="enableTransaction">是否启用事务</param>
/// <returns></returns>
public static IUnitOfWork GetUnitOfWork(this IServiceProvider provider, bool enableTransaction = false)
{
    IUnitOfWork unitOfWork = provider.GetRequiredService<IUnitOfWork>();
    if (enableTransaction)
    {
        unitOfWork.EnableTransaction();
    }
    return unitOfWork;
}

调用时,按传入的enableTransaction决定是否启用事务

IUnitOfWork unitOfWork = provider.GetUnitOfWork(enableTransaction: true);

注意:IUnitOfWork.EnableTransction()IUnitOfWork.Commit() 必须成对出现,否则会出现事务无法正常提交的问题

工作单元嵌套

利益于IUnitOfWork.EnableTransaction()的设计,IUnitOfWork事务已经支持嵌套,只要保持层次结构中IUnitOfWork.EnableTransction()IUnitOfWork.Commit() 成对出现,事务提交时,如果不是最外层工作单元,事务提交会跳过,直到最外层事务时,才会执行真正的IUnitOfWork.Commit()。因此,在业务实现时,可以按需要随意设计事务功能,在事务互相调用时,不会因为事务嵌套产生多次提交事务的冲突。

public class Foo1Service
{
    public void FooMethod()
    {
        IUnitOfWork unitOfWork = serviceProvider.GetService<IUnitOfWork>();
        unitOfWork.EnableTransaction();

        // do something 事务的业务操作

        unitOfWork.Commit();
    }
}

public class Foo2Service
{
    public void FooMethod()
    {
        IUnitOfWork unitOfWork = serviceProvider.GetService<IUnitOfWork>();
        unitOfWork.EnableTransaction();

        // do something 事务的业务操作

        unitOfWork.Commit();
    }
}

在Controller中调用Service

public class FooController
{
    private readonly Foo1Service _foo1Service;
    private readonly Foo2Service _foo2Service;

    public FooController(Foo1Service foo1Service, Foo2Service foo2Service)
    {
        _foo1Service = foo1Service;
        _foo2Service = foo2Service;
    }

    public void FooAction()
    {
        IUnitOfWork unitOfWork = serviceProvider.GetService<IUnitOfWork>();
        unitOfWork.EnableTransaction();

        // do something 事务的业务操作
        _foo1Service.FooMethod();
        _foo2Service.FooMethod();

        unitOfWork.Commit();
    }
}

如上,FooController调用了Foo1ServiceFoo2Service形成事务嵌套,Foo1ServiceFoo2Service的事务提交将被跳过,直到FooController中的unitOfWork.Commit(),事务提交才真正的执行。

@gmf520 gmf520 added Breaked Changes ⚡ 更新有破坏性,对现有业务实现有较大影响 Feature 🔨 新功能,新特性 labels Mar 18, 2021
@gmf520 gmf520 added this to the v5.0.4 milestone Mar 18, 2021
@gmf520 gmf520 changed the title 简化UnitOfWork的设计 简化UnitOfWork的设计,整合UnitOfWorkManager Mar 19, 2021
@gmf520 gmf520 closed this as completed Mar 19, 2021
@gmf520 gmf520 added the Finished ✔️ 实现并完工 label Mar 19, 2021
@gmf520 gmf520 pinned this issue Mar 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Breaked Changes ⚡ 更新有破坏性,对现有业务实现有较大影响 Feature 🔨 新功能,新特性 Finished ✔️ 实现并完工
Projects
None yet
Development

No branches or pull requests

1 participant