ASP.NET Core中使用GraphQL - 第八章 在GraphQL中處理一對多關系
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 - 第五章 字段, 參數, 變量
- 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
ASP.NET Core中使用GraphQL - 第八章 在GraphQL中處理一對多關系