ASP.NET Core中使用GraphQL - 第八章 在GraphQL中处理一对多关系

ASP.NET Core中使用GraphQL - 目录html


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

这里咱们使用的场景是处理一个顾客的全部订单,顾客和订单之间的关系是一对多。一个顾客能够有多个订单,相应的一个订单只属于一个顾客。github

数据库修改

下面咱们首先建立2个新的类CustomerOrder数据库

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配置一下表的主外键。c#

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);

最后咱们使用以下命令建立迁移并更新数据库async

dotnet ef migrations add OneToManyRelationship  
dotnet ef database update

至此数据库修改完成。函数

添加GraphQL代码

下面咱们须要添加GraphQL针对CustomerOrder表的字段配置。ui

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构造函数中添加以下代码:code

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中实现它们。htm

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在解决依赖的时候,只能处理一层, 这里OrderTypeCustomerType是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

相关文章
相关标签/搜索