本文翻譯自ABP的官方教程《EntityFramework Integration》,地址為:http://aspnetboilerplate.com/Pages/Documents/EntityFramework-Integration
ABP 可以與任何 ORM 框架協同工作,它內置了對 EntityFramework 的集成支持。本文將介紹如何在 ABP 中使用 EntityFramework。本文假定你已經初步掌握了EntityFramework。
譯者注:怎么才算初步掌握了 EntityFramework 呢?譯者認為應當懂得使用 Code First 模式進行CRUD。
1 Nuget 包
要在 ABP 中使用 EntityFramework 作為 ORM 框架的話,需要到 Nuget 上下載一個名為 Abp.EntityFramework 的包。比較好的做法是:新建一個獨立的程序集 (dll),然后在這個程序集中調用這個包和 EntityFramework。
2 創建 DbContext(Creating DbContext)
要使用 EntityFramework,首先需要定義一個 DbContext 類。下面是一個DbContex 類的示例:
public class SimpleTaskSystemDbContext : AbpDbContext {
public virtual IDbSet<Person> People { get; set; }
public virtual IDbSet<Task> Tasks { get; set; }
public SimpleTaskSystemDbContext() : base("MyConnectionStringName") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder); modelBuilder.Entity<Person>().ToTable("StsPeople"); modelBuilder.Entity<Task>().ToTable("StsTasks").HasOptional(t=> t.AssignedPerson); } }
上面的 SimpleTaskSystemDbContext 本質上是一個 DbContext 類,它派生自 AbpDbContext,而不是 DbContext。AbpDbContext 提供了很多重載的構造函數,如果需要的話,我們可以使用它。EntityFramework 可以使用約定的方式來映射實體和數據表。除非你想進行自定義映射,否則你甚至不需要做任何配置。在上例中,我們將實體映射到了不同的表中。默認情況下(按照約定優先於配置的原則,會默認采用約定的方式),Task 實體會映射到 Tasks 表,但在這里我們將它映射到了 StsTasks 表。相比起使用 Data Annotation 模式來進行自定義映射,我更喜歡使用 Fluent API 模式。當然,你可以選擇你所喜歡的模式,這里沒有特別的限制。
3 倉儲(Repositories)
ABP 提供了一個名為 EfRepositoryBase 的基類,這使得實現倉儲變得簡單快捷。要實現 IRepository 接口,你只需要從這個基類進行派生即可。但是更好的做法是,自定義一個派生自 EfRepositoryBase 的基類,然后在這個基類中添加一些通用的方法。這樣做的好處是,所有派生自這個基類的倉儲都繼承了這些通用方法。
(1)應用程序專用的倉儲基類(Application specific base repository class)
在下面的例子中,我們定義了一個名為 SimpleTaskSystem 倉儲基類,這個類是此應用程序所專用的。
// 應用程序中的所有倉儲的基類 public class SimpleTaskSystemRepositoryBase<TEntity, TPrimaryKey> : EfRepositoryBase<SimpleTaskSystemDbContext, TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey> { public SimpleTaskSystemRepositoryBase(IDbContextProvider<SimpleTaskSyst emDbContext> dbContextProvider) : base(dbContextProvider) { } //添加倉儲基類的通用方法 } public class SimpleTaskSystemRepositoryBase<TEntity> : SimpleTaskSystemRepositoryBase<TEntity, int>
where TEntity : class, IEntity<int> { public SimpleTaskSystemRepositoryBase(IDbContextProvider<SimpleTaskSyst emDbContext> dbContextProvider) : base(dbContextProvider) { } // 不 要 在 這 里 添 加 任 何 通 用 方 法 ,通 用 方 法 應 當 被添加到 上 面 的 基 類 中(MyRepositoryBase<TEntity, TPrimaryKey>) }
需 要 注 意 的 是 , 我 們 繼 承 了 基 類EfRepositoryBase<SimpleTaskSystemDbContext, TEntity, TPrimaryKey>。這相當於做了一個聲明,它會讓 ABP 在倉儲中調用 SimpleTaskSystemDbContext。
(2)倉儲實現(Implementing a repository)
如果你只想使用預定義的倉儲方法的話,你甚至不需要為實體創建倉儲類。如下所示:
public class PersonAppService : IPersonAppService { private readonly IRepository<Person> _personRepository; public PersonAppService(IRepository<Person> personRepository) { _personRepository = personRepository; } public void CreatePerson(CreatePersonInput input) { person = new Person { Name = input.Name, EmailAddress = input.EmailAddress }; _personRepository.Insert(person); } }
PersonAppService類采用構造函數注入的方式注入了一個IRepository<Person>對象,然后調用了預定義的 Insert 方法。采用這種方式,你可以方便地注入IRepository<TEntity> (或者 IRepository<TEntity, TPrimaryKey>)對象,然后使用預定義的方法。查看倉儲文檔以獲得預定義方法的列表。
(3)自定義倉儲(Custom repositories)
要實現自定義倉儲,只要從上面所創建的應用程序專用的倉儲基類(SimpleTaskSystemRepositoryBase)派生即可。
假定我們有一個名為 Task 的實體,它可以被賦予 Person 實體。Task 有一個狀態屬性(值為新建、已分配、已完成等)。我們需要編寫一個自定義方法,這個方法能根據某些條件獲取 Task 列表,並且使得 Task 的 AssisgnedPerson 屬性可以在一次數據庫查詢中被加載(使用 EntityFramework 的貪婪加載方法 Include)。如下所示:
public interface ITaskRepository : IRepository<Task, long>
{ List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state); } public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository { public TaskRepository(IDbContextProvider<SimpleTaskSystemDbContext> dbContextProvider) : base(dbContextProvider) {
} public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state) { var query = GetAll(); if (assignedPersonId.HasValue) { query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value); }
if (state.HasValue) { query = query.Where(task => task.State == state); } return query .OrderByDescending(task => task.CreationTime) .Include(task => task.AssignedPerson) .ToList(); } }
我們首先定義了一個名為 ITaskRepository 的接口,緊接着定義了一個名為 TaskRepository 的類來實現它。預定義方法 GetAll()返回了一個 IQueryable<Task> 對象,接着我們將參數放入到 Where 篩選器中,最后調用 ToList()來獲得一個 Task 列表。此外,我們可以通過調用 base.Context 屬性來獲得一個 DbContext 對象,這樣一來,你就可以直接使用 Entity Framework 的所有功能。
倉儲類應當在構造函數中獲取IDbContextProvider對象。通過這種方式,在單元測試的時候,我們可以很容易地注入一個虛擬的 DbContext Provider 對象;而在運行的時候,ABP 會根據配置注入相應的 DbContext Provider 對象。