在前面的章节中,我们已经设计了一个简单的领域模型,接下来我们希望能够实现领域模型的持久化及查询。在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. Paypal开发中遇到请求被中止: 未能创建 SSL/TLS 安全通道及解决方案

    最近在基于ASP.NET上开发了Paypal支付平台,在ASP.NET开发的过程中没有遇到这个问题,但是引用到MVC开发模式中的时候就出现了"未能创建 SSL/TLS 安全通道及解决方案&q ...

  2. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  3. SQL Server2016升级前几点自检

    SQL Server2016已经出来一段时间了,而且最新的SP1包也于2016年11月18日正式发布,各种新的特性推出让我们跃跃欲试.那么对于我们真实的业务环境,特别是生产环境要不要"跟风& ...

  4. 高效而稳定的企业级.NET Office 组件Spire(.NET组件介绍之二)

    在项目开发中,尤其是企业的业务系统中,对文档的操作是非常多的,有时几乎给人一种错觉的是”这个系统似乎就是专门操作文档的“.毕竟现在的很多办公中大都是在PC端操作文档等软件,在这些庞大而繁重的业务中,单 ...

  5. jquery.cookie的使用

    今天想到了要为自己的影像日记增加赞的功能,并且需要用到cookie. 记得原生的js操作cookie也不是很麻烦的,但似乎jquery更简单,不过相比原生js,需要额外引入2个文件,似乎又不是很好,但 ...

  6. Java定时任务的常用实现

    Java的定时任务有以下几种常用的实现方式: 1)Timer 2)ScheduledThreadPoolExecutor 3)Spring中集成Cron Quartz 接下来依次介绍这几类具体实现的方 ...

  7. JAVA 分页工具类及其使用

    Pager.java package pers.kangxu.datautils.common; import java.io.Serializable; import java.util.List; ...

  8. 文件随机读写专用类——RandomAccessFile

     RandomAccessFile类可以随机读取文件,但是在测试中并不好用;File类可以测试文件存不存在,不存在可以创建文件;FileWriter类可以对文件进行重写或者追加内容;FileReade ...

  9. zookeeper集群的搭建以及hadoop ha的相关配置

    1.环境 centos7 hadoop2.6.5 zookeeper3.4.9 jdk1.8 master作为active主机,data1作为standby备用机,三台机器均作为数据节点,yarn资源 ...

  10. Spring MVC数据校验

    在web应用程序中,为了防止客户端传来的数据引发程序异常,常常需要对 数据进行验证.输入验证分为客户端验证与服务器端验证.客户端验证主要通过JavaScript脚本进行,而服务器端验证则主要通过Jav ...