Search code examples
c#mediatrminimal-apisasp.net-core-7.0

Resolving a DI error with Mediatr and .NET Core 7 Minimal APIs


I am currently getting a DI related error message when I try to run my minimal API app with the following classes. Any help would be greatly appreciated.

Endpoint

public static void DefineEndpoints(IEndpointRouteBuilder app)
{

        app.MapPost(BaseRoute, NewStoredFileTypeAsync)
            .WithTags(Tag);
    
    }
    
    private static async Task<Guid> NewStoredFileTypeAsync(NewStoredFileTypeDto request, IMediator mediator)
    {
        var messageId = new MessageId(Guid.NewGuid());
        var command = new NewStoredFileTypeCommand()
        {
            Id = messageId,
            CorrelationId = new CorrelationId(Guid.Parse(messageId.ToString())),
            CausationId = new CausationId(Guid.Parse(messageId.ToString())),
            CommandDto = request,
        };
    
        var response = await mediator.Send(command);
        return response;
    
    }

Command

public class NewStoredFileTypeCommand : BaseCommand, IRequest\<Guid\>
{
    public NewStoredFileTypeDto CommandDto { get; set; } = default!;
}

Command Handler

public class NewStoredFileTypeCommandHandler : IRequestHandler\<NewStoredFileTypeCommand, Guid\>  
{  
private readonly IMapper \_mapper;  
private readonly IEventSourcingHandler\<StoredFileType, StoredFileTypeId\> \_eventSourcingHandler;

    public NewStoredFileTypeCommandHandler(IMapper mapper,                                                     
        IEventSourcingHandler<StoredFileType, StoredFileTypeId> eventSourcingHandler)                          
    {                                                                                                          
        _mapper = mapper;                                                                                      
        _eventSourcingHandler = eventSourcingHandler;                                                          
    }                                                                                                          
                                                                                                               
    public async Task<Guid> Handle(NewStoredFileTypeCommand request, CancellationToken cancellationToken)      
    {                                                                                                          
        var response = new BaseCommandResponse<StoredFileTypeId>();                                            
                                                                                                               
        var aggregate = new StoredFileType(                                                                    
            new StoredFileTypeId(Guid.NewGuid()),                                                              
            request.CorrelationId,                                                                             
            new CausationId(Guid.Parse(request.Id.ToString())),                                                
            request.CommandDto.Name, request.CommandDto.IsImageFileType,                                       
            _mapper.Map<BootstrapIconCode>(request.CommandDto.BootstrapIconCode)                               
            );                                                                                                 
                                                                                                               
        await _eventSourcingHandler.SaveAsync(aggregate);                                                      
        return Guid.Parse(aggregate.Id.ToString());                                                            
    }                                                                                                          

IMongoDbRepository

public interface IMongoDbRepository<TEntityId, TEntity> : IRepository<TEntityId, TEntity>
{
    
}

MongoDbRepository

public class MongoDbRepository<TObjectId, TEntity> : IMongoDbRepository<string, TEntity>
    where TEntity : IMongoDocument
{
    private readonly IMongoDbSettings _dbSettings;
    private readonly IMongoCollection<TEntity> _collection;
    
    public MongoDbRepository(IMongoDbSettings dbSettings)
    {
        _dbSettings = dbSettings;

        var connectionFactory = new MongoDbConnectionFactory<TEntity>(_dbSettings);
        _collection = connectionFactory.GetCollection();
    }


    public async Task<bool> InsertAsync(TEntity entity)
    {
        await _collection.InsertOneAsync(entity);
        return true;
    }

    public async Task<bool> UpdateAsync(TEntity entity)
    {
        throw new NotImplementedException();
    }

    public async Task<bool> DeleteAsync(TEntity entity)
    {
        throw new NotImplementedException();
    }

    public async Task<IList<TEntity>> SearchForAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return await _collection.Find(predicate).ToListAsync();
    }

    public async Task<IList<TEntity>> GetAllAsync()
    {
        return await _collection.Find(_ => true).ToListAsync();
    }

    public async Task<TEntity> GetByIdAsync(string id)
    {
        throw new NotImplementedException();
    }
}

MongoDb Repository Registration

public static IServiceCollection AddMongoDb(this IServiceCollection services, IConfiguration configuration)
    {
        //Get connection string from appsettings.json and population MongoDbSettings class
        services.Configure<MongoDbSettings>(configuration.GetSection(nameof(MongoDbSettings)));
        services.AddSingleton<IMongoDbSettings>(serviceProvider =>
            serviceProvider.GetRequiredService<IOptions<MongoDbSettings>>().Value);
        
        
        //Add generic repository
        services.AddScoped(typeof(IMongoDbRepository<,>), typeof(MongoDbRepository<,>));

        return services;
    }

Error Message

Unhandled exception. System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: MediatR.IRequestHandler2[mNet.FileServer.Commands.Ui.MinimalApi.Features.StoredFileTypes.NewStoredFileType.NewStoredFileTypeCommand,System.Guid] Lifetime: Transient ImplementationType: mNet.FileServer.Commands.Ui.MinimalApi.Features.StoredFileTypes.NewStoredFileType.NewStoredFileTypeCommandHandler': Implementation type 'mNet.Common.Infrastructure.Persistence.MongoDbImplementation.MongoDbRepository2[mNet.FileServer.Commands.Domain.Aggregates.StoredFileTypes.ValueObjects.StoredFileTypeId,mNet.Common.Infrastructure.Persistence.MongoDbImplementation.MongoDbEventDocument`1[mNet.FileServer.Commands.Domain.Aggregates.StoredFileTypes.ValueObjects.StoredFileTypeId]]' can't be converted to service type 'mNet.Common.Infrastructure.Persistence.Contracts.Repositories.IMongoDbRepository`2[mNet.FileServer.Commands.Domain.Aggregates.StoredFileTypes.ValueObjects.StoredFileTypeId,mNet.Common.Infrastructure.Persistence.MongoDbImplementation.MongoDbEventDocument`1[mNet.FileServer.Commands.Domain.Aggregates.StoredFileTypes.ValueObjects.StoredFileTypeId]]')

All calls to the repositories are via the interfaces which have registered as Scoped.


Solution

  • Your MongoDbRepository is faulty - it does not use the first generic parameter and passes string as TEntityId for IMongoDbRepository:

    public class MongoDbRepository<TObjectId, TEntity> : IMongoDbRepository<string, TEntity>
    

    Change it to:

    public class MongoDbRepository<TObjectId, TEntity> : IMongoDbRepository<TObjectId, TEntity>
    

    Potentially you will need to fix some code also.