GraphQL Part VI: 使用 Postgres 和 EF Core 持久化数据
这次我们关注持久化数据而不是 GraphQL 方面。我们将使用 Postgres 数据库作为后端存储,您可能问:为什么要使用 Postgres?因为大家都熟悉 SQL Server,我们尝试一下新东西。
在数据访问层,我们将使用一个数据源类,或者说是一个仓储。基于抽象的最佳实践,我们首先创建数据源的接口,IDataStore
public interface IDataStore
{
IEnumerable<Item> GetItems();
Item GetItemByBarcode(string barcode);
}
我们已经用过 GetItemByBarcode 方法了,GetItems 方法返回库存的所有条目。我们随后将会添加一个 GraphQL 的集合。
实现该接口非常简单。
public class DataStore : IDataStore
{
private ApplicationDbContext _applicationDbContext; public DataStore(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
} public Item GetItemByBarcode(string barcode)
{
return _applicationDbContext.Items.First(i => i.Barcode.Equals(barcode));
} public IEnumerable<Item> GetItems()
{
return _applicationDbContext.Items;
}
}
我们使用 Entity Framework Core,这里我们引入 ApplicationDbContext。该类扩展自 Entity Framework 的 DbContext。现在它包含单个的关于 Item 实体的 DbSet 。这将会创建一个名为 Items 的表。
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{ }
public DbSet<Item> Items { get; set; }
}
DbContextOptions 是很酷的传递参数的方式,例如数据库连接串 ConnectionString,在 Startup.cs 的 ConfigureServices 方法中配置 ApplicationDbContext。
services.AddEntityFrameworkNpgsql()
.AddDbContext<ApplicationDbContext>(
options => options.UseNpgsql(Configuration["DefaultConnection"]));
AddEntityFrameworkNpgsql() 扩展方法来自独立的 Npgsql.EntityFrameworkCore.PostgreSQL 包。可以通过 Nuget 或者 dotnet CLI 安装:
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL --version 2.0.2
Configuration 属性来自类型 IConfigurationRoot。我们在 Startup.cs 的构造函数中构建配置对象,并赋予 Configuration 属性。
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
if (env.IsDevelopment())
{
builder.AddUserSecrets<Startup>();
}
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
我们可以把数据库连接串保存在 appsettings.json 文件中,对于我来说,由于保密问题,我总是将它们保存在用户密钥文件中。这里的 builder.AddUserSecrets<Startup>()。你可以在用户密钥文件中如下添加数据库连接串。
dotnet user-secrets set DefaultConnection 'your-connection-string'
然后如下执行初始迁移的命令。
dotnet ef migrations add Initial --output-dir Data\Migrations
使用如下的命令应用迁移命令以创建数据库。
dotnet ef database update
AddDbContext<ApplicationDbContext>() 使用受限的服务生命周期注册 DbContext 。Singleton 和 scope 之间的区别是:
- Singleton 服务实例仅仅创建一次,在应用启动的时候。同样的实例被以后所有的请求所共享。
- Scopt 服务实例在每次被请求的时候重新创建
到目前为止,我们一直使用单例模式,但是,如果我们这样用于 IDataStore,会导致下面的问题:
- 如果你小心检查,我们将 ApplicationDbContext 直接注入到 DataStore,简单来说,我们在通过一个单例的服务访问 scoped 的服务。
- 尽管 scope 的服务实例会在每次请求的时候创建,但我们是从单例对象来访问它的,这导致永远返回第一次创建的实例,结果与单例一样了。
所以,我们必须也将 IDataStore 也注册为 scoped 。
services.AddScoped<IDataStore, DataStore>();
直至现在的 EF Core 2.0,我们仍然没有默认的 Seed 方法。所以,我们使用下面的方式准备一些数据。
public class ApplicationDatabaseInitializer
{
public async Task SeedAsync(IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
var applicationDbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>(); await applicationDbContext.Database.EnsureDeletedAsync();
await applicationDbContext.Database.MigrateAsync();
await applicationDbContext.Database.EnsureCreatedAsync(); var items = new List<Item>
{
new Item { Barcode= "123", Title="Headphone", SellingPrice=50},
new Item { Barcode= "456", Title="Keyboard", SellingPrice= 40},
new Item { Barcode= "789", Title="Monitor", SellingPrice= 100}
}; await applicationDbContext.Items.AddRangeAsync(items); await applicationDbContext.SaveChangesAsync();
}
}
}
一旦我们应用启动,我们期望初始化数据,所以,在 Configure 方法中添加如下代码行。
new ApplicationDatabaseInitializer().SeedAsync(app).GetAwaiter();
一旦进入产品阶段,我们就不再需要这些种子数据。所以,这里并不介意使用 new 来创建对象实例。
其它的调整包括变更服务的生命周期:
services.AddScoped<HelloWorldQuery>();
services.AddScoped<ISchema, HelloWorldSchema>();
此刻,应用可以运行但是不能注册该架构。要记住 .net core 中间件仅在第一次启动的时候注册。但是在中间件中使用 scoped/transient 服务,我们就需要调用 InvokeAsync() 方法了。
public async Task InvokeAsync(HttpContext httpContext, ISchema schema)
{
....
....
}
最后,我希望添加一个新的集合类型的字段,以用于 Items,该字段的类型应该是 ItemType 的 ListGraphType 。
Field<ListGraphType<ItemType>>(
"items",
resolve: context =>
{
return dataStore.GetItems();
}
);
运行应用,查询 items,你将收到如下的结果。

GraphQL Part VI: 使用 Postgres 和 EF Core 持久化数据的更多相关文章
- 【asp.net core 系列】8 实战之 利用 EF Core 完成数据操作层的实现
0. 前言 通过前两篇,我们创建了一个项目,并规定了一个基本的数据层访问接口.这一篇,我们将以EF Core为例演示一下数据层访问接口如何实现,以及实现中需要注意的地方. 1. 添加EF Core 先 ...
- 深入理解 EF Core:EF Core 读取数据时发生了什么?
阅读本文大概需要 11 分钟. 原文:https://bit.ly/2UMiDLb 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能 ...
- 深入理解 EF Core:EF Core 写入数据时发生了什么?
阅读本文大概需要 14 分钟. 原文:https://bit.ly/2C67m1C 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能 ...
- ASP.NET Core MVC+EF Core从开发到部署
笔记本电脑装了双系统(Windows 10和Ubuntu16.04)快半年了,平时有时间就喜欢切换到Ubuntu系统下耍耍Linux,熟悉熟悉Linux命令.Shell脚本以及Linux下的各种应用的 ...
- EF Core 实现读写分离的最佳方案
前言 公司之前使用Ado.net和Dapper进行数据访问层的操作, 进行读写分离也比较简单, 只要使用对应的数据库连接字符串即可. 而最近要迁移到新系统中,新系统使用.net core和EF Cor ...
- C# 数据操作系列 - 8. EF Core的增删改查
0.前言 到目前为止,我们看了一下如何声明EF Core的初步使用,也整体的看了下EF Core的映射关系配置以及导航属性的配置. 这一篇,我带大家分享一下,我在工作中需要的EF Core的用法. 1 ...
- 深入理解 EF Core:使用查询过滤器实现数据软删除
原文:https://bit.ly/2Cy3J5f 作者:Jon P Smith 翻译:王亮 声明:我翻译技术文章不是逐句翻译的,而是根据我自己的理解来表述的.其中可能会去除一些本人实在不知道如何组织 ...
- 万字长文,带你彻底理解EF Core5的运行机制,让你成为团队中的EF Core专家
在EF Core 5中,有很多方式可以窥察工作流程中发生的事情,并与该信息进行交互.这些功能点包括日志记录,拦截,事件处理程序和一些超酷的最新出现的调试功能.EF团队甚至从Entity Framewo ...
- EntityFramework 6.x和EntityFramework Core插入数据探讨
前言 一直以来对EF和EF Core都是单独分开来写,从未以比较的形式来讲解,如果您既用过EF 6.x也用过用EF Core是否有了解过EF和EF Core在插入数据时和返回主键有何异同呢?本篇博客是 ...
- ASP.NET Core中使用GraphQL - 第六章 使用EF Core作为持久化仓储
ASP.NET Core中使用GraphQL ASP.NET Core中使用GraphQL - 第一章 Hello World ASP.NET Core中使用GraphQL - 第二章 中间件 ASP ...
随机推荐
- el-table 宽度自适应bug
和 flex 一起使用的时候会有这个问题.只能自动变宽不能自动变窄. 在 flex-grow:1; 的那一层设置 overflow: auto;
- LeetCode 1349. 参加考试的最大学生数 (状压DP 或 二分图最大独立子集)
给你一个 m * n 的矩阵 seats 表示教室中的座位分布.如果座位是坏的(不可用),就用 '#' 表示:否则,用 '.' 表示. 学生可以看到左侧.右侧.左上.右上这四个方向上紧邻他的学生的答卷 ...
- /proc/pids/io
rchar:代表自进程启动以来所读取的总字符数,包括通过缓存或直接读取的字符.单位是字节(bytes). wchar:代表自进程启动以来所写入的总字符数,包括通过缓存或直接写入的字符.单位是字节(by ...
- git 设置代理和取消代理
1.设置代理 git config --global http.proxy "127.0.0.1:1080" 2.取消代理 git config --global --unset ...
- 精彩回顾|【ACDU 中国行·成都站】数据库主题交流活动成功举办!
2023年10月14日下午,[ACDU 中国行·成都站]在成都市武侯区星宸假日酒店圆满落下帷幕.本次活动由中国数据库联盟(ACDU)联合墨天轮社区主办,围绕「数据库技术及应用」这一主题,六位数据库资深 ...
- 使用Swagger的好处
是一个规范和完整的框架 用于生成.描述.调用和可视化RESTful风格的Web服务 接口的文档在线自动生成 功能测试
- Excel488个函数一览
目录: 常用函数 Web 函数 财务函数 查找和引用函数 多维数据集函数 工程函数 兼容性函数 逻辑函数 日期和时间函数 数据库函数 数学和三角函数 统计函数 文本函数 信息函数 与加载项一起安装的用 ...
- day18-方法
何谓方法? System.out.println() System->类 out->对象 println()->方法 Java方法是语句的集合,它们在一起执行 一个功能. 方法是解决 ...
- 多校A层冲刺NOIP2024模拟赛04
T1.02表示法 竟然有出题人敢出高精度(其实只是一个把string转成01串),开场看出记搜后十分犹豫到底要不要写高精,徘徊很久还是写了个小高精. 码( /* GGrun */ #include&l ...
- 第147篇:微信小程序开发中Promise的使用(aysnc,await)
好家伙, 0.错误描述 今天在开发中犯了一个比较严重的错误 对于Promise的错误使用 场景: 微信小程序中展示搜索条件列表 // API请求工具函数 const apiRequest = (url ...