《ASP.NET Core 微服务实战》-- 读书笔记(第5章)
第 5 章 创建数据服务
选择一种数据存储
由于我坚持要尽可能的跨平台,所以我决定选用 Postgres,而不用 SQL Server 以照顾 Linux 或 Mac 电脑的读者
构建 Postgres 仓储
在本节,我们要升级位置服务让它使用 Postgres
为了完成这一过程,需要创建一个新的仓储实现,以封装 PostgreSQL 的客户端通信
回顾一下位置仓库的接口
public interface ILocationRecordRepository {
LocationRecord Add(LocationRecord locationRecord);
LocationRecord Update(LocationRecord locationRecord);
LocationRecord Get(Guid memberId, Guid recordId);
LocationRecord Delete(Guid memberId, Guid recordId);
LocationRecord GetLatestForMember(Guid memberId);
ICollection<LocationRecord> AllForMember(Guid memberId);
}
接下来要做的就是创建一个数据库上下文
数据库上下文的使用方式是创建与特定模型相关的类型,并从数据库上下文继承
由于与位置数据打交道,所以要创建一个 LocationDbContext 类
using Microsoft.EntityFrameworkCore;
using StatlerWaldorfCorp.LocationService.Models;
using Npgsql.EntityFrameworkCore.PostgreSQL;
namespace StatlerWaldorfCorp.LocationService.Persistence
{
public class LocationDbContext : DbContext
{
public LocationDbContext(DbContextOptions<LocationDbContext> options) :base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasPostgresExtension("uuid-ossp");
}
public DbSet<LocationRecord> LocationRecords {get; set;}
}
}
实现位置记录仓储接口
// using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Collections.Generic;
using StatlerWaldorfCorp.LocationService.Models;
using Microsoft.EntityFrameworkCore;
namespace StatlerWaldorfCorp.LocationService.Persistence
{
public class LocationRecordRepository : ILocationRecordRepository
{
private LocationDbContext context;
public LocationRecordRepository(LocationDbContext context)
{
this.context = context;
}
public LocationRecord Add(LocationRecord locationRecord)
{
this.context.Add(locationRecord);
this.context.SaveChanges();
return locationRecord;
}
public LocationRecord Update(LocationRecord locationRecord)
{
this.context.Entry(locationRecord).State = EntityState.Modified;
this.context.SaveChanges();
return locationRecord;
}
public LocationRecord Get(Guid memberId, Guid recordId)
{
return this.context.LocationRecords.FirstOrDefault(lr => lr.MemberID == memberId && lr.ID == recordId);
}
public LocationRecord Delete(Guid memberId, Guid recordId)
{
LocationRecord locationRecord = this.Get(memberId, recordId);
this.context.Remove(locationRecord);
this.context.SaveChanges();
return locationRecord;
}
public LocationRecord GetLatestForMember(Guid memberId)
{
LocationRecord locationRecord = this.context.LocationRecords.
Where(lr => lr.MemberID == memberId).
OrderBy(lr => lr.Timestamp).
Last();
return locationRecord;
}
public ICollection<LocationRecord> AllForMember(Guid memberId)
{
return this.context.LocationRecords.
Where(lr => lr.MemberID == memberId).
OrderBy(lr => lr.Timestamp).
ToList();
}
}
}
为了实现以注入的方式获取 Postgres 数据库上下文,需要在 Startup 类的 ConfigureServices 方法里把仓储添加到依赖注入系统
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkNpgsql().AddDbContext<LocationDbContext>(options =>
options.UseNpgsql(Configuration));
services.AddScoped<ILocationRecordRepository, LocationRecordRepository>();
services.AddMvc();
}
数据库是一种后端服务
在本例中,我们准备用环境变量来覆盖由配置文件提供的默认配置
appsettings.json
{
"transient": false,
"postgres": {
"cstr": "Host=localhost;Port=5432;Database=locationservice;Username=integrator;Password=inteword"
}
}
前面实现的仓储需要一种数据库上下文才能运作,为了给位置模型创建数据库上下文,只需要创建一个类,并从 DbContext 继承
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using StatlerWaldorfCorp.LocationService.Models;
using Npgsql.EntityFrameworkCore.PostgreSQL;
namespace StatlerWaldorfCorp.LocationService.Persistence
{
public class LocationDbContext : DbContext
{
public LocationDbContext(DbContextOptions<LocationDbContext> options) :base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasPostgresExtension("uuid-ossp");
}
public DbSet<LocationRecord> LocationRecords {get; set;}
}
public class LocationDbContextFactory : IDbContextFactory<LocationDbContext>
{
public LocationDbContext Create(DbContextFactoryOptions options)
{
var optionsBuilder = new DbContextOptionsBuilder<LocationDbContext>();
var connectionString = Startup.Configuration.GetSection("postgres:cstr").Value;
optionsBuilder.UseNpgsql(connectionString);
return new LocationDbContext(optionsBuilder.Options);
}
}
}
创建了新的数据库上下文后,需要让它在依赖注入中可用,这样位置仓储才能使用它
public void ConfigureServices(IServiceCollection services)
{
//var transient = Boolean.Parse(Configuration.GetSection("transient").Value);
var transient = true;
if (Configuration.GetSection("transient") != null) {
transient = Boolean.Parse(Configuration.GetSection("transient").Value);
}
if (transient) {
logger.LogInformation("Using transient location record repository.");
services.AddScoped<ILocationRecordRepository, InMemoryLocationRecordRepository>();
} else {
var connectionString = Configuration.GetSection("postgres:cstr").Value;
services.AddEntityFrameworkNpgsql().AddDbContext<LocationDbContext>(options =>
options.UseNpgsql(connectionString));
logger.LogInformation("Using '{0}' for DB connection string.", connectionString);
services.AddScoped<ILocationRecordRepository, LocationRecordRepository>();
}
services.AddMvc();
}
让这些功能最终生效的奇妙之处在于对 AddEntityFrameworkNpgsql 以及 AddDbContext 两个方法的调用
对真实仓储进行集成测试
我们想要利用自动的构建流水线,每次运行构建时都启动一个新的、空白的 Postgres 实例
然后,让集成测试在这个新实例上运行,执行迁移以配置数据库结构
每次提交代码时,整个过程既要能在本地、团队成员的机器上运行,又要能在云上自动运行
这就是我喜欢搭配使用 Wercker 和 Docker 的原因
试运行数据服务
使用特定参数启动 Postgres
$ docker run -p 5432:5432 --name some-postgres \
-e POSTGRES_PASSWORD=inteword -e POSTGRES_USER=integrator \
-e POSTGRES_DB=locationservice -d postgres
这样就以 some-postgres 为名称启动一个 Postgres 的 Docker 镜像
为验证能够成功连接到 Postgres,可运行下面的 Docker 命令来启动 psql
$ docker run -it --rm --link some-postgres:postgres postgres \
psql -h postgres -U integrator -d locationservice
数据库启动后,还需要表结构,顺便设置了很快会用到的环境变量
$ exprot TRANSIENT=false
$ export POSTGRES__CSTR=“Host=localhost;Username=integrator; \
Password=inteword;Database=locationservice;Port=5432"
$ dotnet ef database update
我们期望位置服务能够访问到自己的容器之外,并进入 Postgres 容器之内
容器链接能够实现这项能力,不过需要在启动 Docker 镜像之前就完成环境变量的修改
$ export POSTGRES__CSTR=“Host=localhost;Username=integrator; \
Password=inteword;Database=locationservice;Port=5432"
$ docker run -p 5000:5000 --link some-postgres:psotgres \
-e TRANSIENT=false -e PORT=5000 \
-e POSTGRES__CSTR dotnetcoreservices/locationservice:latest
使用 psotgres 作为主机名链接 Postgres 容器后,位置服务就应该能够正确连接到数据库了
为亲自验证结果,可以提交一个位置记录
$ curl -H "Content-Type:application/json" -X POST -d \
'{"id":"64c3e69f-1580-4b2f-a9ff-2c5f3b8f0elf","latitude":12.0, \
"longitude":10.0,"altitude":5.0,"timestamp":0, \
"memberId":"63e7acf8-8fae-42ec-9349-3c8593ac8292"}' \
http://localhost:5000/locations/63e7acf8-8fae-42ec-9349-3c8593ac8292
通过服务查询我们虚构的团队成员历史位置
$ curl http://localhost:5000/locations/63e7acf8-8fae-42ec-9349-3c8593ac8292
为了再次确认,查询 latest 端点并确保仍能获取到期望的输出
$ curl http://localhost:5000/locations/63e7acf8-8fae-42ec-9349-3c8593ac8292/latest
最后,为了证实确实在使用真实的数据库实例,可以使用 docker ps 以及 docker kill 找到位置服务所在的 Docker 进程并终止它
然后通过之前用过的命令重新启动服务


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。
《ASP.NET Core 微服务实战》-- 读书笔记(第5章)的更多相关文章
- JavaScript DOM编程艺术读书笔记(三)
第七章 动态创建标记 在web浏览器中往文档添加标记,先回顾下过去使用的技术: <body> <script type="text/javascript"> ...
- JavaScript DOM编程艺术读书笔记(二)
第五章 最佳实践 平稳退化(graceful degradation):如果正确使用了JavaScript脚本,可以让访问者在他们的浏览器不支持JavaScript的情况下仍能顺利地浏览你网站.虽然某 ...
- JavaScript DOM编程艺术读书笔记(一)
第一章,第二章 DOM:是一套对文档的内容进行抽象和概念化的方法. W3C中的定义:一个与系统平台和编程语言无关的接口,程序和脚本可以通过这个接口动态的访问和修改文档的内容,结构和样式. DHTML( ...
- JavaScript DOM编程艺术 - 读书笔记1-3章
1.JavaScript语法 准备工作 一个普通的文本编辑器,一个Web浏览器. JavaScript代码必须通过Html文档才能执行,第一种方式是将JavaScript代码放到文档<head& ...
- JavaScript DOM编程艺术 读书笔记
2. JavaScript语法 2.1 注释 HTML允许使用"<!--"注释跨越多个行,但JavaScript要求这种注释的每行都必须在开头加上"< ...
- JavaScript DOM编程艺术读书笔记(四)
第十章 实现动画效果 var repeat = "moveElement('"+elementID+"',"+final_x+","+fin ...
- JavaScript DOM编程艺术-学习笔记(第二章)
1.好习惯从末尾加分号:开始 2.js区分大小写 3.程序界万能的命名法则:①不以,数字开头的数字.字母.下划线.美元符号 ②提倡以下划线命名法来命名变量,以驼峰命名法来命名函数.但是到了公司往往会身 ...
- 《javascript dom编程艺术》笔记(一)——优雅降级、向后兼容、多个函数绑定onload函数
刚刚开始自学前端,如果不对请指正:欢迎各位技术大牛指点. 开始学习<javascript dom编程艺术>,整理一下学习到的知识.今天刚刚看到第六章,记下get到的几个知识点. 优雅降级 ...
- JavaScript DOM编程艺术学习笔记(一)
嗯,经过了一周的时间,今天终于将<JavaScript DOM编程艺术(第2版)>这本书看完了,感觉受益匪浅,我和作者及出版社等等都不认识,无意为他们做广告,不过本书确实值得一看,也值得推 ...
- JavaScript DOM编程艺术-学习笔记
发现基础不是很好,补习一下.37买了2本书(dom编程和高级程序设计). 以前读书总是自己勾勾画画,有点没意思.现在写下来,说不定会成为传世经典.哈哈...........随便扯扯淡. 第一天(201 ...
随机推荐
- 《深入理解计算机系统》(CSAPP)读书笔记 —— 第五章 优化程序性能
写程序最主要的目标就是使它在所有可能的情况下都正确工作.一个运行得很快但是给出错误结果的程序没有任何用处.程序员必须写出清晰简洁的代码,这样做不仅是为了自己能够看懂代码,也是为了在检査代码和今后需要修 ...
- freeswitch的mod_xml_curl模块
概述 freeswitch是一款简单好用的VOIP开源软交换平台. 随着fs服务的增多,每一台fs都需要在后台单独配置,耗时耗力,心力憔悴. 如果有一个集中管理配置的配置中心,统一管理所有fs的配置, ...
- freeswitch如何判断挂机方
概述 freeswitch作为VOIP的软交换平台,需要对呼叫的信息做判断和归类. 常见的呼叫信息中,挂机方向的信息对于话单统计有很大的用处. 但是fs的原始话单和日志中并没有挂机方向的信息. 环境 ...
- notepad++的使用技巧
一.多标签管理 1.可支持多个标签的展示
- 机器学习-决策树系列-XGBoost算法-chentianqi大神-集成学习-31
目录
- [转帖]是什么让 Redis“气急败坏”回击:13 年来,总有人想替 Redis 换套新架构
https://www.infoq.cn/article/AlF5NIhHdskayl0MTyQG 回击就代表输了?! 今年年中,一位前谷歌.前亚马逊的工程师推出了他创作的开源内存数据缓存系统 Dra ...
- [转帖]tidb Modify Configuration Dynamically
https://docs.pingcap.com/tidb/v6.5/dynamic-config This document describes how to dynamically modify ...
- [转帖]解决jmeter请求响应结果乱码的问题
如下图所示,请求百度接口的时候,发现返回的信息里面中文是乱码 这个时候我们只需要改一下jmeter里的配置文件,设置响应结果的字符编码为UTF-8就行了. 进入jmeter安装目录/bin中,找到jm ...
- [转帖]【P1】Jmeter 准备工作
文章目录 一.Jmeter 介绍 1.1.Jmeter 有什么样功能 1.2.Jmeter 与 LoadRunner 比较 1.3.常用性能测试工具 1.4.性能测试工具如何选型 1.5.学习 Jme ...
- [转帖]安全技术和iptables防火墙
目录 安全技术 Netfilter 防火墙工具介绍 iptables firewalld nftables iptables的组成概述 netfilter与iptables关系 iptables的四表 ...