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

在前面的章节中,咱们已经设计了一个简单的领域模型,接下来咱们但愿可以实现领域模型的持久化及查询。在Apworks中,实现了面向Entity Framework、NHibernate以及MongoDB的仓储基础结构。在本章节中,我将向你们介绍如何在Apworks中使用基于Entity Framework的仓储机制。web

搭建基于Entity Framework的基础结构

在使用Apworks提供的仓储服务以前,咱们首先须要搭建好基于Entity Framework的基础结构,以便接下来的Apworks可以使用这些基础结构功能,并利用Entity Framework实现领域模型对象生命周期的管理。数据库

从DbContext开始

咱们采用Entity Framework Code First的编程模型,所以,咱们将从DbContext开始入手,为Entity Framework仓储机制的使用作好准备工做。编程

首先,在【EasyMemo.Repositories】项目上单击鼠标右键,选择【管理NuGet程序包】选项。在弹出的【管理NuGet程序包】的【搜索联机】文本框中,输入关键字【apworks】。在过滤的列表中,找到【Apworks.Repositories.EntityFramework】,而后单击【安装】按钮。api

image

说明:安装该程序包也会顺带将其所依赖的程序包一并安装到【EasyMemo.Repositories】项目中,这些程序包包括:浏览器

  • Apworks 2.5.5662.37915
  • Castle.Core 3.3.1
  • EntityFramework 6.1.1

接下来,在【EasyMemo.Repositories】项目中,新建一个名为EasyMemoContext的类,该类从System.Data.Entity.DbContext类继承,代码以下:app

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对象,咱们能够定义映射配置类型以下:ide

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重载方法中:ui

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new AccountEntityConfiguration());
}

OK,接下来使用相似的方法针对领域模型中必要的实体类型定义映射配置类,并依次将这些类的实例添加到OnModelCreating重载方法中。限于篇幅,在此就不一一列出代码了,您能够在本章节结尾部分点击下载代码的连接,把源代码下载到本地做参考。this

设置数据库初始化策略

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的类:

image

该类的代码以下:

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自动产生的数据库了:

image

 

打开【Microsoft SQL Server Management Studio】,链接到所配置的数据库实例,咱们能够看到EasyMemoDB已经出如今数据库列表中:

image

 

而且能够查询到咱们预先准备好的数据:

image

由Entity Framework自动产生的数据库结构以下:

image

固然,目前咱们无需对这个数据模型关注太多,毕竟咱们不打算面向数据库编程。

开始使用基于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控制器 - 空】:

image

在弹出的【添加控制器】对话框的【控制器名称】一栏,填入【AccountsController】:

image

在单击【添加】按钮后,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这个帐户的详细信息:

image

总结

本文详细介绍了如何在Apworks框架中使用Entity Framework并为一个ASP.NET Web API的RESTful服务提供仓储及其上下文的基础结构。从下一讲开始,咱们会重点讨论ASP.NET Web API实现的方方面面,包括异常处理、认证与受权、数据传输对象与视图模型等。

源代码下载

【单击此处】下载截止到本文为止的EasyMemo解决方案源代码。

相关文章
相关标签/搜索