Maik
Maik
CC#
Created by Maik on 1/17/2025 in #help
DI Service Usage in Quartz.NET registration
I am using Quartz.NET as my scheduling framework, because Quartz.NET has a built-in support for persistent storage, which solves many problems for me out of the box. The problem is the configuration of the connection string for the database. I got a scoped service which resolves the database connection string, since the appsettings.json only includes the template connection string. The actual user and password comes from a vault during startup. Therefore I have to use the service. As far as I know, Quartz.NET has no overload to utilize the IServiceProvider. Because of that I created this overload method
public static IServiceCollection AddQuartz(
this IServiceCollection services,
Action<IServiceProvider, IServiceCollectionQuartzConfigurator> configure)
{
var serviceProvider = services.BuildServiceProvider();

services.AddQuartz(configurator => configure(serviceProvider, configurator));

return services;
}
public static IServiceCollection AddQuartz(
this IServiceCollection services,
Action<IServiceProvider, IServiceCollectionQuartzConfigurator> configure)
{
var serviceProvider = services.BuildServiceProvider();

services.AddQuartz(configurator => configure(serviceProvider, configurator));

return services;
}
Which allows me to do this
services.AddQuartz((serviceProvider, configuration) =>
{
// Other configuration...

var connectionString = serviceProvider
.GetRequiredService<ISecretConnectionStringProvider>()
.GetDatabaseConnectionString();

configuration.UsePersistentStore(storeOptions =>
{
// Other configuration...

storeOptions.UsePostgres(postgresOptions =>
{
postgresOptions.ConnectionString = connectionString;
});
});
});
services.AddQuartz((serviceProvider, configuration) =>
{
// Other configuration...

var connectionString = serviceProvider
.GetRequiredService<ISecretConnectionStringProvider>()
.GetDatabaseConnectionString();

configuration.UsePersistentStore(storeOptions =>
{
// Other configuration...

storeOptions.UsePostgres(postgresOptions =>
{
postgresOptions.ConnectionString = connectionString;
});
});
});
My question is if there is a better alternative? Somehow I am unhappy with the solution. I dont know why but it feels wrong.
38 replies
CC#
Created by Maik on 6/7/2024 in #help
Resolving correct DbContext depending on ???
Hey everyone, I'm building a modular application with ASP.NET 8 and using the inbox/outbox pattern for communication between modules. Scenario: - Module A receives an integration event. - I want to store this event in the database using a generic IInboxRepository. - Challenge: Each module has its own DbContext for its specific data. How can I design my IInboxRepository to resolve the correct DbContext and save the integration event in the appropriate inbox table for the originating module? Right now I am thinking about something like this, but I don't like it to be honest. I don't know why lol
public interface IDbContext
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;

Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

public interface IInboxRepository<T>
where T : IDbContext
{
Task AddAndSaveAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken = default);
}

public sealed class GenericIntegrationEventHandler<TEvent, TDbContext>(
IInboxRepository<TDbContext> inboxRepository)
: IIntegrationEventHandler<TEvent>
where TEvent : class, IIntegrationEvent
where TDbContext : IDbContext
{
private readonly IInboxRepository<TDbContext> _inboxRepository = inboxRepository;

public async Task Handle(TEvent notification, CancellationToken cancellationToken)
{
await _inboxRepository.AddAndSaveAsync(notification, cancellationToken);
}
}

public sealed class InboxRepository<T>(T dbContext)
: IInboxRepository<T>
where T : IDbContext
{
private readonly T _dbContext = dbContext;

public async Task AddAndSaveAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken = default)
{
// Do stuff here
}
}
public interface IDbContext
{
DbSet<TEntity> Set<TEntity>() where TEntity : class;

Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}

public interface IInboxRepository<T>
where T : IDbContext
{
Task AddAndSaveAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken = default);
}

public sealed class GenericIntegrationEventHandler<TEvent, TDbContext>(
IInboxRepository<TDbContext> inboxRepository)
: IIntegrationEventHandler<TEvent>
where TEvent : class, IIntegrationEvent
where TDbContext : IDbContext
{
private readonly IInboxRepository<TDbContext> _inboxRepository = inboxRepository;

public async Task Handle(TEvent notification, CancellationToken cancellationToken)
{
await _inboxRepository.AddAndSaveAsync(notification, cancellationToken);
}
}

public sealed class InboxRepository<T>(T dbContext)
: IInboxRepository<T>
where T : IDbContext
{
private readonly T _dbContext = dbContext;

public async Task AddAndSaveAsync(IIntegrationEvent integrationEvent, CancellationToken cancellationToken = default)
{
// Do stuff here
}
}
Does anybody know how I could design this better? Or maybe someone has a complete different approach? I highly appreciate any kind of help
3 replies