GraphQL with .NET Core (Part - IX: Many-Many Entity Relations)
source link: https://www.fiyazhasan.me/graphql-with-net-core-part-ix-many-many-entity-relations/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
GraphQL with .NET Core (Part - IX: Many-Many Entity Relations)
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; }
}
OrderItem.csHere 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; }
}
Order.cspublic 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; }
}
Item.csWe 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");
}
}
OrderItemInputType.csAs 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);
});
GameStoreMutation.csNewly 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);
});
}
}
OrderItemType.csNewly 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;
}
Repository.csI'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();
});
GameStoreQuery.csRepository code for GetOrderItem
is as following,
public async Task<IReadOnlyCollection<OrderItem>> GetOrderItem()
{
return await _applicationDbContext.OrderItem.AsNoTracking().ToListAsync();
}
Repository.csLast 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>();
Startup.csThat'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
Share: Twitter Facebook LinkedIn
Comments
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK