ASP.NET Core中使用GraphQL - 第八章 在GraphQL中處理一對多關係
- ofollow,noindex" target="_blank">ASP.NET Core中使用GraphQL - 第一章 Hello World
- ASP.NET Core中使用GraphQL - 第二章 中介軟體
- ASP.NET Core中使用GraphQL - 第三章 依賴注入
- ASP.NET Core中使用GraphQL - 第四章 GrahpiQL
- ASP.NET Core中使用GraphQL - 第五章 欄位, 引數, 變數
- ASP.NET Core中使用GraphQL - 第六章 使用EF Core作為持久化倉儲
- ASP.NET Core中使用GraphQL - 第七章 Mutation
到目前為止我們一直在使用GraphQL操作單個實體。在本篇博文中,我們將使用GraphQL操作實體集合。
這裡我們使用的場景是處理一個顧客的所有訂單,顧客和訂單之間的關係是一對多。一個顧客可以有多個訂單,相應的一個訂單隻屬於一個顧客。
資料庫修改
下面我們首先建立2個新的類 Customer
和 Order
。
Customer
public class Customer { public int CustomerId { get; set; } public string Name { get; set; } public string BillingAddress { get; set; } public IEnumerable<Order> Orders { get; set; } }
Order
public class Order { public int OrderId { get; set; } public string Tag { get; set; } public DateTime CreatedAt { get; set; } public Customer Customer { get; set; } public int CustomerId { get; set; } }
然後我們修改 ApplicationDbContext
類,在 OnModelCreating
配置一下表的主外來鍵。
modelBuilder.Entity<Customer>() .HasKey(p => p.CustomerId); modelBuilder.Entity<Customer>().HasMany(p => p.Orders) .WithOne() .HasForeignKey(p => p.CustomerId); modelBuilder.Entity<Order>().HasKey(p => p.OrderId);
最後我們使用如下命令建立遷移並更新資料庫
dotnet ef migrations add OneToManyRelationship dotnet ef database update
至此資料庫修改完成。
新增GraphQL程式碼
下面我們需要新增GraphQL針對 Customer
和 Order
表的欄位配置。
OrderType
public class OrderType: ObjectGraphType <Order> { public OrderType(IDataStore dataStore) { Field(o => o.Tag); Field(o => o.CreatedAt); Field <CustomerType, Customer> () .Name("Customer") .ResolveAsync(ctx => { return dataStore.GetCustomerByIdAsync(ctx.Source.CustomerId); }); } }
CustomerType.cs
public class CustomerType: ObjectGraphType <Customer> { public CustomerType(IDataStore dataStore) { Field(c => c.Name); Field(c => c.BillingAddress); Field <ListGraphType<OrderType> , IEnumerable<Order>> () .Name("Orders") .ResolveAsync(ctx => { return dataStore.GetOrdersByCustomerIdAsync(ctx.Source.CustomerId); }); } }
為了查詢所有的顧客和訂單,我們還需要暴露出2個新的節點。所以我們修改在 InventoryQuery
建構函式中新增如下程式碼:
InventoryQuery
Field<ListGraphType<OrderType>, IEnumerable<Order>>() .Name("Orders") .ResolveAsync(ctx => { return dataStore.GetOrdersAsync(); }); Field<ListGraphType<CustomerType>, IEnumerable<Customer>>() .Name("Customers") .ResolveAsync(ctx => { return dataStore.GetCustomersAsync(); });
然後我們需要在 IDataStore
中定義6個新的方法,並在 DataStore
中實現它們。
IDataStore
Task<IEnumerable<Order>> GetOrdersAsync(); Task<IEnumerable<Customer>> GetCustomersAsync(); Task<Customer> GetCustomerByIdAsync(int customerId); Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId); Task<Order> AddOrderAsync(Order order); Task<Customer> AddCustomerAsync(Customer customer);
DataStore
public async Task<IEnumerable<Order>> GetOrdersAsync() { return await _context.Orders .AsNoTracking() .ToListAsync(); } public async Task<IEnumerable<Customer>> GetCustomersAsync() { return await _context.Customers .AsNoTracking() .ToListAsync(); } public async Task<Customer> GetCustomerByIdAsync(int customerId) { return await _context.Customers .FindAsync(customerId); } public async Task<IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId) { return await _context.Orders .Where(o => o.CustomerId == customerId) .ToListAsync(); } public async Task<Order> AddOrderAsync(Order order) { var addedOrder = await _context.Orders.AddAsync(order); await _context.SaveChangesAsync(); return addedOrder.Entity; } public async Task<Customer> AddCustomerAsync(Customer customer) { var addedCustomer = await _context.Customers.AddAsync(customer); await _context.SaveChangesAsync(); return addedCustomer.Entity; }
新增完以上程式碼之後,我們就需要定義新增訂單和顧客的輸入型別了。還記得在上一章中我們如何新增貨物的麼?我們添加了一個 ItemInputType
類,定義了新增貨物需要收集的欄位,所以這裡同理,我們也需要為訂單和顧客定義對應的 InputObjectGraphType
。
OrderInputType
public class OrderInputType : InputObjectGraphType { public OrderInputType() { Name = "OrderInput"; Field<NonNullGraphType<StringGraphType>>("tag"); Field<NonNullGraphType<DateGraphType>>("createdAt"); Field<NonNullGraphType<IntGraphType>>("customerId"); } }
CustomerInputType
public class CustomerInputType : InputObjectGraphType { public CustomerInputType() { Name = "CustomerInput"; Field<NonNullGraphType<StringGraphType>>("name"); Field<NonNullGraphType<StringGraphType>>("billingAddress"); } }
當前新增以上程式碼之後,我們還需要在 Startup
類中註冊這幾個新型別
public void ConfigureServices(IServiceCollection services) { .... .... services.AddScoped<CustomerType>(); services.AddScoped<CustomerInputType>(); services.AddScoped<OrderType>(); services.AddScoped<OrderInputType>(); }
如果現在啟動專案,你會得到以下錯誤
Failed to call Activator.CreateInstance. Type: chapter1.OrderType
這裡的問題是在 InventorySchema
建構函式中的注入沒起作用, 原因是 GraphQL
在解決依賴的時候,只能處理一層, 這裡 OrderType
和 CustomerType
是2層的關係。如果想解決這個問題,我們需要在 Startup
中再註冊一個依賴解決器。
services.AddScoped<IDependencyResolver>(s => new FuncDependencyResolver(s.GetRequiredService));
修改完成之後我們還需要修改 InventorySchema
, 在建構函式中將依賴解決器注入。
public class InventorySchema: Schema { public InventorySchema(IDependencyResolver resolver): base(resolver) { Query = resolver.Resolve<InventoryQuery>(); Mutation = resolver.Resolve<InventoryMutation>(); } }
現在再次啟動專案,程式不報錯了。
最終效果
下面我們首先建立一個 Customer
然後我們繼續建立2個 Order
最後我們來查詢一下剛才建立的資料是否存在
資料讀取正確,這說明我們的資料新增成功了。
本文原始碼: https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20VIII