Code samples used in this blog series have been updated to latest version of .NET Core (5.0.4) and GraphQL-Dotnet (4.2.0). Follow this link to get the updated samples.
In a real-world scenario, only a limited quantity of a particular item belongs to a particular order. Imagine, you have an order cart containing references to some selected items with their respective ordered quantities. The end result is you got a relationship something like: an order can have many items whereas an item can be part of multiple orders.
By EF Core conventions, in a many-to-many relation, you have two standalone entities and a third entity between them which represents a relationship bridge. In our case, the bridging entity will be the OrderItem
public class OrderItem
{
public int Id { get; set; }
public int ItemId { get; set; }
public Item Item { get; set; }
public int Quantity { get; set; }
public int OrderId { get; set; }
public Order Order { get; set; }
}Here we have two reference navigation properties for each side of the relation i.e. Order and Item. Also, we have two foreign key properties i.e. ItemId and OrderId.
For a fully defined relationship, we also have individual collection navigation property of OrderItem on each side of the standalone entities,
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; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}public class Item
{
public int Id { get; set; }
public string Tag { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}We are using an In-Memory database so no migration script is needed for each iterative changes on the database. But, once you have configured all the necessary relationships, create a migration and update your development/production database using dotnet CLI with the following commands,
dotnet ef migrations add ManyToManyRelationship
dotnet ef database update We can add a GraphQL end-point for adding an item to a particular order. To do that we need an InputGraphTypeType for OrderItem.
public class OrderItemInputType : InputObjectGraphType
{
public OrderItemInputType()
{
Name = "OrderItemInput";
Field<NonNullGraphType<IntGraphType>>("quantity");
Field<NonNullGraphType<IntGraphType>>("itemId");
Field<NonNullGraphType<IntGraphType>>("orderId");
}
}As for the end-point, we registered a new mutation field inside GameStoreMutation.cs. The field is simply named addOrderItem,
FieldAsync<OrderItemType>(
"addOrderItem",
arguments: new QueryArguments(
new QueryArgument<NonNullGraphType<OrderItemInputType>> { Name = "orderItem" }
),
resolve: async ctx =>
{
var orderItem = ctx.GetArgument<OrderItem>("orderItem");
return await repository.AddOrderItem(orderItem);
});Newly added OrderItemType is as following,
public class OrderItemType : ObjectGraphType<OrderItem>
{
public OrderItemType(IRepository repository)
{
Field(i => i.ItemId);
FieldAsync<ItemType, Item>("item", resolve: ctx =>
{
return repository.GetItemById(ctx.Source.ItemId);
});
Field(i => i.Quantity);
Field(i => i.OrderId);
FieldAsync<OrderType, Order>("order", resolve: ctx =>
{
return repository.GetOrderById(ctx.Source.OrderId);
});
}
}Newly registered methods from Repository.cs are as following,
public async Task<Item> GetItemById(int itemId)
{
return await _applicationDbContext.Items.FindAsync(itemId);
}
public async Task<Order> GetOrderById(int orderId)
{
return await _applicationDbContext.Orders.FindAsync(orderId);
}
public async Task<OrderItem> AddOrderItem(OrderItem orderItem)
{
var addedOrderItem = await _applicationDbContext.OrderItem.AddAsync(orderItem);
await _applicationDbContext.SaveChangesAsync();
return addedOrderItem.Entity;
}I've also threw in an additional field for querying a list of all the OrderItem at once,
FieldAsync<ListGraphType<OrderItemType>, IReadOnlyCollection<OrderItem>>(
"orderItem",
resolve: ctx =>
{
return repository.GetOrderItem();
});Repository code for GetOrderItem is as following,
public async Task<IReadOnlyCollection<OrderItem>> GetOrderItem()
{
return await _applicationDbContext.OrderItem.AsNoTracking().ToListAsync();
}Last but not least, don't forget to register the newly added graph types with the DI system. Services registration inside ConfigureServices are as followings,
services.AddTransient<OrderItemType>();
services.AddTransient<OrderItemInputType>();That's all about it. Run the application and try to add an item to a particular order with a mutation like the following illustration,


Repository Link
Important Links
Configuring Many To Many Relationships in Entity Framework Core

Comments