在ASP.NET MVC Web API中使用Apworks開發第一個HTTP服務


上周我發布了Apworks框架的最新版本,打算寫點文章來介紹如何在實際項目中使用Apworks進行開發。今天先簡單寫一點東西,首先說明一下,Apworks本身是面向領域驅動的一套開發框架,因此,領域模型的分析和建立就是使用Apworks的重點;然而,在大家對Apworks還沒有任何了解的基礎上,我暫時先拋開領域模型的建立,先向大家展示一下,如何使用Apworks開發第一個可以運行的程序。在這篇文章的介紹中,我們的領域模型只有一個聚合:日記(Note),而且我們會將這個聚合對象同時用作數據傳輸對象,這當然與DDD的宗旨是違背的,但為了簡化介紹過程,我們也不把問題復雜化了。現在,就讓我們一起來創建一個對“日記”信息進行維護的ASP.NET MVC Web API應用程序吧。

通過本文的介紹,你將了解到:

  • Apworks下領域模型的建立
  • Apworks框架的初始化
  • 使用基於Entity Framework的倉儲實現聚合維護

新建ASP.NET MVC Web API項目

首先,新建一個ASP.NET MVC Web API項目,這個過程很簡單,打開Visual Studio,然后新建一個ASP.NET MVC 4 Web Application,取名為NoteService(姑且取這個名字吧),然后在New ASP.NET MVC 4 Project對話框中,選擇Web API模板,然后直接單擊OK按鈕:

SNAGHTML22d4e78

在完成解決方案的創建以后,在Solution Explorer(解決方案資源管理器)中,可以看到標准的Web API目錄結構:

image

創建領域模型

再次聲明,雖然接下來的步驟會在NoteService的Models目錄下新建領域模型,但Models目錄本身是用來定義View Model的,作為領域模型,定義在另一個單獨的程序集中會更合適。總之,需要對領域模型(Domain Model)和視圖模型(View Model)進行區分。

首先,在NoteService項目上單擊右鍵,選擇Manage NuGet Packages菜單,在彈出的對話框中,搜索Apworks,然后選擇Apworks,單擊Install按鈕:

SNAGHTML28f775e

接下來,在項目的Models目錄上點右鍵,選擇“Add –> Class”菜單,創建一個類,將類保存成Note.cs文件,並在文件中輸入以下代碼:

using Apworks;
using System;

namespace NoteService.Models
{
    public enum Weather
    {
        Cloudy, Rainy, Sunny, Windy
    }

    public class Note : IAggregateRoot
    {
        public Guid ID { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }
        public DateTime CreatedDate { get; set; }
        public Weather Weather { get; set; }
    }
}

行了,目前我們就只創建一個聚合:Note。從上面的代碼得知,聚合的根需要繼承於IAggregateRoot接口。

使用Unity作為IoC容器

Apworks目前僅集成了Unity作為整個框架的IoC容器,因此,我們需要為Unity的使用作准備。同樣,打開Manage NuGet Packages對話框,從中選擇Apworks Unity Object Container,然后點擊Install安裝。注意:此時NuGet會把所依賴的Unity也一並安裝:

SNAGHTML2cf7fc8

另外,我們還需要添加對Unity.WebAPI組件的引用,該組件提供了Unity對WebAPI的集成,以便能夠在WebAPI中更好地使用Unity。用同樣的方法添加引用:

SNAGHTML2d1cc80

OK,使用Unity的准備工作已經完成了,接下來,我們對Apworks進行配置。

配置Apworks框架

Apworks框架提供三種配置方式:app/web.config、直接寫代碼配置(使用RegluarConfigSource類),以及Fluent  Interface。為應用框架提供多樣化的配置方式,這是框架架構中必不可少的工作,究其原因和實現方式,請參考我以前寫的一篇文章:《.NET應用框架架構設計實踐 - 為應用程序框架提供多樣化的配置方式》。Fluent Interface這種配置方式的實現,請參考我前面寫的文章:《在C#中使用裝飾器模式和擴展方法實現Fluent Interface》。下面言歸正傳。

