[Abp 源码分析]六、工作单元的实现

0.简介

在 Abp 框架内部实现了工作单元,在这里讲解一下,什么是工作单元?

Unit Of Work(工作单元)模式用来维护一个由已经被业务事物修改(增加、删除或更新)的业务对象组成的列表。Unit ?Of Work模式负责协调这些修改的持久化工作以及所有标记的并发问题。在数据访问层中采用Unit Of Work模式带来的好处是能够确保数据完整性。如果在持久化一系列业务对象(他们属于同一个事物)的过程中出现问题,那么应该将所有的修改回滚,以确保数据始终处于有效状态。

而在 Abp 的内部则是结合 Castle 的 Dynamic Proxy 拦截 UnitOfwork Attribute 来进行动态代理注入,实现了当执行标注了 [UnitOfwork] 方法时能够通过 UnitOfworkManager 来进行事务控制。

其大概流程如下:

1 启动流程

首先我们来看一下 Abp 内部是什么时候注入 UOW 相关的代码的,翻阅源码,在 AbpBootstrapper 内部我们就可以看到 Abp 作者为 UOW 写了一个拦截器,并且在 Abp 框架初始化的时候就通过 AddInterceptorRegistrars() 方法来监听 IocManager 的组件注册事件,当触发事件的时候就来判断是否满足条件,如果满足则将拦截器与该类型进行一个绑定。

public class AbpBootstrapper : IDisposable
{
    private AbpBootstrapper([NotNull] Type startupModule, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
    {
        // 其他代码
        if (!options.DisableAllInterceptors)
        {
            // 添加拦截器
            AddInterceptorRegistrars();
        }
    }

    private void AddInterceptorRegistrars()
    {
        ValidationInterceptorRegistrar.Initialize(IocManager);
        AuditingInterceptorRegistrar.Initialize(IocManager);
        EntityHistoryInterceptorRegistrar.Initialize(IocManager);
        UnitOfWorkRegistrar.Initialize(IocManager);
        AuthorizationInterceptorRegistrar.Initialize(IocManager);
    }
}
internal static class UnitOfWorkRegistrar
{
    public static void Initialize(IIocManager iocManager)
    {
        // 监听组件注册事件
        iocManager.IocContainer.Kernel.ComponentRegistered += (key, handler) =>
        {
            var implementationType = handler.ComponentModel.Implementation.GetTypeInfo();

            // 按 UOW 特性注册
            HandleTypesWithUnitOfWorkAttribute(implementationType, handler);
            // 按规约注册
            HandleConventionalUnitOfWorkTypes(iocManager, implementationType, handler);
        };
    }

    private static void HandleTypesWithUnitOfWorkAttribute(TypeInfo implementationType, IHandler handler)
    {
        if (IsUnitOfWorkType(implementationType) || AnyMethodHasUnitOfWork(implementationType))
        {
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
        }
    }

    private static void HandleConventionalUnitOfWorkTypes(IIocManager iocManager, TypeInfo implementationType, IHandler handler)
    {
        if (!iocManager.IsRegistered<IUnitOfWorkDefaultOptions>())
        {
            return;
        }

        var uowOptions = iocManager.Resolve<IUnitOfWorkDefaultOptions>();

        if (uowOptions.IsConventionalUowClass(implementationType.AsType()))
        {
            handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(UnitOfWorkInterceptor)));
        }
    }

    private static bool IsUnitOfWorkType(TypeInfo implementationType)
    {
        return UnitOfWorkHelper.HasUnitOfWorkAttribute(implementationType);
    }

    private static bool AnyMethodHasUnitOfWork(TypeInfo implementationType)
    {
        return implementationType
            .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
            .Any(UnitOfWorkHelper.HasUnitOfWorkAttribute);
    }
}

可以看到在这个 Registrar 里面他拥有两种注册方式,第一种很简单,就是判断注册的组件类型是否拥有 UOW 标签,第二种则是通过规约来注入拦截器。

Abp 默认针对仓储与应用服务会自动将拦截器挂载到这两个类型以及他的所有子类的。这里的 UnitOfWorkDefaultOptionsExtensions.IsConventionalUowClass() 方法就是用来判断传入的 Type 是否属于规约的 Type。

public static bool IsConventionalUowClass(this IUnitOfWorkDefaultOptions unitOfWorkDefaultOptions, Type type)
{
    return unitOfWorkDefaultOptions.ConventionalUowSelectors.Any(selector => selector(type));
}

又牵扯到了一个 IUnitOfWorkDefaultOptions ,看一下他的默认实现 UnitOfWorkDefaultOptions 就会发现这样的代码:

public UnitOfWorkDefaultOptions()
{
    _filters = new List<DataFilterConfiguration>();
    IsTransactional = true;
    Scope = TransactionScopeOption.Required;

    IsTransactionScopeAvailable = true;

    // 默认类型
    ConventionalUowSelectors = new List<Func<Type, bool>>
    {
        type => typeof(IRepository).IsAssignableFrom(type) ||
            typeof(IApplicationService).IsAssignableFrom(type)
    };
}

2. 实现原理

2.1 工作单元拦截器

在上一步我们通过两种注入方式将拦截器注入到需要应用工作单元特性的类型里面,那么我们程序在执行的时候就会使用 Dyncmic Proxy 来拦截包裹这些方法。

下面我们就来看一下刚刚注入的拦截器:

internal class UnitOfWorkInterceptor : IInterceptor
{
    // ...
    // 其他代码

    public void Intercept(IInvocation invocation)
    {
        MethodInfo method;
        try
        {
            method = invocation.MethodInvocationTarget;
        }
        catch
        {
            method = invocation.GetConcreteMethod();
        }

        // 判断当前进入的方法是否带有 UnitOfWork 特性
        var unitOfWorkAttr = _unitOfWorkOptions.GetUnitOfWorkAttributeOrNull(method);
        if (unitOfWorkAttr == null || unitOfWorkAttr.IsDisabled)
        {
            // 没有则直接执行该方法
            invocation.Proceed();
            return;
        }

        PerformUow(invocation, unitOfWorkAttr.CreateOptions());
    }

    // ...
    // 其他代码
}

拦截器内部方法很简单,如果是 UOW 方法则执行 PerformUow() 即可,在该方法内部则对方法类型进行了不同的判断,同步与异步的处理方法是不一样的。

// ...
// 其他代码

private void PerformUow(IInvocation invocation, UnitOfWorkOptions options)
{
    // 判断方法是同步还是异步方法,不同则执行不同的处理操作
    if (invocation.Method.IsAsync())
    {
        PerformAsyncUow(invocation, options);
    }
    else
    {
        PerformSyncUow(invocation, options);
    }
}

// ...
// 其他代码

那么我们就先来看一下同步方法:

// ...
// 其他代码

private void PerformSyncUow(IInvocation invocation, UnitOfWorkOptions options)
{
    using (var uow = _unitOfWorkManager.Begin(options))
    {
        // 继续执行
        invocation.Proceed();
        uow.Complete();
    }
}

// ...
// 其他代码

同步方法针对 UOW 的操作十分简单,直接使用 UnitOfWorkManager.Begin() 方法开启一个事务,然后在内部执行原有方法的代码,执行完成之后调用 Complete() 完成此次调用。

假如我拥有两个应用服务类,他们都拥有 UnitOfWork 特性,然后我再一个 A 方法调用他们两个 B 类的 Run() 方法,而 B类的内部也调用了C 的 Run() 方法,大体如下:

public class A
{
    private readonly B B;

    public A(B b)
    {
        B = b;
    }

    public void TestMethod()
    {
        B.Run();
    }
}

internal class B
{
    private readonly C C;

    public B(C c)
    {
        C = c;
    }

    [UnitOfWork]
    public void Run()
    {
        // 数据库操作
        C.Run();
        Console.WriteLine("B 的 Run 方法被调用.");
    }
}

internal class C
{
    [UnitOfWork]
    public void Run()
    {
        Console.WriteLine("C 的 Run 方法被调用.");
    }
}

然后在拦截器内部的执行过程就类似于下面这种:

internal class UnitOfWorkInterceptor
{
    public void TestMethod()
    {
        using (var uow = _unitOfWorkManager.Begin(options))
        {
            using(var uow2 = _unitOfWorkManager.Begin(options))
            {
                // C 方法的代码
                Console.WriteLine("C 的 Run 方法被调用.");
                uow2.Complete();
            }

            // B 方法的代码
            Console.WriteLine("B 的 Run 方法被调用.");
            uow.Complete();
        }
    }
}

两个工作单元之间的调用会被嵌套在一个 using 语句块之中,一旦任何代码抛出了异常,都会导致最外层的 uow.Complete() 不会被执行,而 Complete() 方法没有执行,则会导致 uow 对象被释放的时候,uow.Dispose() 内部检测到 Complete() 没有被调用,Abp 框架也会自己抛出异常。

所以 Abp 巧妙结合 Castle Dynamic 实现了 UOW 模式。

下面我们继续看一下他是如何处理异步 UOW 方法的。

private void PerformAsyncUow(IInvocation invocation, UnitOfWorkOptions options)
{
    var uow = _unitOfWorkManager.Begin(options);

    try
    {
        invocation.Proceed();
    }
    catch
    {
        uow.Dispose();
        throw;
    }

    // 如果是无返回值的异步方法
    if (invocation.Method.ReturnType == typeof(Task))
    {
        invocation.ReturnValue = InternalAsyncHelper.AwaitTaskWithPostActionAndFinally(
            (Task) invocation.ReturnValue,
            async () => await uow.CompleteAsync(),
            exception => uow.Dispose()
        );
    }
    // 有返回值的异步方法处理
    else
    {
        invocation.ReturnValue = InternalAsyncHelper.CallAwaitTaskWithPostActionAndFinallyAndGetResult(
            invocation.Method.ReturnType.GenericTypeArguments[0],
            invocation.ReturnValue,
            async () => await uow.CompleteAsync(),
            exception => uow.Dispose()
        );
    }
}

相比而言,针对拦截到的异步方法处理起来更加复杂一点,但是总体思路仍然是一样的,将这些工作单元的方法一层层地嵌套起来,依次执行就是核心。而在上面代码里面,一样的首先使用 UnitOfManager.Begin() 获得了一个新的工作单元之后,继续执行原有的操作,下面则主要是通过内部的 InternalAsyncHelper 封装的两个辅助方法来确保等待原有任务执行完成之后,再执行 CompleteAsync() 方法。

我们可以来看一下这个内部类的实现:

// 异步无返回值处理
public static async Task AwaitTaskWithPostActionAndFinally(Task actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
{
    Exception exception = null;

    try
    {
        // 等待原有任务执行完成
        await actualReturnValue;
        // 执行 CompleteAsync() 表示本工作单元已经顺利执行
        await postAction();
    }
    // 捕获异常
    catch (Exception ex)
    {
        exception = ex;
        throw;
    }
    finally
    {
        // 不论是否抛出异常,都调用之前传入的 uow.Dispose() 方法
        finalAction(exception);
    }
}

// 原理基本同上,只是多了一个返回值
public static async Task<T> AwaitTaskWithPostActionAndFinallyAndGetResult<T>(Task<T> actualReturnValue, Func<Task> postAction, Action<Exception> finalAction)
{
    Exception exception = null;

    try
    {
        var result = await actualReturnValue;
        await postAction();
        return result;
    }
    catch (Exception ex)
    {
        exception = ex;
        throw;
    }
    finally
    {
        finalAction(exception);
    }
}

// 异步有返回值处理
public static object CallAwaitTaskWithPostActionAndFinallyAndGetResult(Type taskReturnType, object actualReturnValue, Func<Task> action, Action<Exception> finalAction)
{
    // 这里通过反射获取到 AwaitTaskWithPostActionAndFinallyAndGetResult 方法,并调用。
    return typeof (InternalAsyncHelper)
        .GetMethod("AwaitTaskWithPostActionAndFinallyAndGetResult", BindingFlags.Public | BindingFlags.Static)
        .MakeGenericMethod(taskReturnType)
        .Invoke(null, new object[] { actualReturnValue, action, finalAction });
}

并不复杂,以上即是拦截器所做的操作。

2.2 工作单元管理器

通过上文我们可以看到一个工作单元是通过 IUnitOfWorkManager.Begin() 拿到的,那 IUnitOfWorkManager 又是个什么东西呢?

根据字面意思我们大概知道应该类似于管理 UOW 的东西,它其实只有两个作用。第一,获取当前处于激活状态的工作单元,什么叫激活状态我们后面再讲。第二个作用就是我们之前看到的,可以通过 Begin() 方法来创建一个新的工作单元。

IUnitOfWorkManager 在 Abp 框架初始化的时候就被注入了,其默认实现为 UnitOfWorkManager ,其核心方法就是 Begin() 方法。

public IUnitOfWorkCompleteHandle Begin(UnitOfWorkOptions options)
{
    // 如果没有传入 UOW 参数,则填充一个默认的参数
    options.FillDefaultsForNonProvidedOptions(_defaultOptions);

    // 获取当前的外部工作单元
    var outerUow = _currentUnitOfWorkProvider.Current;

    // 如果已经存在有外部工作单元,则直接构建一个内部工作单元
    if (options.Scope == TransactionScopeOption.Required && outerUow != null)
    {
        return new InnerUnitOfWorkCompleteHandle();
    }

    // 不存在外部工作单元,则从 IOC 容器当中获取一个新的出来
    var uow = _iocResolver.Resolve<IUnitOfWork>();

    // 绑定外部工作单元的事件
    uow.Completed += (sender, args) =>
    {
        _currentUnitOfWorkProvider.Current = null;
    };

    uow.Failed += (sender, args) =>
    {
        _currentUnitOfWorkProvider.Current = null;
    };

    uow.Disposed += (sender, args) =>
    {
        _iocResolver.Release(uow);
    };

    // 设置过滤器
    if (outerUow != null)
    {
        options.FillOuterUowFiltersForNonProvidedOptions(outerUow.Filters.ToList());
    }

    uow.Begin(options);

    // 绑定租户 ID
    if (outerUow != null)
    {
        uow.SetTenantId(outerUow.GetTenantId(), false);
    }

    // 设置当前的外部工作单元为刚刚初始化的工作单元
    _currentUnitOfWorkProvider.Current = uow;

    return uow;
}

可以看到 Begin() 方法返回的是一个类型为 IUnitOfWorkCompleteHandle 的东西,转到其定义:

/// <summary>
/// Used to complete a unit of work.
/// This interface can not be injected or directly used.
/// Use <see cref="IUnitOfWorkManager"/> instead.
/// </summary>
public interface IUnitOfWorkCompleteHandle : IDisposable
{
    /// <summary>
    /// Completes this unit of work.
    /// It saves all changes and commit transaction if exists.
    /// </summary>
    void Complete();

    /// <summary>
    /// Completes this unit of work.
    /// It saves all changes and commit transaction if exists.
    /// </summary>
    Task CompleteAsync();
}

他只有两个方法,都是标识 UOW 处于已经完成的状态。

在方法上面右键查看其实现可以看到有这样一种依赖关系:

可以看到 IUnitOfWorkCompleteHandle 有两个实现,一个是 InnerUnitOfWorkCompleteHandle 还有一个则是 IUnitOfWork 接口。

首先看一下 InnerUnitOfWorkCompleteHandle

internal class InnerUnitOfWorkCompleteHandle : IUnitOfWorkCompleteHandle
{
    public const string DidNotCallCompleteMethodExceptionMessage = "Did not call Complete method of a unit of work.";

    private volatile bool _isCompleteCalled;
    private volatile bool _isDisposed;

    public void Complete()
    {
        _isCompleteCalled = true;
    }

    public Task CompleteAsync()
    {
        _isCompleteCalled = true;
        return Task.FromResult(0);
    }

    public void Dispose()
    {
        if (_isDisposed)
        {
            return;
        }

        _isDisposed = true;

        if (!_isCompleteCalled)
        {
            if (HasException())
            {
                return;
            }

            throw new AbpException(DidNotCallCompleteMethodExceptionMessage);
        }
    }

    private static bool HasException()
    {
        try
        {
            return Marshal.GetExceptionCode() != 0;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

代码很简单,调用 Complete()/CompleteAsync() 会将 _isCompleteCalled 置为 true,然后在 Dispose() 方法内会进行检测,为 faslse 的话直接抛出异常。可以看到在 InnerUnitOfWorkCompleteHandle 内部并不会真正地调用 DbContext.SaveChanges() 进行数据保存。

那么谁才是真正进行数据库操作的工作单元呢?

答案就是之前在 IUnitOfWorkManager.Begin() 里面,可以看到在创建 UOW 对象的时候,他在内部进行了一个判断,如果不存在外部工作单元的情况下才会创建 InnerUnitOfWorkCompleteHandle 对象,否则是解析的一个 IUnitOfWork 对象。

也就是说你可以想象有以下代码:

public void TestUowMethod()
{
    using(var outerUOW = Manager.Begin())  // 这里返回的是 IOC 解析出的 IUnitOfWork
    {
        OperationOuter();
        using(var innerUOW1 = Manager.Begin())  // 内部 UOW
        {
            Operation1();
            using(var innerUOW2 = Manager.Begin())  // 内部 UOW
            {
                Operation2();
                Complete();
            }
            Complete();
        }
        Complete();
    }
}

当代码执行的时候,如同俄罗斯套娃,从内部依次到外部执行,内部工作单元仅会在调用 Complete 方法的时候将 completed 标记为 true,但一旦操作抛出异常,Complete() 无法得到执行,则会直接抛出异常,中断外层代码执行。

在 ABP 内部针对 EF Core 框架实现了一套 UOW,其继承自 UnitOfWorkBase,而在 UnitOfWorkBase 内部有部分针对接口 IActiveUnitOfWork 的实现,同时由于 IUnifOfWork 也实现了 IUnitOfWorkCompleteHandle 接口,所以在 Begin() 方法处能够向上转型。

2.3 抽象工作单元

根据上图可以知道 Abp 默认实现了一个 UnitOfWorkBase 作为工作单元的抽象基类,他主要的属性就是 Id 与 Outer 属性。

public abstract class UnitOfWorkBase : IUnitOfWork
{
    public string Id { get; }

    [DoNotWire]
    public IUnitOfWork Outer { get; set; }
}

这里的 Id 是使用的 Guid 生成的,用于标识每个工作单元。

而 Outer 则是当前 UOW 对象的引用对象。

这里重点说一下 Outer 是哪儿来的,Outer 他的值是在之前的 UnitOfWorkManager.Begin() 里面的 _currentUnitOfWorkProvider.Current = uow; 进行设置的,_currentUnitOfWorkProvider 的实现在 AsyncLocalCurrentUnitOfWorkProvider 内部,其作用是维护一个 UOW 链,确保当前的工作单元始终是最新的,这里的代码原本是使用 CallContext 实现的,现在已经换为 AsyncLocal<T> 了。

public class AsyncLocalCurrentUnitOfWorkProvider : ICurrentUnitOfWorkProvider, ITransientDependency
{
    /// <inheritdoc />
    [DoNotWire]
    public IUnitOfWork Current
    {
        get { return GetCurrentUow(); }
        set { SetCurrentUow(value); }
    }

    public ILogger Logger { get; set; }

    private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>();

    public AsyncLocalCurrentUnitOfWorkProvider()
    {
        Logger = NullLogger.Instance;
    }

    private static IUnitOfWork GetCurrentUow()
    {
        var uow = AsyncLocalUow.Value?.UnitOfWork;
        if (uow == null)
        {
            return null;
        }

        if (uow.IsDisposed)
        {
            AsyncLocalUow.Value = null;
            return null;
        }

        return uow;
    }

    private static void SetCurrentUow(IUnitOfWork value)
    {
        lock (AsyncLocalUow)
        {
            if (value == null)
            {
                if (AsyncLocalUow.Value == null)
                {
                    return;
                }

                if (AsyncLocalUow.Value.UnitOfWork?.Outer == null)
                {
                    AsyncLocalUow.Value.UnitOfWork = null;
                    AsyncLocalUow.Value = null;
                    return;
                }

                AsyncLocalUow.Value.UnitOfWork = AsyncLocalUow.Value.UnitOfWork.Outer;
            }
            else
            {
                if (AsyncLocalUow.Value?.UnitOfWork == null)
                {
                    if (AsyncLocalUow.Value != null)
                    {
                        AsyncLocalUow.Value.UnitOfWork = value;
                    }

                    AsyncLocalUow.Value = new LocalUowWrapper(value);
                    return;
                }

                value.Outer = AsyncLocalUow.Value.UnitOfWork;
                AsyncLocalUow.Value.UnitOfWork = value;
            }
        }
    }

    private class LocalUowWrapper
    {
        public IUnitOfWork UnitOfWork { get; set; }

        public LocalUowWrapper(IUnitOfWork unitOfWork)
        {
            UnitOfWork = unitOfWork;
        }
    }
}

继续往下看,在 UnitOfWorkBase 的里面也是有个 Complete()CompleteAsync() 方法的。

protected abstract void CompleteUow();

/// <inheritdoc/>
public void Complete()
{
    // 判断是否重复完成
    PreventMultipleComplete();
    try
    {
        CompleteUow();
        _succeed = true;
        OnCompleted();
    }
    catch (Exception ex)
    {
        _exception = ex;
        throw;
    }
}

这里的 CompleteUow() 仍然只是一个抽象方法,具体的实现在具体的访问层里面。

2.4 EF Core 实际处理

Abp 针对 EF Core 的 UOW 实现是 EfCoreUnitOfWork,代码如下:

protected override void BeginUow()
{
    if (Options.IsTransactional == true)
    {
        _transactionStrategy.InitOptions(Options);
    }
}

public override void SaveChanges()
{
    foreach (var dbContext in GetAllActiveDbContexts())
    {
        SaveChangesInDbContext(dbContext);
    }
}

public override async Task SaveChangesAsync()
{
    // 遍历所有激活的 DbContext
    foreach (var dbContext in GetAllActiveDbContexts())
    {
        await SaveChangesInDbContextAsync(dbContext);
    }
}

protected override void CompleteUow()
{
    SaveChanges();
    CommitTransaction();
}

protected override async Task CompleteUowAsync()
{
    await SaveChangesAsync();
    CommitTransaction();
}

private void CommitTransaction()
{
    if (Options.IsTransactional == true)
    {
        _transactionStrategy.Commit();
    }
}

public IReadOnlyList<DbContext> GetAllActiveDbContexts()
{
    return ActiveDbContexts.Values.ToImmutableList();
}

根本就是遍历 DbContext 调用其 SaveChanges() 来提交所有数据库更改。

余下更加详细的东西会放在 《七、仓储与 Entity Framework Core》 当中说明的。

3.常见问题

3.1 待写

原文地址:https://www.cnblogs.com/myzony/p/9285098.html

时间: 2024-10-11 10:22:45

[Abp 源码分析]六、工作单元的实现的相关文章

ABP源码分析六:依赖注入的实现

ABP的依赖注入的实现有一个本质两个途径:1.本质上是依赖于Castle这个老牌依赖注入的框架.2.一种实现途径是通过实现IConventionalDependencyRegistrar的类定义Register 规则,然后通过IocManager来读取这个规则完成依赖注入.3另一种实现途径是直接IocManager的Register方法完成注入. 第一种途径: 下面具体分析:代码在Abp项目文件的Dependency文件夹下. 先出一张相关接口和类的关系图,然后逐个解释. IDictionary

ABP源码分析四十六:ABP ZERO中的Ldap模块

通过AD作为用户认证的数据源.整个管理用户认证逻辑就在LdapAuthenticationSource类中实现. LdapSettingProvider:定义LDAP的setting和提供DefautValue.主要提供配置访问AD数据库的账号信息. LdapSettings/ILdapSettings:通过settingManager获取LDAP settings AbpZeroLdapModuleConfig/IAbpZeroLdapModuleConfig: 提供激活Ldap认证的配置.

ABP源码分析三十六:ABP.Web.Api

这里的内容和ABP 动态webapi没有关系.除了动态webapi,ABP必然是支持使用传统的webApi.ABP.Web.Api模块中实现了一些同意的基础功能,以方便我们创建和使用asp.net webApi. AbpApiController:这是一个抽象基类,继承自ApiController,是AB WebApi系统中所有controller的基类.如下图中,其封装了ABP核心模块中提供的大多数的功能对象.同时实现了一些公共的方法.它有四个派生类:DynamicApiController<

ABP源码分析二十六:核心框架中的一些其他功能

本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSession使之与CLR的Session关联 IAbpSession:定义如下图中的四个属性. NullAbpSession:IAbpSession的一个缺省实现,给每个属性都给予null值,无实际作用 ClaimsAbpSession:实现了从ClaimsPrincipal/ClaimsIdentity

[Abp 源码分析]十二、多租户体系与权限验证

0.简介 承接上篇文章我们会在这篇文章详细解说一下 Abp 是如何结合 IPermissionChecker 与 IFeatureChecker 来实现一个完整的多租户系统的权限校验的. 1.多租户的概念 多租户系统又被称之为 Saas ,比如阿里云就是一个典型的多租户系统,用户本身就是一个租户,可以在上面购买自己的 ECS 实例,并且自己的数据与其他使用者(租户)所隔绝,两者的数据都是不可见的. 那么 Abp 是如何实现数据隔离的呢? 1.1 单部署-单数据库 如果你的软件系统仅部署一个实例,

ABP源码分析三十三:ABP.Web

ABP.Web模块并不复杂,主要完成ABP系统的初始化和一些基础功能的实现. AbpWebApplication : 继承自ASP.Net的HttpApplication类,主要完成下面三件事一,在Application_Start完成AbpBootstrapper的初始化.整个ABP系统的初始化就是通过AbpBootstrapper完成初始化的.二,在Application_BeginRequest设置根据request或cookie中的Culture信息,完成当前工作线程的CurrentCu

ABP源码分析三十二:ABP.SignalR

Realtime Realtime是ABP底层模块提供的功能,用于管理在线用户.它是使用SignalR实现给在线用户发送通知的功能的前提 IOnlineClient/OnlineClient: 封装在线用户的信息 OnlineClientManager/IOnlineClientManager: 用于提供基本维护在线用户的方法.其内部维护了一个字典来保存在线的客户信息. SingalR SignalRRealTimeNotifier: 实现了给在线用户发送通知的功能.其从IOnlineClien

ABP源码分析三十五:ABP中动态WebAPI原理解析

动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能,这应该算是对DRY的最佳诠释了. 如下图所示,一行代码就为所有实现了IApplicationService的类型,自动创建对应的动态WebAPI. 这么Magic的功能是如何实现的呢? 本文为你揭开其Magic的外表.你会发现,实现如此Magic的功能,最关键的代码只有四行. 先思考一个问题:如果不

ABP源码分析三十:ABP.RedisCache

ABP 通过StackExchange.Redis类库来操作Redis数据库. AbpRedisCacheModule:完成ABP.RedisCache模块的初始化(完成常规的依赖注入) AbpRedisCacheConfig:定义了connectionStringKey和databaseIdAppSetting的值.这两个值对象redis 在web.config中的key值. ABP.RedisCache模块通过读取web.config来获取redis的配置. IAbpRedisConnect