ASP.NET Core中使用GraphQL - 第六章 使用EF Core做爲持久化倉儲

ASP.NET Core中使用GraphQLhtml


本篇中我將演示如何配置持久化倉儲,這裏原文中是使用的Postgres, 這裏我改用了EF Core For SqlServer。本文的例子須要在上一篇的代碼基礎上修改。沒有代碼的同窗,能夠去https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20V下載。git

以前咱們編寫了一個DataStore類,裏面硬編碼了一個數據集合,這裏咱們但願改用依賴注入的方式進行解耦,因此首先咱們須要建立一個抽象接口IDataStoregithub

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個初始數據。json

下面咱們修改DataStore類, DataStore應該實現IDataStore接口, 其中的GetItemByBarcodeGetItems方法須要改成從數據庫中讀取。c#

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配置api

services.AddDbContext<ApplicationDbContext>(option =>
{
    option.UseSqlServer(Configuration.GetConnectionString("SampleDB"));
});

TIPS: 這裏注意不要忘記建立一個appsettings.json, 在其中添加數據庫鏈接字符串多線程

配置完成以後,咱們須要使用如下命令添加Migration,並更新數據庫app

dotnet ef migrations add Initial
dotnet ef database update

如今針對數據庫的修改都已經完成了。async

另外咱們還須要修改服務註冊代碼,將註冊服務的生命週期從單例(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

相關文章
相關標籤/搜索