打開Global.asax.cs文件,首先添加對Apworks.Application、Apworks.Config.Fluent、Microsoft.Practices.Unity和Unity.WebApi這幾個命名空間的引用,然后在Application_Start中添加以下代碼:

AppRuntime.Instance
    .ConfigureApworks()
    .UsingUnityContainerWithDefaultSettings()
    .Create((sender, e) =>
        {
            var container = e.ObjectContainer.GetWrappedContainer<UnityContainer>();
            // TODO: register types
            GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
        }).Start();

上面代碼應該非常清晰地表述了配置內容,我也就不多解釋了,在應用中可以使用IntelliSense來了解一下Apworks Fluent Interface還實現了哪些配置項目。

使用基於Entity Framework的倉儲

以同樣的方法,引入Apworks Entity Framework Repository組件:

SNAGHTML2e7f1eb

在NoteService項目上新建一個類,取名為NoteServiceDbContext,該類實現如下:

public class NoteServiceDbContext : DbContext
{
    public NoteServiceDbContext()
        : base("NoteServiceDB") { }

    public DbSet<Note> Notes { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Note>()
            .HasKey(p => p.ID)
            .Property(p => p.ID)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        base.OnModelCreating(modelBuilder);
    }
}

另外,為了能在第一次創建數據庫時,加入一些樣本數據作為測試,我還添加了以下類:

public class NoteServiceInitializer : DropCreateDatabaseIfModelChanges<NoteServiceDbContext>
{
    protected override void Seed(NoteServiceDbContext context)
    {
        new List<Note>
        {
            new Note
            {
                Title = "My first note", 
                Content = "This is my first note.", 
                CreatedDate = DateTime.Now, 
                Weather = Weather.Sunny
            },
            new Note
            {
                Title = "My second note", 
                Content = "This is my second note.", 
                CreatedDate = DateTime.Now, 
                Weather = Weather.Windy
            }
        }.ForEach(p => context.Notes.Add(p));
    }
}

好了,Entity Framework的准備已經做好,接下來就是幾個常規的配置項。

首先,修改web.config文件,將Entity Framework的數據庫連接配置加上:

<entityFramework>
	<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
	  <parameters>
		<parameter value="Data Source=(LocalDb)\v11.0; Initial Catalog=NoetServiceDB; Integrated Security=True; Connect Timeout=120; MultipleActiveResultSets=True; AttachDBFilename=|DataDirectory|\NoteServiceDB.mdf" />
	  </parameters>
	</defaultConnectionFactory>
</entityFramework>

然后,打開Global.asax.cs文件,將Application_Start方法修改成:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    // Initialize database
    Database.SetInitializer<NoteServiceDbContext>(new NoteServiceInitializer());

    AppRuntime.Instance
        .ConfigureApworks()
        .UsingUnityContainerWithDefaultSettings()
        .Create((sender, e) =>
            {
                var container = e.ObjectContainer.GetWrappedContainer<UnityContainer>();
                // TODO: register types
                container.RegisterInstance<NoteServiceDbContext>(new NoteServiceDbContext(), new PerResolveLifetimeManager())
                    .RegisterType<IRepositoryContext, EntityFrameworkRepositoryContext>(new HierarchicalLifetimeManager(),
                        new InjectionConstructor(new ResolvedParameter<NoteServiceDbContext>()))
                    .RegisterType(typeof(IRepository<>), typeof(EntityFrameworkRepository<>));
                GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
            }).Start();
}

這部分代碼中,改動的地方是:1、使用Database.SetInitializer方法,設置EF的數據庫初始化策略,我們使用已經編寫好的NoteServiceInitializer作為初始化策略;2、在Create的委托方法中,我們添加了對IRepositoryContext、IRepository<>以及NoteServiceDbContext的類型注冊,以使用Entity Framework Repository。注意:IRepositoryContext被注冊為HierarchicalLifetimeManager生命周期,以便Unity能夠在Request結束時能夠正確調用IRepositoryContext的Dispose方法。詳細請參見:http://devtrends.co.uk/blog/introducing-the-unity.webapi-nuget-package

開發Web API服務

在Solution Explorer(解決方案資源管理器)中,將ValuesController改名為NotesController,然后,改寫NotesController類如下:

using Apworks.Repositories;
using NoteService.Models;
using System;
using System.Collections.Generic;
using System.Web.Http;

namespace NoteService.Controllers
{
    public class NotesController : ApiController
    {
        readonly IRepository<Note> noteRepository;

        public NotesController(IRepository<Note> noteRepository)
        {
            this.noteRepository = noteRepository;
        }

        // GET api/notes
        public IEnumerable<Note> Get()
        {
            return noteRepository.FindAll();
        }

        // GET api/notes/6EE246A5-9E68-4BC9-BA24-7F4EC2B326D4
        public Note Get(Guid id)
        {
            return noteRepository.GetByKey(id);
        }

        // POST api/notes
        public void Post([FromBody]Note value)
        {
            noteRepository.Add(value);
            noteRepository.Context.Commit();
        }

        // PUT api/notes/6EE246A5-9E68-4BC9-BA24-7F4EC2B326D4
        public void Put(Guid id, [FromBody]Note value)
        {
            var note = noteRepository.GetByKey(id);
            note.Title = value.Title;
            note.Content = value.Content;
            note.CreatedDate = value.CreatedDate;
            note.Weather = value.Weather;
            noteRepository.Update(note);
            noteRepository.Context.Commit();
        }

        // DELETE api/notes/6EE246A5-9E68-4BC9-BA24-7F4EC2B326D4
        public void Delete(Guid id)
        {
            var note = noteRepository.GetByKey(id);
            noteRepository.Remove(note);
            noteRepository.Context.Commit();
        }
    }
}

OK,至此,一個使用Apworks開發的ASP.NET MVC Web API服務已經完成,由於DependencyResolver的使用,NotesController在被創建時會獲得IRepository<Note>的實例(由IoC通過構造函數注入),於是,在每個方法調用中,都能使用Note倉儲完成所需的操作。

測試

我們可以使用Microsoft ASP.NET Web API Client Libraries對開發的Web API服務進行測試,具體用法也就不說了,可以自行參閱網上的文章。例如:可以用以下方法測試GET請求:

[TestClass]
public class NoteServiceTest
{
    static readonly HttpClient client = new HttpClient();

    [ClassInitialize]
    public static void TestInitialize(TestContext context)
    {
        client.BaseAddress = new Uri("http://localhost:10895");
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    }

    [TestMethod]
    public void GetTest()
    {
        var response = client.GetAsync("api/notes").Result;
        Assert.IsTrue(response.IsSuccessStatusCode);
        var notes = response.Content.ReadAsAsync<IEnumerable<Note>>().Result;
        Assert.IsTrue(notes.Count() > 0);
    }
}

限於篇幅,其它方法的測試用例我就不貼代碼了,我是通過了所有測試的:

image

總結

本文介紹了在ASP.NET MVC Web API中使用Apworks框架開發一個簡單的HTTP服務的一般步驟。通過本文的介紹,我們可以了解到如何基於Apworks創建我們的領域模型,如何配置Apworks以使用Unity Container,如何配置Apworks以使用基於Entity Framework的倉儲。同時我們還了解到了一些ASP.NET MVC Web API的開發技術,希望本文對打算使用Apworks進行面向領域驅動架構開發的開發人員有一定的幫助。

或許在讀完本文之后,你會覺得,在本案例中,直接使用Entity Framework貌似要比使用Apworks來得更快。不錯,本案例僅僅是對Apworks的一個開場演示,它的重點是在Apworks框架的使用上,而不是在Entity Framework上。主要是時間有限,我目前沒有辦法再去使用Apworks重新做一套完整的案例(事實上我已經向社區貢獻了一個案例:Byteart Retail,不過它並沒有使用Apworks的任何組件),但我有可能會在近期將Byteart Retail改成使用Apworks實現,相信到時候你會發現:使用Apworks開發面向領域驅動分層架構的應用程序,真的非常簡單。我也會不定期地繼續發布一些有關Apworks框架應用的文章,來幫助大家更好地理解這個框架。

本文案例代碼

【單擊此處】下載本文案例代碼,下載解壓后請用Visual Studio 2012 Update 2打開解決方案文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM