《ASP.NET Core 微服务实战》-- 读书笔记(第5章)
第 5 章 創(chuàng)建數(shù)據(jù)服務(wù)
選擇一種數(shù)據(jù)存儲
由于我堅持要盡可能的跨平臺,所以我決定選用 Postgres,而不用 SQL Server 以照顧 Linux 或 Mac 電腦的讀者
構(gòu)建 Postgres 倉儲
在本節(jié),我們要升級位置服務(wù)讓它使用 Postgres
為了完成這一過程,需要創(chuàng)建一個新的倉儲實現(xiàn),以封裝 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); }接下來要做的就是創(chuàng)建一個數(shù)據(jù)庫上下文
數(shù)據(jù)庫上下文的使用方式是創(chuàng)建與特定模型相關(guān)的類型,并從數(shù)據(jù)庫上下文繼承
由于與位置數(shù)據(jù)打交道,所以要創(chuàng)建一個 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;}} }實現(xiàn)位置記錄倉儲接口
// 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();}} }為了實現(xiàn)以注入的方式獲取 Postgres 數(shù)據(jù)庫上下文,需要在 Startup 類的 ConfigureServices 方法里把倉儲添加到依賴注入系統(tǒng)
public void ConfigureServices(IServiceCollection services) {services.AddEntityFrameworkNpgsql().AddDbContext<LocationDbContext>(options =>options.UseNpgsql(Configuration));services.AddScoped<ILocationRecordRepository, LocationRecordRepository>();services.AddMvc(); }數(shù)據(jù)庫是一種后端服務(wù)
在本例中,我們準(zhǔn)備用環(huán)境變量來覆蓋由配置文件提供的默認(rèn)配置
appsettings.json
{"transient": false,"postgres": {"cstr": "Host=localhost;Port=5432;Database=locationservice;Username=integrator;Password=inteword"} }前面實現(xiàn)的倉儲需要一種數(shù)據(jù)庫上下文才能運作,為了給位置模型創(chuàng)建數(shù)據(jù)庫上下文,只需要創(chuàng)建一個類,并從 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);}} }創(chuàng)建了新的數(shù)據(jù)庫上下文后,需要讓它在依賴注入中可用,這樣位置倉儲才能使用它
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 兩個方法的調(diào)用
對真實倉儲進(jìn)行集成測試
我們想要利用自動的構(gòu)建流水線,每次運行構(gòu)建時都啟動一個新的、空白的 Postgres 實例
然后,讓集成測試在這個新實例上運行,執(zhí)行遷移以配置數(shù)據(jù)庫結(jié)構(gòu)
每次提交代碼時,整個過程既要能在本地、團(tuán)隊成員的機器上運行,又要能在云上自動運行
這就是我喜歡搭配使用 Wercker 和 Docker 的原因
試運行數(shù)據(jù)服務(wù)
使用特定參數(shù)啟動 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數(shù)據(jù)庫啟動后,還需要表結(jié)構(gòu),順便設(shè)置了很快會用到的環(huán)境變量
$ exprot TRANSIENT=false $ export POSTGRES__CSTR=“Host=localhost;Username=integrator; \ Password=inteword;Database=locationservice;Port=5432" $ dotnet ef database update我們期望位置服務(wù)能夠訪問到自己的容器之外,并進(jìn)入 Postgres 容器之內(nèi)
容器鏈接能夠?qū)崿F(xiàn)這項能力,不過需要在啟動 Docker 鏡像之前就完成環(huán)境變量的修改
$ 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 容器后,位置服務(wù)就應(yīng)該能夠正確連接到數(shù)據(jù)庫了
為親自驗證結(jié)果,可以提交一個位置記錄
$ 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通過服務(wù)查詢我們虛構(gòu)的團(tuán)隊成員歷史位置
$ curl http://localhost:5000/locations/63e7acf8-8fae-42ec-9349-3c8593ac8292為了再次確認(rèn),查詢 latest 端點并確保仍能獲取到期望的輸出
$ curl http://localhost:5000/locations/63e7acf8-8fae-42ec-9349-3c8593ac8292/latest最后,為了證實確實在使用真實的數(shù)據(jù)庫實例,可以使用 docker ps 以及 docker kill 找到位置服務(wù)所在的 Docker 進(jìn)程并終止它
然后通過之前用過的命令重新啟動服務(wù)
總結(jié)
以上是生活随笔為你收集整理的《ASP.NET Core 微服务实战》-- 读书笔记(第5章)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: SqlServer 利用游标批量更新数据
- 下一篇: 腾飞答不忘初心的三个问题
