在Apworks數據服務中使用基於Entity Framework Core的倉儲(Repository)實現


在ASP.NET Core中使用Apworks快速開發數據服務》一文中,我介紹了如何使用Apworks框架的數據服務來快速構建用於查詢和管理數據模型的RESTful API,通過該文的介紹,你會看到,使用Apworks框架開發數據服務是何等簡單快捷,提供的功能也非常多,比如對Hypermedia的支持,以及提供豐富的異常信息和調用棧信息。另外,Apworks數據服務可以支持各種類型的倉儲(Repository)實現,在該文的案例中,我使用了MongoDB作為倉儲的實現,這是為了快速方便地演示數據服務的搭建過程。如果你所使用的是關系型數據庫,也沒有關系(意思是不要緊,不是說數據沒有關系。。。-_-!!!),基於Entity Framework Core的倉儲實現能夠滿足你的需求。

需要注意的一個問題

在以前老版本的Apworks中,倉儲的接口是支持飢餓加載的,也就是說,在延遲加載被啟用的時候,倉儲允許通過顯式指定一系列的對象屬性,當主對象被返回時,這些屬性所指向的子對象也會同時返回。這樣的設計在當時的場景下是合理的,因為是否需要加載某些屬性是可以在程序中指定的,對於類似MongoDB的倉儲實現,它沒有延遲加載的概念,因此可以忽略這個參數。在Apworks數據服務中,由於倉儲的操作會直接被DataServiceController調用,而相關的查詢條件都是來自於RESTful API的,因此,很難在API的層面來確定某些聚合的對象屬性是否需要飢餓加載(Eager Loading)。另一方面,禁用延遲加載又會產生性能問題,因此,在當前版本的實現中,我還沒有考慮好用何種方式來解決這個問題。或許可以通過HTTP Header來指定需要飢餓加載的屬性路徑,但這是另一個問題。總之,在接下來的案例中,你將看到,雖然數據已經添加成功,但在返回的結果里,被聚合的子對象將無法返回。我會設法解決這個問題。

案例:Customer Service

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

  1. 向ASP.NET Core應用程序添加以下NuGet包引用:
    • Apworks.Integration.AspNetCore
    • Apworks.Repositories.EntityFramework
    • Microsoft.EntityFrameworkCore.SqlServer(在本案例中我們使用SQL Server,當然也可以使用PostgreSQL或者MySQL等)
    • Microsoft.EntityFrameworkCore.Tools
  2. 新建領域模型,向ASP.NET Core應用程序中添加Customer和Address類:
    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; }
    }
    
  3. 新建一個DbContext類,用於指定數據庫的訪問方式,以及對模型對象/數據表結構進行映射:
    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;");
        }
    }
    
  4. 打開Package Manager Console,並在解決方案資源管理器中將當前ASP.NET Core項目設置為啟動項目(注意:這一點非常重要)。然后依次執行:
    Add-Migration InitialCreate
    Update-Database
    成功完成這一步驟后,我們的數據庫就已經准備好了。事實上,以上步驟都是開發一個Entity Framework Core應用程序所必須經歷的標准步驟,目前還沒有用到Apworks的功能(當然,將Customer定義成聚合根除外)。接下來,我們開始實現並配置Apworks數據服務,接下來的步驟跟基於MongoDB的實現非常類似。
  5. 在ASP.NET Core應用程序的Controllers文件夾下,新建一個CustomersController,從DataServiceController繼承:
    public class CustomersController : DataServiceController<Guid, Customer>
    {
        public CustomersController(IRepositoryContext repositoryContext) : base(repositoryContext)
        {
        }
    }

  6. 打開Startup.cs文件,分別修改ConfigureServices和Configure方法,如下:
    // 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實現不同的是,在使用EntityFrameworkRepositoryContext之前,我們需要通過services.AddScoped方法,將CustomerDbContext以Scoped生命周期注冊到IoC容器中,而在初始化EntityFrameworkRepositoryContext時,使用Service Provider的GetService方法進行Resolve,這樣就能確保每次HTTP請求完成時,資源都能成功釋放。

下面,我們來測試一下這個Apworks數據服務。在Visual Studio 2017中按下Ctrl+F5,直接運行ASP.NET Core應用程序,使用你喜歡的RESTful客戶端軟件,向/api/Customers進行POST操作,可以看到,Customer可以被成功創建,Customer Id即刻返回:

image

讓我們再GET一下試試(注意:返回的ContactAddress是null,而事實上數據庫里是有值的。這里返回null的原因是因為我們沒有在Entity Framework中通過Include調用進行飢餓加載(Eager Loading),接下來會嘗試解決這個問題):

image

除了ContactAddress在GET請求中返回為null之外,其它各種行為,包括數據服務所支持的API接口、調用方式等,都與之前MongoDB的實現完全相同。

源代碼

本文案例中的源代碼可以在Apworks Examples開源項目中找到。本案例的源代碼在Apworks.Examples.CustomerService.EntityFramework目錄下。

總結

本文帶領着大家一起預覽了Apworks數據服務對Entity Framework Core的支持,使得Apworks數據服務不僅可以使用MongoDB等NoSQL存儲方案,也可以使用關系型數據庫存儲方案,而且編程體驗也是幾乎相同的。這對於不同應用場景下微服務的實現是非常有幫助的。雖然在Entity Framework Core的實現中,目前有些瑕疵,但我會盡快解決這個問題。


免責聲明!

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



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