在ASP.NET Core中使用Apworks快速開(kāi)發(fā)數(shù)據(jù)服務(wù)》一文中,我介紹了如何使用Apworks框架的數(shù)據(jù)服務(wù)來(lái)快速構(gòu)建用于查詢(xún)和管理數(shù)據(jù)模型的RESTful API,通過(guò)該文的介紹,你會(huì)看到,使用Apworks框架開(kāi)發(fā)數(shù)據(jù)服務(wù)是何等簡(jiǎn)單快捷,提供的功能也非常多,比如對(duì)Hypermedia的支持,以及提供豐富的異常信息和調(diào)用棧信息。另外,Apworks數(shù)據(jù)服務(wù)可以支持各種類(lèi)型的倉(cāng)儲(chǔ)(Repository)實(shí)現(xiàn),在該文的案例中,我使用了MongoDB作為倉(cāng)儲(chǔ)的實(shí)現(xiàn),這是為了快速方便地演示數(shù)據(jù)服務(wù)的搭建過(guò)程。如果你所使用的是關(guān)系型數(shù)據(jù)庫(kù),也沒(méi)有關(guān)系(意思是不要緊,不是說(shuō)數(shù)據(jù)沒(méi)有關(guān)系。。。-_-!!!),基于Entity Framework Core的倉(cāng)儲(chǔ)實(shí)現(xiàn)能夠滿足你的需求。

需要注意的一個(gè)問(wèn)題

在以前老版本的Apworks中,倉(cāng)儲(chǔ)的接口是支持饑餓加載的,也就是說(shuō),在延遲加載被啟用的時(shí)候,倉(cāng)儲(chǔ)允許通過(guò)顯式指定一系列的對(duì)象屬性,當(dāng)主對(duì)象被返回時(shí),這些屬性所指向的子對(duì)象也會(huì)同時(shí)返回。這樣的設(shè)計(jì)在當(dāng)時(shí)的場(chǎng)景下是合理的,因?yàn)槭欠裥枰虞d某些屬性是可以在程序中指定的,對(duì)于類(lèi)似MongoDB的倉(cāng)儲(chǔ)實(shí)現(xiàn),它沒(méi)有延遲加載的概念,因此可以忽略這個(gè)參數(shù)。在Apworks數(shù)據(jù)服務(wù)中,由于倉(cāng)儲(chǔ)的操作會(huì)直接被DataServiceController調(diào)用,而相關(guān)的查詢(xún)條件都是來(lái)自于RESTful API的,因此,很難在API的層面來(lái)確定某些聚合的對(duì)象屬性是否需要饑餓加載(Eager Loading)。另一方面,禁用延遲加載又會(huì)產(chǎn)生性能問(wèn)題,因此,在當(dāng)前版本的實(shí)現(xiàn)中,我還沒(méi)有考慮好用何種方式來(lái)解決這個(gè)問(wèn)題?;蛟S可以通過(guò)HTTP Header來(lái)指定需要饑餓加載的屬性路徑,但這是另一個(gè)問(wèn)題??傊?,在接下來(lái)的案例中,你將看到,雖然數(shù)據(jù)已經(jīng)添加成功,但在返回的結(jié)果里,被聚合的子對(duì)象將無(wú)法返回。我會(huì)設(shè)法解決這個(gè)問(wèn)題。

案例:Customer Service

