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
。本文的例子需要在上一篇的代碼基礎上修改。沒有代碼的同學,可以去https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20V下載。
之前我們編寫了一個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