0%

ASP.NET Core中使用GraphQL - 目录


到目前为止我们一直在使用GraphQL操作单个实体。在本篇博文中,我们将使用GraphQL操作实体集合。

这里我们使用的场景是处理一个顾客的所有订单,顾客和订单之间的关系是一对多。一个顾客可以有多个订单,相应的一个订单只属于一个顾客。

数据库修改#

下面我们首先创建2个新的类CustomerOrder

Customer
1
2
3
4
5
6
7
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
1
2
3
4
5
6
7
8
9
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配置一下表的主外键。

1
2
3
4
5
6
7
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);

最后我们使用如下命令创建迁移并更新数据库

1
2
dotnet ef migrations add OneToManyRelationship  
dotnet ef database update

至此数据库修改完成。

添加GraphQL代码#

下面我们需要添加GraphQL针对CustomerOrder表的字段配置。

OrderType
1
2
3
4
5
6
7
8
9
10
11
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
1
2
3
4
5
6
7
8
9
10
11
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
1
2
3
4
5
6
7
8
9
10
11
12
13
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
1
2
3
4
5
6
7
8
9
10
11
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
1
2
3
4
5
6
7
8
9
public class OrderInputType : InputObjectGraphType {  
public OrderInputType()
{
Name = "OrderInput";
Field<NonNullGraphType<StringGraphType>>("tag");
Field<NonNullGraphType<DateGraphType>>("createdAt");
Field<NonNullGraphType<IntGraphType>>("customerId");
}
}
CustomerInputType
1
2
3
4
5
6
7
8
public class CustomerInputType : InputObjectGraphType {  
public CustomerInputType()
{
Name = "CustomerInput";
Field<NonNullGraphType<StringGraphType>>("name");
Field<NonNullGraphType<StringGraphType>>("billingAddress");
}
}

当前添加以上代码之后,我们还需要在Startup类中注册这几个新类型

1
2
3
4
5
6
7
8
9
public void ConfigureServices(IServiceCollection services)  
{
....
....
services.AddScoped<CustomerType>();
services.AddScoped<CustomerInputType>();
services.AddScoped<OrderType>();
services.AddScoped<OrderInputType>();
}

如果现在启动项目,你会得到以下错误

1
Failed to call Activator.CreateInstance. Type: chapter1.OrderType

这里的问题是在InventorySchema构造函数中的注入没起作用, 原因是GraphQL在解决依赖的时候,只能处理一层, 这里OrderTypeCustomerType是2层的关系。如果想解决这个问题,我们需要在Startup中再注册一个依赖解决器。

1
2
services.AddScoped<IDependencyResolver>(s => 
new FuncDependencyResolver(s.GetRequiredService));

修改完成之后我们还需要修改InventorySchema, 在构造函数中将依赖解决器注入。

1
2
3
4
5
6
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