在前面的章節中,我們已經設計了一個簡單的領域模型,接下來我們希望能夠實現領域模型的持久化及查詢。在Apworks中,實現了面向Entity Framework、NHibernate以及MongoDB的倉儲基礎結構。在本章節中,我將向大家介紹如何在Apworks中使用基於Entity Framework的倉儲機制。
搭建基於Entity Framework的基礎結構
在使用Apworks提供的倉儲服務之前,我們首先需要搭建好基於Entity Framework的基礎結構,以便接下來的Apworks能夠使用這些基礎結構功能,並利用Entity Framework實現領域模型對象生命周期的管理。
從DbContext開始
我們采用Entity Framework Code First的編程模型,因此,我們將從DbContext開始入手,為Entity Framework倉儲機制的使用做好准備工作。
首先,在【EasyMemo.Repositories】項目上單擊鼠標右鍵,選擇【管理NuGet程序包】選項。在彈出的【管理NuGet程序包】的【搜索聯機】文本框中,輸入關鍵字【apworks】。在過濾的列表中,找到【Apworks.Repositories.EntityFramework】,然后單擊【安裝】按鈕。
說明:安裝該程序包也會順帶將其所依賴的程序包一並安裝到【EasyMemo.Repositories】項目中,這些程序包包括:
- Apworks 2.5.5662.37915
- Castle.Core 3.3.1
- EntityFramework 6.1.1
接下來,在【EasyMemo.Repositories】項目中,新建一個名為EasyMemoContext的類,該類從System.Data.Entity.DbContext類繼承,代碼如下:
public class EasyMemoContext : DbContext
{
public EasyMemoContext()
: base("EasyMemoDB")
{
}
public DbSet<Account> Accounts { get; set; }
public DbSet<Role> Roles { get; set; }
public DbSet<Memo> Memos { get; set; }
}
這就是標准的Entity Framework Code First的用法,不過,Apworks的最佳實踐中建議,此處僅對聚合根定義DbSet屬性,這樣能使DbContext的定義變得非常簡潔直觀。
下一步就是針對領域模型中的實體定義一些類型/數據庫映射。根據標准的Entity Framework使用方法,我們可以定義一系列繼承於EntityTypeConfiguration泛型類的子類,在這些子類中定義映射規則,並在EasyMemoContext的OnModelCreating重載方法中將這些子類的實例添加到Configurations集合里;或者也可以直接在OnModelCreating方法中定義映射規則。我還是比較偏向於前面這種方式,即針對每個需要配置映射的實體,都創建一個繼承於EntityTypeConfiguration的子類,雖然看起來會有很多額外的類定義,但這樣做會使得代碼結構有着更好的可讀性。例如,針對Account對象,我們可以定義映射配置類型如下:
public class AccountEntityConfiguration : EntityTypeConfiguration<Account>
{
public AccountEntityConfiguration()
{
ToTable("Accounts");
HasKey(x => x.ID);
Property(x => x.ID)
.IsRequired()
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.DateCreated).IsRequired();
Property(x => x.DateLastLogon).IsOptional();
Property(x => x.DisplayName)
.IsRequired()
.IsUnicode()
.HasMaxLength(32);
Property(x => x.Email)
.IsRequired()
.IsUnicode()
.HasMaxLength(64);
Property(x => x.IsDeleted).IsOptional();
Property(x => x.Name).IsRequired()
.IsUnicode()
.HasMaxLength(16);
Property(x => x.Password).IsRequired()
.IsUnicode()
.HasMaxLength(4096);
}
}
然后將該類的實例添加到OnModelCreating重載方法中:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AccountEntityConfiguration());
}
OK,接下來使用類似的方法針對領域模型中必要的實體類型定義映射配置類,並依次將這些類的實例添加到OnModelCreating重載方法中。限於篇幅,在此就不一一列出代碼了,您可以在本章節結尾部分點擊下載代碼的鏈接,把源代碼下載到本地作參考。
設置數據庫初始化策略
Entity Framework本身支持以下幾種數據庫初始化策略:
- MigrateDatabaseToLatestVersion:使用Code First數據庫遷移策略,將數據庫更新到最新版本
- NullDatabaseInitializer:一個什么都不干的數據庫初始化器
- CreateDatabaseIfNotExists:顧名思義,如果數據庫不存在則新建數據庫
- DropCreateDatabaseAlways:無論數據庫是否存在,始終重建數據庫
- DropCreateDatabaseIfModelChanges:僅當領域模型發生變化時才重建數據庫
在實際應用當中,我們可以直接使用以上數據庫初始化策略,在調用Database對象的Initialize方法時,Entity Framework就會根據所選擇的初始化策略以及上面的映射配置信息來初始化數據庫。為了演示目的,我們希望能夠在數據庫初始化的同時,為我們准備一些數據,以便對今后的內容進行介紹,因此,我們可以自定義一套數據庫初始化策略,並在其中將所需的數據寫入數據庫。
首先,在【EasyMemo.Repositories】項目中,新建一個名為DatabaseInitializeStrategy的類,並使其繼承DropCreateDatabaseIfModelChanges類型:
public class DatabaseInitializeStrategy
: DropCreateDatabaseIfModelChanges<EasyMemoContext>
{
}
然后,在該類型中重載Seed方法,添加如下代碼:
public class DatabaseInitializeStrategy
: DropCreateDatabaseIfModelChanges<EasyMemoContext>
{
protected override void Seed(EasyMemoContext context)
{
var adminPermission = new Permission
{
Privilege = Privilege.SystemAdministration,
Value = PermissionValue.Allow
};
var administrators = new Role
{
Name = "系統管理員",
Description = "執行系統管理任務的一組賬戶",
Permissions = new List<Permission> {adminPermission}
};
var administrator = new Account
{
DateCreated = DateTime.UtcNow,
DisplayName = "管理員",
Email = "admin@easymemo.com",
Name = "admin",
Password = "admin",
Roles = new List<Role> {administrators}
};
context.Accounts.Add(administrator);
base.Seed(context);
}
}
於是,我們就有了自己的數據庫初始化策略,下一步就是在EasyMemo的系統中使用這個策略。
運行我們的代碼
打開【EasyMemo.Services】項目,以上述相同的方法,通過【管理NuGet程序包】功能,添加對【Apworks.Repositories.EntityFramework】程序包的引用。然后,在Appp_Start目錄下,新建一個名為DatabaseConfig的類:
該類的代碼如下:
public static class DatabaseConfig
{
public static void Initialize()
{
Database.SetInitializer(new DatabaseInitializeStrategy());
new EasyMemoContext().Database.Initialize(true);
}
}
接下來,打開【EasyMemo.Services】項目下的Global.asax.cs文件,向Application_Start方法添加對DatabaseConfig.Initialize的調用:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
DatabaseConfig.Initialize();
}
}
打開【EasyMemo.Services】項目的web.config文件,找到其中的entityFramework節點,對該節點進行配置,使得Entity Framework能夠使用您所指定的SQL Server數據庫:
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=localhost; Initial Catalog=EasyMemoDB; Integrated Security=True; Connect Timeout=120; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
現在,請將【EasyMemo.Services】項目設置為啟動項目,然后直接按F5,稍等片刻,當瀏覽器出現如下畫面后,我們就可以到SQL Server中找到由Entity Framework自動產生的數據庫了:
打開【Microsoft SQL Server Management Studio】,連接到所配置的數據庫實例,我們可以看到EasyMemoDB已經出現在數據庫列表中:
並且可以查詢到我們預先准備好的數據:
由Entity Framework自動產生的數據庫結構如下:
當然,目前我們無需對這個數據模型關注太多,畢竟我們不打算面向數據庫編程。
開始使用基於Entity Framework的Apworks倉儲服務
在使用Apworks倉儲服務之前,我們首先需要對Apworks的整個運行環境進行配置。基於之前的分層結構的討論,EasyMemo.Services項目是一個位於服務端的RESTful API項目,它由ASP.NET Web API 2.0實現。因此,針對Apworks框架運行環境的配置也會在這個項目中發生。為了能夠讓Web API控制器能夠得到Apworks倉儲及其上下文的實例,我們采用了IoC技術,並選擇Microsoft Unity作為依賴注入框架,這也是Apworks框架目前支持的唯一一種依賴注入框架。當然,Apworks的依賴注入系統是可以擴展的,您可以根據自己項目需要對其進行擴展,使其能夠支持多種主流的依賴注入框架。
配置Apworks的運行環境
首先,通過【管理NuGet程序包】,向【EasyMemo.Services】項目中添加以下程序包引用:
- Apworks.ObjectContainers.Unity:Apworks對Unity的支持庫
- Unity.WebAPI:Unity對ASP.NET Web API的支持,它可以使得ASP.NET Web API能夠使用Unity作為依賴注入框架
接下來,與之前添加DatabaseConfig類一樣,在【EasyMemo.Services】項目的【App_Start】文件夾下,新建一個名為ApworksConfig的靜態類,內容如下:
using Apworks.Application;
using Apworks.Config.Fluent;
using Apworks.Repositories;
using Apworks.Repositories.EntityFramework;
using EasyMemo.Repositories;
using Microsoft.Practices.Unity;
using Unity.WebApi;
public static class ApworksConfig
{
public static void Initialize()
{
AppRuntime
.Instance
.ConfigureApworks()
.UsingUnityContainerWithDefaultSettings()
.Create((sender, e) =>
{
var unityContainer = e.ObjectContainer.GetWrappedContainer<UnityContainer>();
unityContainer.RegisterInstance(new EasyMemoContext(), new PerResolveLifetimeManager())
.RegisterType<IRepositoryContext, EntityFrameworkRepositoryContext>(
new HierarchicalLifetimeManager(),
new InjectionConstructor(new ResolvedParameter<EasyMemoContext>()))
.RegisterType(typeof (IRepository<>), typeof (EntityFrameworkRepository<>));
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(unityContainer);
})
.Start();
}
}
注意:這里采用的是Fluent Interface(流暢接口)的配置方式。Apworks同時也支持基於web.config/app.config的配置方式,今后有機會我再介紹這部分內容。
然后,同樣地,在Global.asax.cs文件的Application_Start方法中,調用上述代碼:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
DatabaseConfig.Initialize();
ApworksConfig.Initialize();
}
}
OK,Apworks的運行環境配置基本上就算完成了。接下來,讓我們新建一個簡單的RESTful API,來跑通整個流程。
開始我們的ASP.NET Web API之旅
在【EasyMemo.Services】項目中,找到【Controllers】目錄,單擊鼠標右鍵,在右鍵菜單中選擇【添加 –> 控制器】。在彈出的【添加基架】對話框中,選擇【Web API 2控制器 - 空】:
在彈出的【添加控制器】對話框的【控制器名稱】一欄,填入【AccountsController】:
在單擊【添加】按鈕后,Visual Studio會打開AccountsController的代碼編輯界面。此時,我們可以向AccountsController類添加以下代碼:
[RoutePrefix("api/accounts")]
public class AccountsController : ApiController
{
private readonly IRepository<Account> accountRepository;
private readonly IRepositoryContext unitOfWork;
public AccountsController(IRepositoryContext unitOfWork, IRepository<Account> accountRepository)
{
this.accountRepository = accountRepository;
this.unitOfWork = unitOfWork;
}
[HttpGet]
[Route("name/{name}")]
public IHttpActionResult GetByName(string name)
{
var account = this.accountRepository.Find(Specification<Account>.Eval(acct => acct.Name == name));
if (account != null)
{
return Ok(new
{
account.Name,
account.DisplayName,
account.Email,
account.DateCreated,
account.DateLastLogon
});
}
throw new Exception(string.Format("The account '{0}' does not exist.", name));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
unitOfWork.Dispose();
}
base.Dispose(disposing);
}
}
代碼還算簡潔吧?讓我們再次啟動【EasyMemo.Services】項目:將該項目設置為啟動項目,然后直接按F5,在一個與上面相同的【403.14 – Forbidden】頁面出來后,在瀏覽器中輸入:
http://localhost:30295/api/accounts/name/admin
此時,你就能看到這個RESTful API返回的結果:它返回了admin這個賬戶的詳細信息:
總結
本文詳細介紹了如何在Apworks框架中使用Entity Framework並為一個ASP.NET Web API的RESTful服務提供倉儲及其上下文的基礎結構。從下一講開始,我們會重點討論ASP.NET Web API實現的方方面面,包括異常處理、認證與授權、數據傳輸對象與視圖模型等。
源代碼下載
請【單擊此處】下載截止到本文為止的EasyMemo解決方案源代碼。









