1、实体Entites

1.1 概念

实体是DDD(领域驱动设计)的核心概念之一。

实体是具有唯一标识的ID且存储在数据库总。实体通常被映射成数据库中的一个表。

在ABP中,实体继承自Entity类。

public class Person : Entity
{
    public virtual string Name { get; set; }
}

Id是所有继承自Entity类的实体主键。

Id数据类型可以被更改,默认是int(int32)类型。

public class Person : Entity<string>
{
    public virtual string Name { get; set; }
}

1.2 接口约定

1.2.1 审计(Auditing)

IHasCreationTime具有CreationTime属性,当该实体被插入到数据库时, ABP会自动设置该属性的值为当前时间。

public interface IHasCreationTime
{
    DateTime CreationTime { get; set; }
}

ICreationAudited  扩展自IHasCreationTime  并且该接口具有属性CreatorUserId。当保存一个新的实体时,ABP会自动设置CreatorUserId 的属性值为当前用户的Id。

public interface ICreationAudited : IHasCreationTime
{
    long? CreatorUserId { get; set; }
} 

IModificationAudited具有LastModificationTime 和 LastModifierUserId,当更新一个实体时,APB会自动设置这些属性的值。

public interface IModificationAudited
{
    DateTime? LastModificationTime { get; set; }
    long? LastModifierUserId { get; set; }
} 

IAudited 实现所有的审计属性

public interface IAudited : ICreationAudited, IModificationAudited
{ 

} 

AuditedEntity类已经实现了所有审计功能,直接继承该类即可。

1.2.2软删除(Soft delete)

软删除是一个通用的模式,它标记一个实体已经被删除了,而不是实际从数据库中删除记录。

FullAuditedEntity类已经实现了软删除功能,直接继承该类即可。

1.2.3激活状态/闲置状态(Active/Passive)

有些实体需要被标记为激活状态或者闲置状态。那么你可以为实体采取active/passive 状态的方式来实现。基于这个原因而创建的实体,你可以扩展IPassivable  接口来实现该功能。该接口定义了IsActive 的属性。

如果你首次创建的实体被标记为激活状态,你可以在构造函数设置IsActive 属性值为true。

2、仓储Repositories

2.1概念

在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象。

2.2 IRepository接口

在ABP中,仓储类要实现IRepository接口。最好的方式是针对不同仓储对象定义各自不同的接口。

继承方式:(1)IRepository<TEntity>  定义默认Id类型int32的实体。(2)IRepository<TEntity, TPrimaryKey> 定义指定Id类型的实体。

2.2.1 查询(Query)

2.2.2 新增(Insert)

2.2.3 更新(Update)

2.2.4 删除(Delete)

2.2.5 其他方法(Others)

2.2.6 关于异步方法(About Async methods)

2.3 仓储的实现

ABP在设计上是采取不指定特定ORM框架或其它存取数据库技术的方式。只要实现IRepository 接口,任何框架都可以使用。

仓储要使用NHibernate或EntityFramework来实现都很简单,直接注入IRepository<TEntity>(或IRepository<TEntity, TPrimaryKey>)。

2.4 管理数据库连接

数据库连接的开启和关闭,在仓储方法中,ABP会自动化的进行连接管理。

2.5 仓储的生命周期

所有的仓储对象都是暂时性的。这就是说,它们是在有需要的时候才会被创建。ABP大量的使用依赖注入,当仓储类需要被注入的时候,新的类实体会由注入容器会自动地创建。

3 工作单元Unit of Work

3.1 通用连接和事务管理方法

连接和事务管理是使用数据库的应用程序最重要的概念之一。当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的。

在应用程序中,有两个通用的方来创建/释放一个数据库连接:

(1)在Web请求到达的时候, 创建一个连接对象。使用同一个连接对象来处理所有的数据库操作,并且在请求结束的时候关闭/释放这个连接。

(2)创建一个连接当需要的时候(只要在使用它之前)并且释放它在使用它之后。这是相当高效的,但是就得乏味而且反复的去进行(创建/释放连接)。

3.2 ABP的连接和事务管理

ABP综合上述两个连接管理的方法,并且提供一个简单而且高效的模型。

3.2.1 仓储类(Repository classes)

public class ContentRepository : NhRepositoryBase<Content>,
IContentRepository {
      public List<Content> GetActiveContents(string searchCondition) {
         var query = from content in Session.Query<Content>()
                           where content.IsActive && !content.IsDeleted
                           select content; 

         if(string.IsNullorEmpty(searchCondition)) {
            query = query.Where(content =>
content.Text.Contains(searchCodition));
         } 

         return query.ToList();
      }
   }

3.2.2 应用服务(Application service classes)

一个应用服务的方法也被考虑使用工作单元。

 public class PersonAppService : IPersonAppService
 {
     private readonly IPersonRepository _personRepository;
     private readonly IStatisticsRepository _statisticsRepository;

     public PersonAppService(IPersonRepository personRepository,IStatisticsRepository statisticsRepository)
     {
         _personRepository = personRepository;
         _statisticsRepository = statisticsRepository;
     }

     public void CreatePerson(CreatePersonInput input)
     {
         var person = new Person
         {
             Name = input.Name,
             EmailAddress = input.EmailAddress
         };
         _personRepository = personRepository;
         _statisticsRepository = statisticsRepository;
     }
 }

这是一个应用服务的方法,所以两个仓储共享同一个连接和事务。

3.2.3 工作单元(Unit of work)

工作单元在后台替仓储和应用服务的方法工作。

有两种直接使用的方式:

(1)使用UnitOfWorkAttribute的方式。

[UnitOfWork]
public void CreatePerson(CreatePersonInput input)
{
    var person = new Person
    {
        Name = input.Name,
        EmailAddress = input.EmailAddress
    };
    _personRepository.Insert(person);
    _statisticsRepository.IncrementPeopleCount();
}

(2)使用IUnitOfWorkManager.Begin(...)方法。

(代码略)

3.3 工作单元

3.3.1 禁用工作单元(Disabling unit of work)

工作单元默认是启动的,如需禁用,则需如下配置属性:[UnitOfWork(IsDisabled = true)]。(慎用)

[UnitOfWork(IsDisabled = true)]
public virtual void RemoveFriendship(RemoveFriendInput input)
{
    _friendshipRepository.Delete(input.Id);
} 

3.3.2 无事务的工作单元(Non-transactional unit of work)

工作单元默认上是具事务性的(这是它的天性)。因此,ABP 启动/提交/回滚一个显性的数据库等级的事务。

在有些特殊案例中,事务可能会导致问题,因为它可能会锁住有些数据列或是数据表于数据库中。在此这些情境下, 或许需要禁用数据库等级的事务。

[UnitOfWork(false)]
public GetTasksOutput GetTasks(GetTasksInput input)
{
    var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);
    return new GetTasksOutput
    {
        Tasks = Mapper.Map<List<TaskDto>>(tasks)
    };
} 

[UnitOfWork(false)]等价于[UnitOfWork(isTransaction:false)],后者更具可读性。

注意:这里有一个非事务性UoW的限制。如果你已经位于事务性UoW区域内,设定isTransactional 为false这个动作会被忽略。

3.3.3 工作单元调用其它工作单元(A unit of work method calls another)

当工作单元区域开始,所有的程序代码都会在同一个线程中执行并共享同一个连接事务,直到工作单元区域终止。

3.3.4 自动化的saving changes (Automatically saving changes)

[UnitOfWork]
public void UpdateName(UpdateNameInput input)
{
    var person = _personRepository.Get(input.PersonId);
    person.Name = input.NewName;
}

Name即被修改。

3.3.5 仓储接口的GetAll()方法(IRepository.GetAll() method)

当你在仓储方法外调用GetAll方法, 这必定得有一个开启状态的数据库连接,因为它返回IQueryable 类型的对象。这是需要的,因为IQueryable 延迟执行。它并不会马上执行数据库查询,直到你调用ToList()方法或在foreach 循环中使用IQueryable(或是存取被查询结果集的情况下)。因此,当你调用ToList()方法,数据库连接必需是启用状态。

[UnitOfWork]
public SearchPeopleOutput SearchPeople(SearchPeopleInput input)
{
    //取得 IQueryable<Person>
    var query = _personRepository.GetAll();

    //若有选取,则添加一些过滤条件
    if (!string.IsNullOfEmpty(input.SearchedName))
    {
        query = query.Where(person =>person.Name.StartsWith(input.SearchedName));
    }

    if (input.IsActive.HasValue)
    {
        query = query.Where(person => person.IsActive == input.IsActive.Value);
    }

    //取得分页结果集
    var people = query.Skip(input.SkipCount).Take(input.MaxResultCount).ToList();

    return new SearchPeopleOutput
    {
        People = Mapper.Map<List<PersonDto>>(people)
    };
}

3.3.6 工作单员属性的限制(UnitOfWork attribute restrictions)

使用UnitOfWork属性标签的情况:

(1)类所有public或public  virtual这些基于界面的方法(像是应用服务是基于服务界面)。

(2)自我注入类的public virtual方法(像是MVC Controller和Web API Controller) 。

(3)所有protected virtual方法。

3.4 选项

可以在startup configuration 中改变所有工作单元的所有默认值。

public class SimpleTaskSystemCoreModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.UnitOfWork.IsolationLevel = IsolationLevel.ReadCommitted;
        Configuration.UnitOfWork.Timeout = TimeSpan.FromMinutes();
    }
    //...其它模块方法
}

3.5 方法

工作单元系统运作是无缝且不可视的。但是,在有些特例下,你需要调用它的方法。

SaveChanges

可以注入IUnitOfWorkManager并且调用IUnitOfWorkManager.Current.SaveChanges()方法。

3.6 事件

IUnitOfWorkManager.Current 属性来取得当前已激活的工作单元并且注册它的事件。你或许会想要执行有些程序代码于当前工作单元成功地完成。示例:

public void CreateTask(CreateTaskInput input)
{
    var task = new Task { Description = input.Description };
    if (input.AssignedPersonId.HasValue)
    {
        task.AssignedPersonId = input.AssignedPersonId.Value;
        _unitOfWorkManager.Current.Completed += (sender, args) => { };
    }
    _taskRepository.Insert(task);
}

未完待续......

ABP领域层的更多相关文章

  1. ABP(现代ASP.NET样板开发框架)系列之10、ABP领域层——实体

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板 ...

  2. ABP(现代ASP.NET样板开发框架)系列之11、ABP领域层——仓储(Repositories)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是“ASP.NET Boilerplate Proj ...

  3. ABP(现代ASP.NET样板开发框架)系列之12、ABP领域层——工作单元(Unit Of work)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Pr ...

  4. ABP(现代ASP.NET样板开发框架)系列之13、ABP领域层——数据过滤器(Data filters)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之13.ABP领域层——数据过滤器(Data filters) ABP是“ASP.NET Boilerplate P ...

  5. ABP(现代ASP.NET样板开发框架)系列之14、ABP领域层——领域事件(Domain events)

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET Boilerplate P ...

  6. ABP领域层——领域事件(Domain events)

    ABP领域层——领域事件(Domain events) 基于DDD的现代ASP.NET开发框架--ABP系列之14.ABP领域层——领域事件(Domain events) ABP是“ASP.NET B ...

  7. ABP领域层——工作单元(Unit Of work)

    ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ...

  8. ABP领域层——仓储(Repositories)

    ABP领域层——仓储(Repositories) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之11.ABP领域层——仓储(Repositories) ABP是 ...

  9. ABP领域层——实体

    ABP领域层——实体 基于DDD的现代ASP.NET开发框架--ABP系列之10.ABP领域层——实体 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的 ...

  10. ABP领域层知识回顾之---工作单元

    1. 前言   在上一篇博文中(http://www.cnblogs.com/xiyin/p/6842958.html) 我们讲到了ABP领域层的仓储.这边博文我们来讲 工作单元.个人觉得比较重要.文 ...

随机推荐

  1. C# DateTime日期格式化

    在C#中DateTime是一个包含日期.时间的类型,此类型通过ToString()转换为字符串时,可根据传入给Tostring()的参数转换为多种字符串格式. 目录 1. 分类 2. 制式类型 3. ...

  2. 实现一个类 RequireJS 的模块加载器 (二)

    2017 新年好 ! 新年第一天对我来说真是悲伤 ,早上兴冲冲地爬起来背着书包跑去实验室,结果今天大家都休息 .回宿舍的时候发现书包湿了,原来盒子装的牛奶盖子松了,泼了一书包,电脑风扇口和USB口都进 ...

  3. Javacript实现字典结构

    字典是一种用[键,值]形式存储元素的数据结构.也称作映射,ECMAScript6中,原生用Map实现了字典结构. 下面代码是尝试用JS的Object对象来模拟实现一个字典结构. <script& ...

  4. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  5. Python 基础之一变量和赋值

    变量:程序在运行的时候会用到很多临时存储数据,这个时候就用到了变量,临时数据的名字. Python中变量不需要声明,直接可以使用,变量的数据类型由赋值确定. >>> name=&qu ...

  6. Android Fragment 剖析

    1.Fragment如何产生?2.什么是Fragment Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视.针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后 ...

  7. 真正的汉化-PowerDesigner 16.5 汉化

    一.背景 经常使用PowerDesigner,之前使用15版本,后来16出来后,就一直在使用16,不过一直是英文.一些同事对使用英文版总显示有些吃力. 遍寻百度.必应,都没有找到真正的针对版本16的汉 ...

  8. SQL Server事务、视图和索引

    废话不多说,直接上干货 14:13:23 事务 概括:事务是一种机制,一个操作序列,包含一组数据库操作命令,并且把所有的命令作为一个整体一起 向系统提交或撤销操作 请求. 事务的特性:   1.原子性 ...

  9. [AlwaysOn Availability Groups]健康模型 Part 1——概述

    健康模型概述 在成功部署AG之后,跟踪和维护健康状况是很重要的. 1.AG健康模型概述 AG的健康模型是基于策略管理(Policy Based Management PBM)的.如果不熟悉这个特性,可 ...

  10. centos 6 安装配置openvpn

    下载地址:http://swupdate.openvpn.org/community/releases/http://www.oberhumer.com/opensource/lzo/download ...