I'm creating a .net core Azure webjob triggered by a Q-message on Azure Q-storage.
When the webjob is triggered I get the following error.
System.InvalidOperationException: 'Unable to resolve service for type 'xxx.SERVICE.Cloud.ICloudStorageService' while attempting to activate 'xxx.Functions'.'
Any suggestions ?
In program.cs / main I wired up DI :
IServiceCollection services = new ServiceCollection();
//...
services.AddTransient<ICloudStorageService, AzureBlobStorageService>();
And the 'hart' of the webjob
public class Functions
{
private readonly ICloudStorageService _storageService;
public Functions(ICloudStorageService cloudStorageService)
{
_storageService = cloudStorageService;
}
public async Task ProcessQueueMessage(
[QueueTrigger(AzureQueues.NoShowUploads)] BatchJob job
, ILogger logger
)
{
//...
}
}
The AzureBlobStorageService
class implements the ICloudStorageService
interface
public class AzureBlobStorageService : ICloudStorageService
{
//...
}
UPDATE:
class Program
{
static void Main(string[] args)
{
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
IServiceCollection services = new ServiceCollection();
services.Configure<SendGridClientOptions>(options =>
{
services.Configure<AzureQueueOptions>(options =>
{
configuration.GetSection("AzureQueueOptions").Bind(options);
options.StorageConnectionString = configuration.GetConnectionString("Storage");
});
services.Configure<AzureBlobStorageOptions>(options =>
{
configuration.GetSection("AzureBlobStorageOptions").Bind(options);
options.StorageConnectionString = configuration.GetConnectionString("Storage");
});
services.AddTransient<Functions, Functions>();
services.AddTransient<ICloudStorageService, AzureBlobStorageService>();
services.AddTransient<WebJobEntryPoint>();
services.BuildServiceProvider().GetService<WebJobEntryPoint>().Run();
}
}
The webentrypoint class
public class WebJobEntryPoint
{
private readonly MailOptions _mailOptions;
private readonly SendGridClientOptions _sendGridClientOptions;
private readonly AzureBlobStorageOptions _azureBlobStorageOptions;
private readonly AzureQueueOptions _azureQueueOptions;
public WebJobEntryPoint(
IOptionsMonitor<SendGridClientOptions> sendGridClientOptionsMonitor,
IOptionsMonitor<MailOptions> mailOptionsMonitor,
IOptionsMonitor<AzureBlobStorageOptions> azureBlobStorageOptionsMonitor,
IOptionsMonitor<AzureQueueOptions> azureQueueOptionsMonitor)
{
_sendGridClientOptions = sendGridClientOptionsMonitor.CurrentValue;
_mailOptions = mailOptionsMonitor.CurrentValue;
_azureBlobStorageOptions = azureBlobStorageOptionsMonitor.CurrentValue;
_azureQueueOptions = azureQueueOptionsMonitor.CurrentValue;
}
public void Run()
{
var builder = new HostBuilder()
.ConfigureWebJobs(webJobConfiguration =>
{
webJobConfiguration.AddAzureStorageCoreServices();
webJobConfiguration.AddAzureStorage(c => c.BatchSize = _azureQueueOptions.BatchSize);
})
.ConfigureLogging((context, b) =>
{
b.AddConsole();
});
var host = builder.Build();
using (host)
{
host.Run();
}
}
}
With your current design the host is unaware of the service registrations that were done in main.
Here is a simplification of the current setup based on what was provided in the original example:
class Program {
static void Main(string[] args) {
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables()
.Build();
var azureQueueOptions = configuration.GetSection("AzureQueueOptions").Get<AzureQueueOptions>();
azureQueueOptions.StorageConnectionString = configuration.GetConnectionString("Storage");
var builder = new HostBuilder()
.ConfigureWebJobs(webJobConfiguration => {
webJobConfiguration.AddAzureStorageCoreServices();
webJobConfiguration.AddAzureStorage(c => c.BatchSize = azureQueueOptions.BatchSize);
})
.ConfigureServices(services => {
services.AddTransient<Functions>();
services.AddTransient<ICloudStorageService, AzureBlobStorageService>();
})
.ConfigureLogging((context, config) => {
config.AddConsole();
});
var host = builder.Build();
host.Run();
}
}
That or create an actual Startup
and have the builder use it:
var builder = new HostBuilder()
.UseStartup<Startup>()
//...omitted for brevity