ASP.NET Core中使用GraphQL - 第八章 在GraphQL中處理一對多關系


ASP.NET Core中使用GraphQL - 目錄


到目前為止我們一直在使用GraphQL操作單個實體。在本篇博文中,我們將使用GraphQL操作實體集合。

這里我們使用的場景是處理一個顧客的所有訂單,顧客和訂單之間的關系是一對多。一個顧客可以有多個訂單,相應的一個訂單只屬於一個顧客。

數據庫修改

下面我們首先創建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配置一下表的主外鍵。

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針對CustomerOrder表的字段配置。

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在解決依賴的時候,只能處理一層, 這里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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM