本篇中我将演示如何配置持久化仓储, 这里原文中是使用的 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 VI
来源: https://www.cnblogs.com/lwqlun/p/9937468.html