ASP.NET Core中使用GraphQL - 第六章 使用EF Core作為持久化倉儲
ASP.NET Core中使用GraphQL
- ASP.NET Core中使用GraphQL - 第一章 Hello World
- ASP.NET Core中使用GraphQL - 第二章 中介軟體
- ASP.NET Core中使用GraphQL - 第三章 依賴注入
- ASP.NET Core中使用GraphQL - 第四章 GrahpiQL
- ASP.NET Core中使用GraphQL - 第五章 欄位, 引數, 變數
本篇中我將演示如何配置持久化倉儲,這裡原文中是使用的Postgres
, 這裡我改用了EF Core For SqlServer
。本文的例子需要在上一篇的程式碼基礎上修改。沒有程式碼的同學,可以去
之前我們編寫了一個DataStore
類,裡面硬編碼了一個數據集合,這裡我們希望改用依賴注入的方式進行解耦,所以首先我們需要建立一個抽象介面IDataStore
。
public interface IDataStore
{
IEnumerable<Item> GetItems();
Item GetItemByBarcode(string barcode);
}
由於接下來我們需要使用EF Core
, 所以這裡我們需要新增一個EF Core
的上下文類ApplicationDbContext
public class ApplicationDbContext : DbContext { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } public DbSet<Item> Items { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Item>().ToTable("Items"); modelBuilder.Entity<Item>().HasKey(p => p.Barcode); modelBuilder.Entity<Item>().HasData(new Item { Barcode = "123", Title = "Headphone", SellingPrice = 50 }); modelBuilder.Entity<Item>().HasData(new Item { Barcode = "456", Title = "Keyboard", SellingPrice = 40 }); modelBuilder.Entity<Item>().HasData(new Item { Barcode = "789", Title = "Monitor", SellingPrice = 100 }); base.OnModelCreating(modelBuilder); } }
這裡為了匯入一些初始資料,我們在OnModelCreating
方法中使用HasData
方法添加了3個初始資料。
下面我們修改DataStore
類, DataStore
應該實現IDataStore
介面, 其中的GetItemByBarcode
和GetItems
方法需要改為從資料庫中讀取。
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;
}
}
接下來,我們要在Startup.cs
類中的ConfigureServices
新增Entity Framework配置
services.AddDbContext<ApplicationDbContext>(option =>
{
option.UseSqlServer(Configuration.GetConnectionString("SampleDB"));
});
TIPS: 這裡注意不要忘記建立一個
appsettings.json
, 在其中新增資料庫連線字串
配置完成之後,我們需要使用以下命令新增Migration,並更新資料庫
dotnet ef migrations add Initial
dotnet ef database update
現在針對資料庫的修改都已經完成了。
另外我們還需要修改服務註冊程式碼,將註冊服務的生命週期從單例(Singleton)改為作用域(Scoped), 因為當注入服務的生命週期為單例時,需要處理多執行緒問題和潛在的記憶體洩漏問題。
services.AddScoped<IDataStore, DataStore>();
services.AddScoped<HelloWorldQuery>();
services.AddScoped<ISchema, HelloWorldSchema>();
修改完成後,Startup.cs
最終程式碼如下:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(option =>
{
option.UseSqlServer(Configuration.GetConnectionString("SampleDB"));
});
services.AddSingleton<IDocumentExecuter, DocumentExecuter>();
services.AddSingleton<IDocumentWriter, DocumentWriter>();
services.AddScoped<IDataStore, DataStore>();
services.AddScoped<HelloWorldQuery>();
services.AddScoped<ISchema, HelloWorldSchema>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMiddleware<GraphQLMiddleware>();
}
}
現在我們啟動專案, 程式會丟擲一個錯誤
System.InvalidOperationException: Cannot resolve scoped service 'GraphQL.Types.ISchema' from root provider
這個問題的原因是,中介軟體是單例的,如果在中介軟體的建構函式中使用作用域(Scoped)的依賴注入, 會導致這個問題(具體請參見https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.1)。這裡ISchema
的生命週期是作用域,並且在GraphQLMiddleware
類中是從建構函式注入的,所以這裡我們需要修改GraphQLMiddleware
類,ISchema
需要改從Invoke
方法注入。
中介軟體最終程式碼如下:
public class GraphQLMiddleware
{
private readonly RequestDelegate _next;
private readonly IDocumentWriter _writer;
private readonly IDocumentExecuter _executor;
public GraphQLMiddleware(RequestDelegate next, IDocumentWriter writer, IDocumentExecuter executor)
{
_next = next;
_writer = writer;
_executor = executor;
}
public async Task InvokeAsync(HttpContext httpContext, ISchema schema)
{
if (httpContext.Request.Path.StartsWithSegments("/api/graphql")
&& string.Equals(httpContext.Request.Method,
"POST",
StringComparison.OrdinalIgnoreCase))
{
string body;
using (var streamReader = new StreamReader(httpContext.Request.Body))
{
body = await streamReader.ReadToEndAsync();
var request = JsonConvert.DeserializeObject<GraphQLRequest>(body);
var result = await _executor.ExecuteAsync(doc =>
{
doc.Schema = schema;
doc.Query = request.Query;
doc.Inputs = request.Variables.ToInputs();
}).ConfigureAwait(false);
var json = await _writer.WriteToStringAsync(result);
await httpContext.Response.WriteAsync(json);
}
}
else
{
await _next(httpContext);
}
}
}
修改完成之後,我們重新啟動專案,專案正常啟動成功, GraphiQL
介面出現。
現在我們還是使用上一章的查詢程式碼,查詢二維碼是123的貨物資料。
資料正常從資料庫中讀取成功。下一章我們將講解在ASP.NET Core中如何使用GraphQL新增修改資料。
本文原始碼: https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20VI