ASP.NET Core中使用GraphQL - 第八章 在GraphQL中处理一对多关系
ASP.NET Core中使用GraphQL
– [ASP.NET Core中使用GraphQL – 第一章 Hello World](https://www.cnblogs.com/lwqlun/p/9907127.html)
– [ASP.NET Core中使用GraphQL – 第二章 中间件](https://www.cnblogs.com/lwqlun/p/9910953.html)
– [ASP.NET Core中使用GraphQL – 第三章 依赖注入](https://www.cnblogs.com/lwqlun/p/9918006.html)
– [ASP.NET Core中使用GraphQL – 第四章 GrahpiQL](https://www.cnblogs.com/lwqlun/p/9925542.html)
– [ASP.NET Core中使用GraphQL – 第五章 字段, 参数, 变量](https://www.cnblogs.com/lwqlun/p/9926315.html)
– [ASP.NET Core中使用GraphQL – 第六章 使用EF Core作为持久化仓储](https://www.cnblogs.com/lwqlun/p/9937468.html )
– [ASP.NET Core中使用GraphQL – 第七章 Mutation](https://www.cnblogs.com/lwqlun/p/9943372.html)
—
到目前为止我们一直在使用GraphQL操作单个实体。在本篇博文中,我们将使用GraphQL操作实体集合。
这里我们使用的场景是处理一个顾客的所有订单,顾客和订单之间的关系是一对多。一个顾客可以有多个订单,相应的一个订单只属于一个顾客。
### 数据库修改
下面我们首先创建2个新的类Customer
和Order
。
##### Customer
“`c#
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string BillingAddress { get; set; }
public IEnumerable Orders { get; set; }
}
“`
##### Order
“`c#
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
配置一下表的主外键。
“`c#
modelBuilder.Entity()
.HasKey(p => p.CustomerId);
modelBuilder.Entity().HasMany(p => p.Orders)
.WithOne()
.HasForeignKey(p => p.CustomerId);
modelBuilder.Entity().HasKey(p => p.OrderId);
“`
最后我们使用如下命令创建迁移并更新数据库
“`
dotnet ef migrations add OneToManyRelationship
dotnet ef database update
“`
至此数据库修改完成。
### 添加GraphQL代码
下面我们需要添加GraphQL针对Customer
和Order
表的字段配置。
##### OrderType
“`c#
public class OrderType: ObjectGraphType {
public OrderType(IDataStore dataStore) {
Field(o => o.Tag);
Field(o => o.CreatedAt);
Field ()
.Name(“Customer”)
.ResolveAsync(ctx => {
return dataStore.GetCustomerByIdAsync(ctx.Source.CustomerId);
});
}
}
“`
##### CustomerType.cs
“`c#
public class CustomerType: ObjectGraphType {
public CustomerType(IDataStore dataStore) {
Field(c => c.Name);
Field(c => c.BillingAddress);
Field , IEnumerable> ()
.Name(“Orders”)
.ResolveAsync(ctx => {
return dataStore.GetOrdersByCustomerIdAsync(ctx.Source.CustomerId);
});
}
}
“`
为了查询所有的顾客和订单,我们还需要暴露出2个新的节点。所以我们修改在InventoryQuery
构造函数中添加如下代码:
##### InventoryQuery
“`c#
Field, IEnumerable>()
.Name(“Orders”)
.ResolveAsync(ctx =>
{
return dataStore.GetOrdersAsync();
});
Field, IEnumerable>()
.Name(“Customers”)
.ResolveAsync(ctx =>
{
return dataStore.GetCustomersAsync();
});
“`
然后我们需要在IDataStore
中定义6个新的方法,并在DataStore
中实现它们。
##### IDataStore
“`c#
Task> GetOrdersAsync();
Task> GetCustomersAsync();
Task GetCustomerByIdAsync(int customerId);
Task> GetOrdersByCustomerIdAsync(int customerId);
Task AddOrderAsync(Order order);
Task AddCustomerAsync(Customer customer);
“`
##### DataStore
“`c#
public async Task> GetOrdersAsync()
{
return await _context.Orders
.AsNoTracking()
.ToListAsync();
}
public async Task> GetCustomersAsync()
{
return await _context.Customers
.AsNoTracking()
.ToListAsync();
}
public async Task GetCustomerByIdAsync(int customerId)
{
return await _context.Customers
.FindAsync(customerId);
}
public async Task> GetOrdersByCustomerIdAsync(int customerId)
{
return await _context.Orders
.Where(o => o.CustomerId == customerId)
.ToListAsync();
}
public async Task AddOrderAsync(Order order)
{
var addedOrder = await _context.Orders.AddAsync(order);
await _context.SaveChangesAsync();
return addedOrder.Entity;
}
public async Task AddCustomerAsync(Customer customer)
{
var addedCustomer = await _context.Customers.AddAsync(customer);
await _context.SaveChangesAsync();
return addedCustomer.Entity;
}
“`
添加完以上代码之后,我们就需要定义添加订单和顾客的输入类型了。还记得在上一章中我们如何添加货物的么?我们添加了一个ItemInputType
类,定义了添加货物需要收集的字段,所以这里同理,我们也需要为订单和顾客定义对应的InputObjectGraphType
。
##### OrderInputType
“`c#
public class OrderInputType : InputObjectGraphType {
public OrderInputType()
{
Name = “OrderInput”;
Field>(“tag”);
Field>(“createdAt”);
Field>(“customerId”);
}
}
“`
##### CustomerInputType
“`c#
public class CustomerInputType : InputObjectGraphType {
public CustomerInputType()
{
Name = “CustomerInput”;
Field>(“name”);
Field>(“billingAddress”);
}
}
“`
当前添加以上代码之后,我们还需要在Startup
类中注册这几个新类型
“`c#
public void ConfigureServices(IServiceCollection services)
{
….
….
services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
}
“`
如果现在启动项目,你会得到以下错误
“`
Failed to call Activator.CreateInstance. Type: chapter1.OrderType
“`
这里的问题是在InventorySchema
构造函数中的注入没起作用, 原因是GraphQL
在解决依赖的时候,只能处理一层, 这里OrderType
和CustomerType
是2层的关系。如果想解决这个问题,我们需要在Startup
中再注册一个依赖解决器。
“`c#
services.AddScoped(s =>
new FuncDependencyResolver(s.GetRequiredService));
“`
修改完成之后我们还需要修改InventorySchema
, 在构造函数中将依赖解决器注入。
“`c#
public class InventorySchema: Schema {
public InventorySchema(IDependencyResolver resolver): base(resolver) {
Query = resolver.Resolve();
Mutation = resolver.Resolve();
}
}
“`
现在再次启动项目,程序不报错了。
### 最终效果
下面我们首先创建一个Customer

然后我们继续创建2个Order


最后我们来查询一下刚才创建的数据是否存在

数据读取正确,这说明我们的数据添加成功了。
[本文源代码: https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20VIII](https://github.com/lamondlu/GraphQL_Blogs/tree/master/Part%20VIII)