在前面的章节中,我们已经设计了一个简单的领域模型,接下来我们希望能够实现领域模型的持久化及查询。在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本身支持以下几种数据库初始化策略:

  1. MigrateDatabaseToLatestVersion:使用Code First数据库迁移策略,将数据库更新到最新版本
  2. NullDatabaseInitializer:一个什么都不干的数据库初始化器
  3. CreateDatabaseIfNotExists:顾名思义,如果数据库不存在则新建数据库
  4. DropCreateDatabaseAlways:无论数据库是否存在,始终重建数据库
  5. 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】项目中添加以下程序包引用:

  1. Apworks.ObjectContainers.Unity:Apworks对Unity的支持库
  2. 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的仓储基础结构的更多相关文章

  1. 在Apworks数据服务中使用基于Entity Framework Core的仓储(Repository)实现

    <在ASP.NET Core中使用Apworks快速开发数据服务>一文中,我介绍了如何使用Apworks框架的数据服务来快速构建用于查询和管理数据模型的RESTful API,通过该文的介 ...

  2. Apworks框架实战

    Apworks框架实战(一):Apworks到底是什么? Apworks框架实战(二):开始使用 Apworks框架实战(三):单元测试与持续集成 Apworks框架实战(四):使用Visual St ...

  3. Apworks框架实战(五):EasyMemo的领域模型设计

    在上一讲中,我们已经新建了一个聚合根对象Account,并已经可以开始设计领域模型了.在这一讲中,我们会着重介绍EasyMemo领域模型的分析和设计,并引入Visual Studio Ultimate ...

  4. Apworks框架实战(一):Apworks到底是什么?

    简介 Apworks是一款基于Microsoft .NET的面向领域驱动的企业级应用程序开发框架,它适用于以领域模型为核心的企业级系统的开发和集成.Apworks不仅能够很好地支持经典的分层架构,而且 ...

  5. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

    创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...

  6. 基于Entity Framework的自定义分页,增删改的通用实现

    简介 之前写个一个基于Dapper的分页实现,现在再来写一个基于Entity Framework的分页实现,以及增删改的通用实现. 代码 还是先上代码:https://github.com/jinwe ...

  7. Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF

    Entity FrameWork(实体框架)是以ADO.NET Entity FrameWork ,简称为EF Entity FrameWork的特点 1.支持多种数据库(MSSQL.Oracle.M ...

  8. 基于Entity Framework 6的框架Nido Framework

    随着 Entity Framework 最新主版本 EF6 的推出,Microsoft 对象关系映射 (ORM) 工具达到了新的专业高度,与久负盛名的 .NET ORM 工具相比已不再是门外汉. EF ...

  9. Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始

    时隔一年,继续我们的Apworks框架之旅.在接下来的文章中,我将逐渐向大家介绍如何在Visual Studio中结合Apworks框架,使用ASP.NET Web API和MVC来开发面向经典分层架 ...

随机推荐

  1. HTML DOM 对象

    本篇主要介绍HTML DOM 对象:Document.Element.Attr.Event等4个对象. 目录 1. Document 对象:表示文档树的根节点,大部分属性和方法都是对元素进行操作. 2 ...

  2. nginx的使用

    1.nginx的下载 解压后文件目录: 2.nginx的常用命令 nginx -s stop 强制关闭  nginx -s quit 安全关闭  nginx -s reload 改变配置文件的时候,重 ...

  3. Android消息传递之基于RxJava实现一个EventBus - RxBus

    前言: 上篇文章学习了Android事件总线管理开源框架EventBus,EventBus的出现大大降低了开发成本以及开发难度,今天我们就利用目前大红大紫的RxJava来实现一下类似EventBus事 ...

  4. JavaScript常见的五种数组去重的方式

    ▓▓▓▓▓▓ 大致介绍 JavaScript的数组去重问题在许多面试中都会遇到,现在做个总结 先来建立一个数组 var arr = [1,2,3,3,2,'我','我',34,'我的',NaN,NaN ...

  5. 就这么漂来漂去---一个毕业三个月的java程序员的裸辞风波

    注:这并不是一篇技术文章,而是记录了我这几个月经历的入职,裸辞,找工作的心路历程,简单介绍一个博主的情况,我是16年毕业生,校招进了一家北京的公司,java开发,和很多年轻人一样,干了一段时间,我发现 ...

  6. 在centos7上安装ClamAV杀毒,并杀毒(centos随机英文10字母)成功

    前言 上传文件的时候发现总是失败,查看top发现有个进程一直cpu占用80%以上,而且名称还是随机数.kill之后,一会儿又重新生成了.突然发现居然没有在服务端杀毒的经历.在此处补齐. 安装clama ...

  7. Web安全开发之验证码设计不当引发的撞库问题

    感谢某电商平台安全工程师feiyu跟我一起讨论这个漏洞的修复.以往在安全测试的过程中后台经常存在验证码不失效果造成的撞库问题,甚至在一些银行或者电商的登录与查存页面同样存在这个问题,一旦造成撞库无论对 ...

  8. git

    CMD命令:git initgit add . [添加文件至暂存区]git commit -m '描述性语句 随意写即可'git branch gh-pages [创建仓库分支]git checkou ...

  9. Linux 中的数值计算和符号计算

    不知道经常需要做科学计算的朋友们有没有这样的好奇:在 Linux 系统下使用什么工具呢?说到科学计算,首先想到的肯定是 Matlab,如果再说到符号计算,那就非 Mathematica 不可了.可惜, ...

  10. Glide源码导读

    最近比较无聊,为了找点事干,就花了两天时间把Glide的源码大概看了一下.刚开始看Glide的源码头脑还是比较乱的,因为作者引入了几个概念,又大量用了泛型,如果不了解这些概念读起代码来就比较痛苦,我也 ...