假設(shè)我們需要使用Entity Framework快速構(gòu)建一個(gè)支持增刪改查操作的數(shù)據(jù)服務(wù)(Data Service),并希望該服務(wù)能夠在容器中運(yùn)行,我們可以首先新建一個(gè)ASP.NET Core的應(yīng)用程序,然后依照下面的步驟進(jìn)行:

  1. 向ASP.NET Core應(yīng)用程序添加以下NuGet包引用:

    • Apworks.Integration.AspNetCore

    • Apworks.Repositories.EntityFramework

    • Microsoft.EntityFrameworkCore.SqlServer(在本案例中我們使用SQL Server,當(dāng)然也可以使用PostgreSQL或者M(jìn)ySQL等)

    • Microsoft.EntityFrameworkCore.Tools

  2. 新建領(lǐng)域模型,向ASP.NET Core應(yīng)用程序中添加Customer和Address類(lèi): 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    public class Address
    {
        public Guid Id { get; set; }
     
        public string Country { get; set; }
     
        public string State { get; set; }
     
        public string City { get; set; }
     
        public string Street { get; set; }
     
        public string ZipCode { get; set; }
    }
     
    public class Customer : IAggregateRoot<Guid>
    {
        public Guid Id { get; set; }
     
        public string Name { get; set; }
     
        public string Email { get; set; }
     
        public Address ContactAddress { get; set; }
    }
    <font face="Calibri"></font>
  3. 新建一個(gè)DbContext類(lèi),用于指定數(shù)據(jù)庫(kù)的訪問(wèn)方式,以及對(duì)模型對(duì)象/數(shù)據(jù)表結(jié)構(gòu)進(jìn)行映射: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public class CustomerDbContext : DbContext
    {
        public DbSet<Customer> Customers { get; set; }
     
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Customer>()
                .ToTable("Customers")
                .HasKey(x => x.Id);
            modelBuilder.Entity<Customer>()
                .Property(x => x.Id)
                .ForSqlServerHasDefaultValueSql("newid()");
            modelBuilder.Entity<Customer>()
                .Property(x => x.Name)
                .IsUnicode()
                .IsRequired()
                .HasMaxLength(20);
            modelBuilder.Entity<Customer>()
                .Property(x => x.Email)
                .IsUnicode()
                .IsRequired()
                .HasMaxLength(50);
            modelBuilder.Entity<Address>()
                .ToTable("Addresses")
                .HasKey(x => x.Id);
            modelBuilder.Entity<Address>()
                .Property(x => x.Id)
                .ForSqlServerHasDefaultValueSql("newid()");
     
            base.OnModelCreating(modelBuilder);
        }
     
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"Server=localhost\sqlexpress; Database=CustomerService; Integrated Security=SSPI;");
        }
    }
    <font face="Calibri"></font>
  4. 打開(kāi)Package Manager Console,并在解決方案資源管理器中將當(dāng)前ASP.NET Core項(xiàng)目設(shè)置為啟動(dòng)項(xiàng)目(注意:這一點(diǎn)非常重要)。然后依次執(zhí)行: 

    1
    2
    Add-Migration InitialCreate
    Update-Database

    成功完成這一步驟后,我們的數(shù)據(jù)庫(kù)就已經(jīng)準(zhǔn)備好了。事實(shí)上,以上步驟都是開(kāi)發(fā)一個(gè)Entity Framework Core應(yīng)用程序所必須經(jīng)歷的標(biāo)準(zhǔn)步驟,目前還沒(méi)有用到Apworks的功能(當(dāng)然,將Customer定義成聚合根除外)。接下來(lái),我們開(kāi)始實(shí)現(xiàn)并配置Apworks數(shù)據(jù)服務(wù),接下來(lái)的步驟跟基于MongoDB的實(shí)現(xiàn)非常類(lèi)似。 

  5. 在ASP.NET Core應(yīng)用程序的Controllers文件夾下,新建一個(gè)CustomersController,從DataServiceController繼承: 

    1
    2
    3
    4
    5
    6
    public class CustomersController : DataServiceController<Guid, Customer>
    {
        public CustomersController(IRepositoryContext repositoryContext) : base(repositoryContext)
        {
        }
    }


  6. 打開(kāi)Startup.cs文件,分別修改ConfigureServices和Configure方法,如下: 

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        // Add framework services.
        services.AddMvc();
        services.AddScoped<CustomerDbContext>();
        services.AddApworks()
            .WithDataServiceSupport(new DataServiceConfigurationOptions(sp =>
                new EntityFrameworkRepositoryContext(sp.GetService<CustomerDbContext>())))
            .Configure();
    }
     
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
     
        app.EnrichDataServiceExceptionResponse();
     
        app.UseMvc();
    }


    與MongoDB實(shí)現(xiàn)不同的是,在使用EntityFrameworkRepositoryContext之前,我們需要通過(guò)services.AddScoped方法,將CustomerDbContext以Scoped生命周期注冊(cè)到IoC容器中,而在初始化EntityFrameworkRepositoryContext時(shí),使用Service Provider的GetService方法進(jìn)行Resolve,這樣就能確保每次HTTP請(qǐng)求完成時(shí),資源都能成功釋放。 

下面,我們來(lái)測(cè)試一下這個(gè)Apworks數(shù)據(jù)服務(wù)。在Visual Studio 2017中按下Ctrl+F5,直接運(yùn)行ASP.NET Core應(yīng)用程序,使用你喜歡的RESTful客戶端軟件,向/api/Customers進(jìn)行POST操作,可以看到,Customer可以被成功創(chuàng)建,Customer Id即刻返回:

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

讓我們?cè)貵ET一下試試(注意:返回的ContactAddress是null,而事實(shí)上數(shù)據(jù)庫(kù)里是有值的。這里返回null的原因是因?yàn)槲覀儧](méi)有在Entity Framework中通過(guò)Include調(diào)用進(jìn)行饑餓加載(Eager Loading),接下來(lái)會(huì)嘗試解決這個(gè)問(wèn)題):

iOS培訓(xùn),Swift培訓(xùn),蘋(píng)果開(kāi)發(fā)培訓(xùn),移動(dòng)開(kāi)發(fā)培訓(xùn)

除了ContactAddress在GET請(qǐng)求中返回為null之外,其它各種行為,包括數(shù)據(jù)服務(wù)所支持的API接口、調(diào)用方式等,都與之前MongoDB的實(shí)現(xiàn)完全相同。

源代碼

本文案例中的源代碼可以在Apworks Examples開(kāi)源項(xiàng)目中找到。本案例的源代碼在Apworks.Examples.CustomerService.EntityFramework目錄下。

總結(jié)

本文帶領(lǐng)著大家一起預(yù)覽了Apworks數(shù)據(jù)服務(wù)對(duì)Entity Framework Core的支持,使得Apworks數(shù)據(jù)服務(wù)不僅可以使用MongoDB等NoSQL存儲(chǔ)方案,也可以使用關(guān)系型數(shù)據(jù)庫(kù)存儲(chǔ)方案,而且編程體驗(yàn)也是幾乎相同的。這對(duì)于不同應(yīng)用場(chǎng)景下微服務(wù)的實(shí)現(xiàn)是非常有幫助的。雖然在Entity Framework Core的實(shí)現(xiàn)中,目前有些瑕疵,但我會(huì)盡快解決這個(gè)問(wèn)題。

http://www.cnblogs.com/daxnet/p/7157896.html