在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即刻返回:

让我们再GET一下试试(注意:返回的ContactAddress是null,而事实上数据库里是有值的。这里返回null的原因是因为我们没有在Entity Framework中通过Include调用进行饥饿加载(Eager Loading),接下来会尝试解决这个问题):

除了ContactAddress在GET请求中返回为null之外,其它各种行为,包括数据服务所支持的API接口、调用方式等,都与之前MongoDB的实现完全相同。

源代码

本文案例中的源代码可以在Apworks Examples开源项目中找到。本案例的源代码在Apworks.Examples.CustomerService.EntityFramework目录下。

总结

本文带领着大家一起预览了Apworks数据服务对Entity Framework Core的支持,使得Apworks数据服务不仅可以使用MongoDB等NoSQL存储方案,也可以使用关系型数据库存储方案,而且编程体验也是几乎相同的。这对于不同应用场景下微服务的实现是非常有帮助的。虽然在Entity Framework Core的实现中,目前有些瑕疵,但我会尽快解决这个问题。

在Apworks数据服务中使用基于Entity Framework Core的仓储(Repository)实现的更多相关文章

  1. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

    创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...

  2. vs for Mac中的启用Entity Framework Core .NET命令行工具

    在vs for Mac的工具菜单中已没有了Package Manager Console. 我们可以通过以下方法使用Entity Framework Core .NET命令行工具: 1.添加Nuget ...

  3. 请问在 .NET Core 中如何让 Entity Framework Core 在日志中记录由 LINQ 生成的SQL语句?

    using dotNET.Core; using Microsoft.Extensions.Logging; using System; using System.Collections.Generi ...

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

    在前面的章节中,我们已经设计了一个简单的领域模型,接下来我们希望能够实现领域模型的持久化及查询.在Apworks中,实现了面向Entity Framework.NHibernate以及MongoDB的 ...

  5. Entity Framework Core for Console

    包 Microsoft.EntityFrameworkCore Microsoft.EntityFrameworkCore.SqlServer Microsoft.EntityFrameworkCor ...

  6. UWP开发之ORM实践:如何使用Entity Framework Core做SQLite数据持久层?

    选择SQLite的理由 在做UWP开发的时候我们首选的本地数据库一般都是Sqlite,我以前也不知道为啥?后来仔细研究了一下也是有原因的: 1,微软做的UWP应用大部分也是用Sqlite.或者说是微软 ...

  7. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 更新关系数据

    Updating related data¶ 7 of 7 people found this helpful The Contoso University sample web applicatio ...

  8. 浅析Entity Framework Core中的并发处理

    前言 Entity Framework Core 2.0更新也已经有一段时间了,园子里也有不少的文章.. 本文主要是浅析一下Entity Framework Core的并发处理方式. 1.常见的并发处 ...

  9. Working with Data » Getting started with ASP.NET Core and Entity Framework Core using Visual Studio » 读取关系数据

    Reading related data¶ 9 of 9 people found this helpful The Contoso University sample web application ...

随机推荐

  1. NetFramework各个版本的特性笔记

    公式记忆: .Net 2.0=CLR+BCL+C#(VB.Net)+Win Form+Web Form .Net 3.0=.Net 2.0+WCF+WPF+WF+WCS .Net 3.5=.Net 3 ...

  2. JDFS:一款分布式文件管理实用程序第二篇(更新升级、解决一些bug)

    一 前言 本文是<JDFS:一款分布式文件管理实用程序>系列博客的第二篇,在上一篇博客中,笔者向读者展示了JDFS的核心功能部分,包括:服务端与客户端部分的上传.下载功能的实现,epoll ...

  3. VR全景,零售业冬天里的一把火——全景智慧城市

    对今天的中国来说,寻找经济转型的突破口,寻找经济权力的新霸主,零售业应该当仁不让. 零售业正在经历一场脱胎换骨的改造.一方面零售额达到前所未有的水平,另一方面,传统零售商也面临诸多挑战,其中之一,便是 ...

  4. JVM-7.Java内存模型与高效并发

    更多内容参见<并发与同步>系列 一.引子 二.JMM 三.Java中的线程 四.线程安全 五.锁优化       一.引子 运算能力 摩尔定律:晶体管数量,代表的CPU的频率 Amdahl ...

  5. javaSE_05Java中方法(函数)与重载、递归-练习

    1.使用的递归的方法求5! public class DiGui{ public static void main(String[] args){ //使用的递归的方法求5! System.out.p ...

  6. 天气正好,hello world!

    两个多月,稀里糊涂的回来了,内心很迷茫,回来一周了,明知道还需要有一大堆东西需要去学,但是却找不到之前学习的状态,在寝室,在实验室,看着自己一向不喜欢的电视剧,无目的的逛着淘宝,刷着头条和知乎,就这么 ...

  7. 常见浏览器的宽高代码写法!有原生JavaScript和jquery两种写法-------------------------------以及我的个人网站

    我的个人网站 点击链接!欢迎大家访问 下面是网页一些常见的宽高的获取.........这是原生的写法(JavaScript) 网页可见区域宽: document.body.clientWidth 网页 ...

  8. Hadoop 2.7 伪分布式环境搭建

    1.安装环境 ①.一台Linux CentOS6.7 系统 hostname                ipaddress              subnet mask             ...

  9. Facebook开源Zstandard新型压缩算法代替Zlib 简单使用

    简介 Zstandard(缩写为Zstd)是由Facebook的Yann Collet开发的一个无损数据压缩算法.Zstandard在设计上与DEFLATE(.zip.gzip)算法有着差不多的压缩比 ...

  10. 通过LOGBACK实现每个类、包或自定义级别

    项实现LOGBACK对每个包或者类或者通过自定义级别的方式实现自定义输出的日志进入制定的文件.查阅了很多资料,都没有找到行之有效的解决方案,直到看到了这篇文章http://www.360doc.com ...