GraphQL Part VIII: Use one to many query

 

Today, we introduce two new entities to deal with customer orders. Many relationship between the customer and order, a customer can have one or more orders, in turn, an order can only be owned by a customer.

Relationships between entities can be configured in accordance with Engity Framework agreement. If an entity has a second set of attributes of an entity, Entity Framework automatically creates many relationship. This property is called navigation property .

Orders have a Customer entity attribute, as a set of attribute types of navigation.

Customer.cs

public class Customer  
{
    public int CustomerId { get; set; }
    public string Name { get; set; }
    public string BillingAddress { get; set; }
    public IEnumerable<Order> Orders { get; set; }
}

 

In most cases, the definition of a navigation attribute is enough, however, it is recommended that the definition of full relations. A reference to define the foreign key attribute navigation attribute on the second entity.

Order.cs

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

 

Once you have defined all the relationships needed, you can use dotnet CLI to create a migration and then update the database.

dotnet ef migrations add OneToManyRelationship  
dotnet ef database update 

 

We also need to create two new ObjectGraphType.

OrderType.cs

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

 

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

 

In order to expose two new endpoint, you need to register in these two types InventoryQuery.

InventoryQuery.cs

Field<ListGraphType<OrderType>, IEnumerable<Order>>()  
    .Name("Orders")
    .ResolveAsync(ctx =>
    {
        return dataStore.GetOrdersAsync();
    });

Field<ListGraphType<CustomerType>, IEnumerable<Customer>>()  
    .Name("Customers")
    .ResolveAsync(ctx =>
    {
        return dataStore.GetCustomersAsync();
    });

 

In the background of the data warehouse, also we need to provide the appropriate data access methods.

DataStore.cs

public async Task <IEnumerable<Order>> GetOrdersAsync() {  
    return await _applicationDbContext.Orders.AsNoTracking().ToListAsync();
}

public async Task <IEnumerable<Customer>> GetCustomersAsync() {  
    return await _applicationDbContext.Customers.AsNoTracking().ToListAsync();
}

public async Task <Customer> GetCustomerByIdAsync(int customerId) {  
    return await _applicationDbContext.Customers.FindAsync(customerId);
}

public async Task <IEnumerable<Order>> GetOrdersByCustomerIdAsync(int customerId) {  
    return await _applicationDbContext.Orders.Where(o => o.CustomerId == customerId).ToListAsync();
}

 

At the same time, we also added two methods for creating Customer and Order,

public async Task<Order> AddOrderAsync(Order order)  
{
    var addedOrder = await _applicationDbContext.Orders.AddAsync(order);
    await _applicationDbContext.SaveChangesAsync();
    return addedOrder.Entity;
}

public async Task<Customer> AddCustomerAsync(Customer customer)  
{         
    var addedCustomer = await _applicationDbContext.Customers.AddAsync(customer);
    await _applicationDbContext.SaveChangesAsync();
    return addedCustomer.Entity;
}

 

In the previous Blog, we created an InputObjectGraphType for Item creation, with its similar, we also need to create the corresponding InputObjectGraph as Customer and Order.

OrderInputType.cs

public class OrderInputType : InputObjectGraphType {  
    public OrderInputType()
    {
        Name = "OrderInput";
        Field<NonNullGraphType<StringGraphType>>("tag");
        Field<NonNullGraphType<DateGraphType>>("createdAt");
        Field<NonNullGraphType<IntGraphType>>("customerId");
    }
}

 

CustomerInputType.cs

public class CustomerInputType : InputObjectGraphType {  
    public CustomerInputType()
    {
        Name = "CustomerInput";
        Field<NonNullGraphType<StringGraphType>>("name");
        Field<NonNullGraphType<StringGraphType>>("billingAddress");
    }
}

 

Finally, we need to register all new types to the DI system. Startup method follows ConfigureServices registered in.

public void ConfigureServices(IServiceCollection services)  
{ 
....
....
    services.AddScoped<CustomerType>();
    services.AddScoped<CustomerInput>();
    services.AddScoped<OrderType>();
    services.AddScoped<OrderInputType>();
}

 

Now, if you run the application, you will see the following error message:

"No parameterless constructor defined for this object."

Through the investigation graphql-dotnet, I found this problem: Issue

For the current embodiment, Schema constructor injection not work. DI system can get the type once, but again could not get to the children of the object chain. In simple terms, if injected by IDataSource a constructor InventoryQuery in, but you can not inject in other Graph Type constructor; for example CustomerType. However, this is not the behavior we expect, therefore, to use IDependencyResolver, registered in the DI system IDependencyResolver, and to ensure a limited life cycle.

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

 

InventorySchemacs do need to modify the point, modify the code in the constructor IDependencyResolver injection.

InventorySchema.cs

public  class InventorySchema: Schema {  
     public InventorySchema (IDependencyResolver resolver): base (resolver) { 
        query = resolver.Resolve <InventoryQuery> (); 
        Mutation = resolver.Resolve <InventoryMutation> (); 
    } 
}

 

Now, run the application again, and verify that you can access the newly added fields.

 

Guess you like

Origin www.cnblogs.com/haogj/p/9177106.html
Recommended