0

I am learning BatchDataLoader using Hot Chocolate. Per their guidelines, I am using .AddPooledDbContextFactory. Using Microsoft.Azure.Cosmos nuget package to support cosmos DB.

Now, I am getting null when I'm trying to fetch Cosmos record with valid id. My model(User) and container(graphql) name is different, but I am defining that on OnModelCreating.

  • What am I doing wrong here?
  • Am I not using DataLoader correctly?

Reference(s):

Program.cs:

using GraphQlHotChocolate.Data;
using GraphQlHotChocolate.DataLoader;
using GraphQlHotChocolate.Queries;
using Microsoft.EntityFrameworkCore;
using HotChocolate.AspNetCore;

public class Startup
{
    private readonly IConfiguration _configuration;

    public Startup(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public void ConfigureServices(IServiceCollection services)
    {
        services
            .AddCors(
                o => o.AddDefaultPolicy(b => b.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())
            )
            // First we add the DBContext which we will be using to interact with our Database.
            .AddPooledDbContextFactory<ApplicationDbContext>(
                options =>
                    options.UseCosmos(
                        _configuration["Cosmos:ConnectionString"],
                        _configuration["Cosmos:DbName"]
                    )
            )
            // This adds the GraphQL server core service and declares a schema.
            .AddGraphQLServer()
            
            // Next we add the types to our schema.
            .AddQueryType()
            
            .AddTypeExtension<UserQueries>()
            .AddDataLoader<UserByIdDataLoader>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseCors();
        app.UseWebSockets();
        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            // We will be using the new routing API to host our GraphQL middleware.
            endpoints.MapGraphQL()
                .WithOptions(new GraphQLServerOptions
                {
                    Tool =
                    {
                        GaTrackingId = "G-2Y04SFDV8F"
                    }
                });

            endpoints.MapGet("/", context =>
            {
                context.Response.Redirect("/graphql", true);
                return Task.CompletedTask;
            });
        });
    }
}

User.cs:

using System.ComponentModel.DataAnnotations;

namespace GraphQlHotChocolate.Data;
// I am seeing same issue even though I rename it "GraphQl" (similar to my cosmos container name)
public class User 
{
    public string id { get; set; }
    [Required]
    public string userId { get; set; } = Guid.NewGuid().ToString();
    public bool isActive { get; set; }
    public Contact contact { get; set; }
}

public class Contact
{
    public string firstName { get; set; }
    public string lastName { get; set; }
    public string email { get; set; }
    public string phoneNumber { get; set; }
}

UserQuery.cs:

using GraphQlHotChocolate.Data;
using GraphQlHotChocolate.DataLoader;

namespace GraphQlHotChocolate.Queries;

[ExtendObjectType(OperationTypeNames.Query)]
public class UserQueries
{
    /// <summary>
    /// Gets all Users of this conference.
    /// </summary>
    [UseApplicationDbContext]
    [UsePaging]
    public IQueryable<User> GetUsers(
        [ScopedService] ApplicationDbContext context)
        => context.Users;

    /// <summary>
    /// Gets an User by its identifier.
    /// </summary>
    /// <param name="id">The User identifier.</param>
    /// <param name="userById"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public Task<User> GetUserByIdAsync(
        [ID(nameof(User))] string userId,
        UserByIdDataLoader userById,
        CancellationToken cancellationToken)
        => userById.LoadAsync(userId, cancellationToken);
}

UserbyIdDataLoader.cs:

using GraphQlHotChocolate.Data;
using Microsoft.EntityFrameworkCore;
using User = GraphQlHotChocolate.Data.User;

namespace GraphQlHotChocolate.DataLoader;

public class UserByIdDataLoader : BatchDataLoader<string, User>
{
    private readonly IDbContextFactory<ApplicationDbContext> _dbContextFactory;

    public UserByIdDataLoader(
        IDbContextFactory<ApplicationDbContext> dbContextFactory,
        IBatchScheduler batchScheduler,
        DataLoaderOptions options
    )
        : base(batchScheduler, options)
    {
        _dbContextFactory =
            dbContextFactory ?? throw new ArgumentNullException(nameof(dbContextFactory));
    }

    protected override async Task<IReadOnlyDictionary<string, User>> LoadBatchAsync(
        IReadOnlyList<string> keys,
        CancellationToken cancellationToken
    )
    {
        await using var dbContext = await _dbContextFactory.CreateDbContextAsync(cancellationToken);

        var result = await dbContext.Users
            .Where(s => keys.Contains(s.userId))
            .ToDictionaryAsync(t => t.userId, cancellationToken);
        // This is coming null
        return result;
       
    }
}

ApplicationDbContext.cs:

using Microsoft.EntityFrameworkCore;

namespace GraphQlHotChocolate.Data;

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

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder
            .Entity<User>()
            .ToContainer("graphql") // Here, I am defining container name.
            .HasPartitionKey(a => a.userId);
    }

    public DbSet<User> Users { get; set; } = default!;
}
GThree
  • 2,708
  • 7
  • 34
  • 67

0 Answers0