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!;
}