zoukankan      html  css  js  c++  java
  • 在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=localhostsqlexpress; 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的实现中,目前有些瑕疵,但我会尽快解决这个问题。

  • 相关阅读:
    Extjs 4 生成饼状图的例子
    cocos2d-x 3.0rc2 对于每个包执行情况的重要平台 (超级方便)
    HDOJ 1495 非常可乐 【BFS】
    Android采用HttpClient下载图片
    解决本地访问Android文档是非常慢的问题
    潜在语义分析Latent semantic analysis note(LSA)原理及代码
    你奋斗这么辛苦,这辈子要证明什么?
    Objective-C时间戳转换的转换和时间
    Delphi 线程resume 不能调用Execute
    Delphi 多线程 “尚未调用CoInitialize错误”的解决方法
  • 原文地址:https://www.cnblogs.com/daxnet/p/7157896.html
Copyright © 2011-2022 走看看