Search code examples
c#asp.net-coredependency-injectionconsole-application.net-8.0

Dependency Injection works in API, but failing in a .NET 8 console application


I am trying to utilize a library written to add Sharepoint functionality. Here is how the SharepointClient class is constructed in the library.

public class SharePointClient : ISharePointClient
{
    private readonly SafeILogger<SharePointClient> _logger;
    private readonly SharePointClientConfig _clientConfig;
    private readonly GraphServiceClient _graphClient;

    public SharePointClient(SafeILogger<SharePointClient> logger, 
    IOptions<SharePointClientConfig> clientConfig)
    {
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _clientConfig = clientConfig.Value ?? throw new 
         ArgumentNullException(nameof(clientConfig));

        _graphClient = GetAuthenticatedGraphClient();
    }
}

The method to add the utility library looks like this

public static class ServiceCollectionExtensions
{
    public static IServiceCollection AddSharePointUtils(this IServiceCollection services)
    {
        services.AddOptions<SharePointClientConfig>()
            .BindConfiguration("SharePointClientConfig")
            .ValidateDataAnnotations()
            .ValidateOnStart();

        services.AddScoped<ISharePointClient, SharePointClient>();

        return services;
    }
}

I have a FileUploader class that takes the SharepointClient class in the constructor which I need to instantiate through dependency injection.

public class FileUploader : IFileUploader
{        
     private readonly ISharePointClient _sharePointClient;

     public FileUploader(ISharePointClient sharePointClient)
     {
         _sharePointClient = sharePointClient ?? throw new ArgumentNullException(nameof(sharePointClient));
     }

     public async Task UploadFile(string fileSourceFilePath, string fileName)
     {
         await _sharePointClient.UploadFileToDrive("TestPath", "Test",
                                                   fileSourceFilePath, fileName);
     }
}

Now I have used the utility in a .NET 8 API project and the DI works perfectly.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSafeILogger();
builder.Services.AddSharePointUtils();
builder.Services.AddScoped<IFileUploader, FileUploader>();

But when I am trying to implement it in a .NET 8 console app, I get this error:

var services = new ServiceCollection();
services.AddSafeILogger();
services.AddScoped<IFileUploader, FileUploader>();
services.AddSharePointUtils();
var serviceProvider = services.BuildServiceProvider();
var fileUploader = serviceProvider.GetRequiredService<IFileUploader>();

'Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger'1[Common.SharePoint.Utils.SharePointClient]' while attempting to activate 'Common.Api.Logging.SafeILogger'1[Common.SharePoint.Utils.SharePointClient]'.'

The method AddSafeILogger() resolves this dependency in the API project, but it is failing to resolve the dependency in Console application.

Here is the AddSafeILogger:

namespace Common.Api.Logging;

public static class IServiceCollectionExtensions
{
    public static IServiceCollection AddSafeILogger(this IServiceCollection services)
    {
        services.TryAddSingleton(typeof(SafeILogger<>));
        return services;
    }
}

SafeILogger is a class inherited from Microsoft.Extensions.Logging.Ilogger

I am not sure why the DI implementation is working for API application, but in the console app when I am trying to instantiate FileUploader by calling GetRequiredService method, I am getting the mentioned dependency error. DI for other custom classes work fine in the console app.


Solution

  • You can use the same hosting infrastructure in a console application by using the same Generic Host :

    var builder = Host.CreateDefaultBuilder(args);
    
    builder.Services.AddSafeILogger();
    builder.Services.AddSharePointUtils();
    builder.Services.AddScoped<IFileUploader, FileUploader>();
    
    var app=builder.Build()
    
    var client=app.Services.GetRequiredService<ISharePointClient>();
    ...