1、實體Entites 1.1 概念 實體是DDD(領域驅動設計)的核心概念之一。 實體是具有唯一標識的ID且存儲在資料庫總。實體通常被映射成資料庫中的一個表。 在ABP中,實體繼承自Entity類。 Id是所有繼承自Entity類的實體主鍵。 Id數據類型可以被更改,預設是int(int32)類型 ...
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)
一個應用服務的方法也被考慮使用工作單元。
1 public class PersonAppService : IPersonAppService 2 { 3 private readonly IPersonRepository _personRepository; 4 private readonly IStatisticsRepository _statisticsRepository; 5 6 public PersonAppService(IPersonRepository personRepository,IStatisticsRepository statisticsRepository) 7 { 8 _personRepository = personRepository; 9 _statisticsRepository = statisticsRepository; 10 } 11 12 public void CreatePerson(CreatePersonInput input) 13 { 14 var person = new Person 15 { 16 Name = input.Name, 17 EmailAddress = input.EmailAddress 18 }; 19 _personRepository = personRepository; 20 _statisticsRepository = statisticsRepository; 21 } 22 }
這是一個應用服務的方法,所以兩個倉儲共用同一個連接和事務。
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(30); } //...其它模塊方法 }
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); }
未完待續......