GraphQL with .NET Core (Part - VII: Data Persistence)



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.

This post focuses more on configuring a persistent data storage rather than discussing different aspects of GraphQL. With that being said, let's connect to an in-memory database for fake testing.

In our data access layer, we will have a repository. Since it's a good practice to code against abstraction; we will create an interface first for the repository class i.e. IRepository

public interface IRepository
{
    Task<IReadOnlyCollection<Item>> GetItems();
    Task<Item> GetItemByTag(string tag);
    Task<Item> AddItem(Item item);
}
IRepository.cs

We are already familiar with the GetItemByTag and AddItem methods. The GetItems returns all the items in the inventory. We will add a GraphQL collection field for that later.

The implementation of the IRepository is pretty simple as following,

public class Repository : IRepository
{
    private ApplicationDbContext _applicationDbContext;

    public Repository(ApplicationDbContext applicationDbContext)
    {
        _applicationDbContext = applicationDbContext;
    }

    public Task<Item> GetItemByTag(string tag)
    {
        return _applicationDbContext.Items.FirstAsync(i => i.Tag.Equals(tag));
    }

    public async Task<IReadOnlyCollection<Item>> GetItems()
    {
        return await _applicationDbContext.Items.ToListAsync();
    }

    public async Task<Item> AddItem(Item item)
    {
        var addedEntity = await _applicationDbContext.Items.AddAsync(item);
        return addedEntity.Entity;
    }
}

We are using entity framework core, hence the introduction of ApplicationDbContext. The class extends from the DbContext of entity framework and contains a single DbSet for the Item entity. It will create a table named Items in memory,

public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
    {

    }
    public DbSet<Item> Items { get; set; }
}

DbContextOptions is a way to pass options such as database connection string while configuring ApplicationDbContext inside ConfigureServices method of Startup.cs. In our case, we just have to specify the name of the in-memory database as follow,

public void ConfigureServices(IServiceCollection services)
{
    /* Code removed for brevity */

    services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("InMemoryDb"));
}

For this, we need to install the Microsoft.EntityFrameworkCore package. Install this via Nuget or dotnet-cli from the official package source,

dotnet add package Microsoft.EntityFrameworkCore -s https://api.nuget.org/v3/index.json -v 3.1.4

AddDbContext<ApplicationDbContext>() registers the DbContext with a scoped service lifetime. Difference between singleton and scope lifetime are,

  • A Singleton service instance is created only one time (when the application first starts) and the same instance is shared with other services for every subsequent request.
  • A Scope service instance is created every time a new request comes in. It's like singleton per request.

The UseInMemoryDatabase()  extension comes from a separate package i.e. Microsoft.EntityFrameworkCore.InMemory. Install this as well,

dotnet add package Microsoft.EntityFrameworkCore.InMemory -s https://api.nuget.org/v3/index.json -v 3.1.4

Notice, we mentioned Item as an entity earlier. In order to work with an in-memory database, every entity should have an identity field. The modified Item class with an identity field is as follows,

public class Item 
{
    public int Id { get; set; }
    public string Tag { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
}

Time to register the IRepository with a transient lifetime,

public void ConfigureServices(IServiceCollection services)
{
    /* Code removed for brevity */

    services.AddTransient<IRepository, Repository>();
    services.AddDbContext<ApplicationDbContext>(options => options.UseInMemoryDatabase("InMemoryDb"));
}

One last thing I want to do is to add a new collection field for showing all the items. The type of the field would be a ListGraphType of ItemType,

public class GameStoreQuery : ObjectGraphType
{
    public GameStoreQuery(IRepository repository)
    {
        Field<StringGraphType>(
            name: "name",
            resolve: context => "Steam"
        );

        FieldAsync<ItemType>(
            "item",
            arguments: new QueryArguments(new QueryArgument<NonNullGraphType<StringGraphType>> { Name = "tag" }),
            resolve: async context =>
            {
                var tag = context.GetArgument<string>("tag");
                return await repository.GetItemByTag(tag);
            }
        );

        FieldAsync<ListGraphType<ItemType>, IReadOnlyCollection<Item>>(
            "items",
            resolve: async context =>
            {
                return await repository.GetItems();
            }
        );
    }
}
GameStoreQuery.cs

Notice, I've used FieldAsync instead of Field since we are dealing with Task. The Mutation is also modified to call the AddItem of the repository as follows,

public GameStoreMutation(IRepository repository)
{
    FieldAsync<ItemType>(
        "createItem",
        arguments: new QueryArguments(
            new QueryArgument<NonNullGraphType<ItemInputType>> { Name = "item" }
        ),
        resolve: async context =>
        {
            var item = context.GetArgument<Item>("item");
            return await repository.AddItem(item);
        });
}
GameStoreMutation.cs

Feel free to delete the DataSource.cs class as it is not needed anymore.

Run the application now and you can fake testing your createItem mutation field as follows,

Now try to query the items field and you will see something like the following,

Part-VII

fiyazbinhasan/GraphQLCoreFromScratch
https://fiyazhasan.me/tag/graphql/. Contribute to fiyazbinhasan/GraphQLCoreFromScratch development by creating an account on GitHub.

Don't Share Your Secrets! (.NET CORE Secret Manager Tool)

GraphQL Schema and Type