Apworks框架实战(六):使用基于Entity Framework的仓储基础结构
在前面的章节中,我们已经设计了一个简单的领域模型,接下来我们希望能够实现领域模型的持久化及查询。在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解决方案源代码。
Apworks框架实战(六):使用基于Entity Framework的仓储基础结构的更多相关文章
- 在Apworks数据服务中使用基于Entity Framework Core的仓储(Repository)实现
<在ASP.NET Core中使用Apworks快速开发数据服务>一文中,我介绍了如何使用Apworks框架的数据服务来快速构建用于查询和管理数据模型的RESTful API,通过该文的介 ...
- Apworks框架实战
Apworks框架实战(一):Apworks到底是什么? Apworks框架实战(二):开始使用 Apworks框架实战(三):单元测试与持续集成 Apworks框架实战(四):使用Visual St ...
- Apworks框架实战(五):EasyMemo的领域模型设计
在上一讲中,我们已经新建了一个聚合根对象Account,并已经可以开始设计领域模型了.在这一讲中,我们会着重介绍EasyMemo领域模型的分析和设计,并引入Visual Studio Ultimate ...
- Apworks框架实战(一):Apworks到底是什么?
简介 Apworks是一款基于Microsoft .NET的面向领域驱动的企业级应用程序开发框架,它适用于以领域模型为核心的企业级系统的开发和集成.Apworks不仅能够很好地支持经典的分层架构,而且 ...
- 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表
创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...
- 基于Entity Framework的自定义分页,增删改的通用实现
简介 之前写个一个基于Dapper的分页实现,现在再来写一个基于Entity Framework的分页实现,以及增删改的通用实现. 代码 还是先上代码:https://github.com/jinwe ...
- Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF
Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF Entity FrameWork的特点 1.支持多种数据库(MSSQL.Oracle.M ...
- 基于Entity Framework 6的框架Nido Framework
随着 Entity Framework 最新主版本 EF6 的推出,Microsoft 对象关系映射 (ORM) 工具达到了新的专业高度,与久负盛名的 .NET ORM 工具相比已不再是门外汉. EF ...
- Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始
时隔一年,继续我们的Apworks框架之旅.在接下来的文章中,我将逐渐向大家介绍如何在Visual Studio中结合Apworks框架,使用ASP.NET Web API和MVC来开发面向经典分层架 ...
随机推荐
- SignalR代理对象异常:Uncaught TypeError: Cannot read property 'client' of undefined 推出的结论
异常汇总:http://www.cnblogs.com/dunitian/p/4523006.html#signalR 后台创建了一个DntHub的集线器 前台在调用的时候出现了问题(经检查是代理对象 ...
- 读python源码--对象模型
学python的人都知道,python中一切皆是对象,如class生成的对象是对象,class本身也是对象,int是对象,str是对象,dict是对象....所以,我很好奇,python是怎样实现这些 ...
- 前端性能优化的另一种方式——HTTP2.0
最近在读一本书叫<web性能权威指南>谷歌公司高性能团队核心成员的权威之作. 一直听说HTTP2.0,对此也仅仅是耳闻,没有具体研读过,这次正好有两个篇章,分别讲HTTP1.1和HTTP2 ...
- JavaScript求两个数字之间所有数字的和
这是在fcc上的中级算法中的第一题,拉出来的原因并不是因为有什么好说的,而是我刚看时以为是求两个数字的和, 很显然错了.我感觉自己的文字理解能力被严重鄙视了- -.故拉出来折腾折腾. 要求: 给你一个 ...
- Javascript 严格模式详解
转自http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html 一.概述 除了正常运行模式,ECMAscript 5添加了第二 ...
- iOS - 模态Model视图跳转和Push视图跳转的混合需求实现原理
在研发中总会遇到一些莫名的需求,本着存在即合理的态度跟大家分享一下"模态Model视图跳转和Push视图跳转的需求实现",本文仅仅传授研发技术不传授产品以及UE的思想,请大家合理对 ...
- Android中的多线程断点下载
首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...
- T-SQL字符串相加之后被截断的那点事
本文出处:http://www.cnblogs.com/wy123/p/6217772.html 字符串自身相加, 虽然赋值给了varchar(max)类型的变量,在某些特殊情况下仍然会被“截断”,这 ...
- Ubuntu 16.04 安装 arm-linux-gcc 嵌入式交叉编译环境 问题汇总
闲扯: 实习了将近半年一直在做硬件以及底层的驱动,最近要找工作了发现了对linux普遍要求很高,而且工作岗位也非常多,所以最近一些时间在时不时地接触linux. 正文:(我一时兴起开始写博客,准备不充 ...
- raspberrypi(树莓派)上安装mono和jexus,运行asp.net程序
参考网址: http://www.linuxdot.net/ http://www.cnblogs.com/mayswind/p/3279380.html http://www.raspberrypi ...