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本身支持以下几种数据库初始化策略:

  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 = "[email protected]",
            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解决方案源代码。

时间: 2024-10-25 16:51:56

Apworks框架实战(六):使用基于Entity Framework的仓储基础结构的相关文章

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

在上一讲中,我们已经新建了一个聚合根对象Account,并已经可以开始设计领域模型了.在这一讲中,我们会着重介绍EasyMemo领域模型的分析和设计,并引入Visual Studio Ultimate(旗舰版)版本的特性,介绍在Visual Studio 2013 Ultimate中如何使用体系结构建模工具进行领域模型设计,并自动化产生支持Apworks框架的代码. 界定上下文 由于EasyMemo所需实现的功能非常简单,因此,我们很容易从领域概念中剥离出两个界定上下文:用户账户上下文和用户便签

Apworks框架实战

Apworks框架实战(一):Apworks到底是什么? Apworks框架实战(二):开始使用 Apworks框架实战(三):单元测试与持续集成 Apworks框架实战(四):使用Visual Studio开发面向经典分层架构的应用程序:从EasyMemo案例开始 Apworks框架实战(五):EasyMemo的领域模型设计

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

创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添加一个User类: namespace MyFirstApp.Models { public class User { public int ID { get; set; } public string Name { get; set; } public string Email { get; se

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

简介 之前写个一个基于Dapper的分页实现,现在再来写一个基于Entity Framework的分页实现,以及增删改的通用实现. 代码 还是先上代码:https://github.com/jinweijie/EF.GenericRepository 如何运行示例 还是像先前一样: 1. 先Clone下代码,在Database里面解压缩Database.7z 2. Attach到Sql Server LocalDB上.如果你用的不是Sql Server的LocalDB,你需要更改App.Conf

应用程序框架实战二十一:DDD分层架构之仓储(介绍篇)

前面已经介绍过Entity Framework的工作单元和映射层超类型的封装,从本文开始,将逐步介绍仓储以及对查询的扩展支持. 什么是仓储 仓储表示聚合的集合. 仓储所表现出来的集合外观,仅仅是一种模拟,除了测试以外,没有理由使用内存中真正的集合来创建仓储. 不应该为所有实体建立仓储,只有聚合才拥有仓储. 仓储用来重建已持久化的聚合,而工厂用于新建聚合. 使用仓储的优点 直接使用Entity Framework的DbContext不是很好吗,为什么还要在DbContext的上方封装一层仓储呢,这

应用程序框架实战六:应用程序框架的特点

之前说过,.Net Framework为所有.Net程序员提供底层支持,第三方开源框架建立在.Net Framework的基础上,专门解决某些特定问题,而应用程序框架则建立在.Net Framework和第三方框架的基础上,为项目开发的方方面面保驾护航.它们的作用虽然都是帮助程序员更好的建立应用程序,但抽象层次不同,从比较原始到更加具体,导致了应用程序框架有一些自己的特点. 第一,没有真正通用的应用程序框架. 由于应用程序类型千差万别,没有哪个应用程序框架能够覆盖所有应用程序的细枝末节,所以真正

应用程序框架实战二十七: 基于Mvc+EasyUi+EF+Autofac的CRUD DEMO免费发放,纯干货,附截图

不知不觉,这个系列已经写了好几十篇了.我本来打算把基础介绍完再发放Demo进行整体说明,不过大部分人更喜欢看得见摸得着的表现层,对后端不是太感兴趣,所以我决定先发一个简单的CRUD Demo出来,让大家先感受一下,被应用程序框架封装之后的代码大体是什么样子. 采用EasyUi作为前端框架,主要是它比Dwz强大,另外也是基于Html扩展,比更强大的Ext要简单得多,更重要的是它越来越流行了,对于更详细的决择或前端架构设计,我会在后续文章说明. 虽然是一个简单的单表CRUD操作,但是分层架构和各方面

基于Entity FrameWork实现存储过程分页并返回总数

很多项目都会用到分页这个功能.网上也有很多这方面的资料,包括存储过程也是.但是基于EF来存储过程并返回总条数或者总页数的资料就没那么齐全了,至少我找了很久也没有找到.没有办法,项目有需求,那就自己一点点研究咯.好在皇天不负有心人,总算是弄出来了.这里写出来,方便你我他. 先上存储过程的代码: ALTER PROC [dbo].[Common_PageList](@tab nvarchar(max),---表名@strFld nvarchar(max), --字段字符串@strWhere varc

Entity Framework 实体框架的形成之旅--数据传输模型DTO和实体模型Entity的分离与联合

在使用Entity Framework 实体框架的时候,我们大多数时候操作的都是实体模型Entity,这个和数据库操作上下文结合,可以利用LINQ等各种方便手段,实现起来非常方便,一切看起来很美好.但是如果考虑使用WCF的时候,可能就会碰到很多相关的陷阱或者错误了.因为实体模型Entity的对象可能包括了其他实体的引用,在WCF里面就无法进行序列化,出现错误:而且基于WCF的时候,可能无法有效利用Express表达式,无法直接使用LINQ等问题都一股脑出现了.本文基于上面的种种问题,阐述了我的整