6

GraphQL with .NET Core (Part - IX: Many-Many Entity Relations)

 3 years ago
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.
neoserver,ios ssh client
August 25, 2020

GraphQL with .NET Core (Part - IX: Many-Many Entity Relations)

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.cs

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; }
}
Order.cs
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; }
}
Item.cs

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");
    }
}
OrderItemInputType.cs

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);
    });
GameStoreMutation.cs

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);
        });
    }
}
OrderItemType.cs

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;
}
Repository.cs

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();
    });
GameStoreQuery.cs

Repository code for GetOrderItem is as following,

public async Task<IReadOnlyCollection<OrderItem>> GetOrderItem()
{
    return await _applicationDbContext.OrderItem.AsNoTracking().ToListAsync();
}
Repository.cs

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>();
Startup.cs

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,

1.png2.png

Repository Link

Part-IX

6568968?s=400&v=4

Important Links

Configuring Many To Many Relationships in Entity Framework Core

Share: Twitter Facebook LinkedIn

Comments


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK