ABP框架详解(三)Domain

  此处的Domain主要指Abp类库根目录下Domain文件夹。顾名思义该目录下是用来存放与领域实体,领域逻辑执行,存储,领域服务相关的内容。

  1.Entities

   (1)为整个Abp框架后期开发的所有实体Model提供接口及基础实现,总的来讲为实现某种规范的接口都拥有两个,一个是主键数据类型为泛型的实现,另一个则是继承该泛型接口并为泛型提供一个默认的数据类型,例如IEntity<TPrimaryKey>的属性Id就为TPrimaryKey类型,另一个接口IEntity只是接单的继承上述接口并指定一个int型public interface IEntity : IEntity<int>,最基本的模型接口IEntity<TPrimaryKey>提供了一个实现类public abstract class Entity<TPrimaryKey>,提供了对象比较功能,bool Equals(object obj)和int GetHashCode()方法。Entities提供了一个IMayHaveTenant接口只有一个可空的TenantId属性,用于确定实体是否属于应用级别的(值为空)或者租户级别以及哪个租户,在后面的EF的DynamicFilter也会用到。同时提供了一个IMustHaveTenant接口含有一个不可空的TenantId属性,指明实体对象属于哪个租户所有,IPassivable接口包含一个IsActive的布尔类型的属性,用以指明实体对象是否激活状态。ISoftDelete是为软删除设计模式设计的布尔类型的IsDeleted属性表明实体记录是否已被删除,对于EF搜索可用对象集合十分有帮助,ISoftDelete接口拥有一个扩展方法bool IsNullOrDeleted(this ISoftDelete entity)可以方便表明对象是否属于已删除状态。

   (2)Entities为框架额外提供了一个审计模型接口包括一些基类实现,用以在实体对象中保存相关信息如:创建人,删除人,更新人,更新时间用以保存到持久化设备中。所有的设计类型都有泛型类型(主要用于指定主键类型)和默认类型(指明主键的类型)。同时还有一个两个泛型参数的版本,用以设置一个外键方便跳转到相应实体。

public abstract class CreationAuditedEntity<TPrimaryKey, TUser> : CreationAuditedEntity<TPrimaryKey>, ICreationAudited<TUser>
where TUser : IEntity<long>
{
/// <summary>
/// Reference to the creator user of this entity.
/// </summary>
[ForeignKey("CreatorUserId")]
public virtual TUser CreatorUser { get; set; }
}
  • CreationAuditedEntity<TPrimaryKey>,如果模型实例仅需记录创建者ID和创建时间,那么继承该抽象类就可以了,此时也无需再继承Entity<TPrimaryKey>抽象类就已经是一个符合领域规范的实体了。
  • AuditedEntity<TPrimaryKey>,继承于第一个泛型类,同时添加了可空的修改人Id和修改时间
  • FullAuditedEntity<TPrimaryKey>,为了方便实体记录中添加创建人,修改人,是否已处于删除状态,还有删除人,删除时间,提供了此类包含上述所有需要记录的信息。

  2.Policies

   只是提供了一个IPolicy接口,无任何实现。

  3.Repositories

   (1)IRepository空接口用以表明实例是一个Repository。

   (2)IRepository<TEntity, TPrimaryKey>包含一个真正的Repository所拥有的CRUD方法,第一个泛型表示将要操作的实体类型(必须继承自IEntity<TPrimaryKey>接口),第二个泛型表示TEntity中主键的类型。主要分为五大块,分别是1.Select/Get/Query(用以搜索返回记录)2.Insert 3.Update 4.Delete 5.Aggregate(计算所有记录数),所有的方法都有同步及异步。

   (3)AbpRepositoryBase<TEntity, TPrimaryKey>,关于上述接口,框架提供了一个默认实现,该实现是一个抽象类,定义了数个抽象方法用以在具体所使用的ORM框架来决定真正逻辑,public abstract IQueryable<TEntity> GetAll();就是这样一个需要在后面实现的抽象类,所有其他的查找方法都是依赖此方法来完成自己的逻辑的,所以如果用EF来做ORM的话只需新建一个EFRepository并实现GetAll()抽象方法就ok了,1.public abstract TEntity Update(TEntity entity);2.public abstract void Delete(TEntity entity);同样如此。

  4.Services

   该块是系统的领域驱动服务,拥有一个空的IDomainService接口,同时拥有一个实现DomainService,这是一个抽象类,并继承AbpServiceBase,在AbpServiceBase类中已经存在ISettingManager,IUnitOfWorkManager,IActiveUnitOfWork,ILocalizationManager,ILogger(默认是一个空实现但是可以通过属性注入获得有意义的Logger,如果容器中有的话),还有一个本地方的方法string L(string name, CultureInfo culture, params object[] args)。

  5.UOW

   系统中所有事务逻辑都是保存于此的(系统默认是IApplicationService和IRepository方法执行时会生成事务环境并保存在静态私有字典字段中,通过线程槽中得到GUID去唯一匹配)。另外也可以为所以将会存入依赖容器的类型的方法上应用UnitOfWorkAttribute来保证在事务中被执行,同时该特性有一个IsDisabled来关闭默认事务。

   (1)在系统的启动阶段,会注册相关的类型到依赖容器中,如果是IApplicationService和IRepository或者类型或者方法上应用了UnitOfWorkAttribute依赖容器就会为该类添加一个UnitOfWorkInterceptor,确保方法在事务环境中被执行,该拦截器的void Intercept(IInvocation invocation)方法执行时会首先通过IUnitOfWorkManager的Current属性获取保存到线程槽中的IUnitOfWork(实际上是IUnitOfWork的GUID键值),如果不为空说明其已经在一个事务上下文中了,下面直接执行真正的逻辑就行了,如果为空的话会尝试获取应用在方法上的UnitOfWorkAttribute特性,如果没有的话会new一个新的(默认单元工作类上没有),利用此特性创建UnitOfWorkOptions,利用得到的options作为参数调用IUnitOfWorkManager的Begin方法,Begin方法会返回一个完成句柄,该句柄同时也是Disposable的,如果真正的逻辑执行ok,在最后会调用完成句柄的Complete()完成保存,同时该句柄也会被Dispose掉,通过Begin方法保存到线程数据槽中的IUnitOfWork也会被释放掉。(真正的执行逻辑是可以分异步同步的,都是支持的)

   (2)UnitOfWorkBase具有一些事件像Completed,Failed,Disposed都会在事务的相应阶段被处罚,需要额外处理一些东西的话可以监控这些事件,关于动态过滤的所有参数也UnitOfWorkBase中设置添加,在UnitOfWorkBase的继承类中会使用到控制在数据库的记录集的筛选。SaveChanges方法执行真正的保存逻辑也需要根据不同的ORM而有所变化的。所以当前是抽象方法。

   (3)CallContextCurrentUnitOfWorkProvider,在每次生成新的IUnitOfWork的时候我们需要存放这些对象以方便在函数调用堆栈中的方法的获得,所以提供了这样一个类,实际上IUnitOfWorkManager的Current属性就是调用该类的同名属性来获取的,当创建新的IUnitOfWork的时候,也通过此类的Current属性来保存。当获取当前环境的IUnitOfWork的时候会先从线程数据槽中得到当前执行线程的单元工作的GUID,CallContext.LogicalGetData(ContextKey),如果不存在就返回null,如果存在就从该类的ConcurrentDictionary<string, IUnitOfWork> UnitOfWorkDictionary私有静态只读属性中获取相应的IUnitOfWork,如果不存在就返回null,同时清空数据槽中的GUID,如果存在就获取IUnitOfWork再猜判断是否已经Dispose掉了,没有被Dispose就返回该IUnitOfWork,否则清理数据返回null。而设置保存一个新建的IUnitOfWork则是跟获取的方法执行流程相反。

上一篇:Codeforces Round #341 (Div. 2) E - Wet Shark and Blocks


下一篇:linux dd命令实用详解