Intermittent dependency injection failure - c#

I am seeing the following intermittent exception locally when trying to resolve a service within a .net core 2.1 azure function app. It only seems to happen when multiple messages are being processed by the function concurrently and it only fails some of the messages.
'System.Private.CoreLib: Exception while executing function: FunctionOne. Microsoft.Extensions.DependencyInjection: Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfiguration' while attempting to activate 'XXX.Service2'.
When the Service is in the same project as the function then everything works fine. It is only when I move it into another project that this occurs. The other project I created is just a simple .net standard 2.0 project with just this service in and a reference to the Microsoft.Extensions.Configuration nuget.
I know that this implementation uses a Service Locator which is an anti pattern but I still want to understand why this exception occurs.
[FunctionName("FunctionOne")]
public static void Run(
[QueueTrigger(_queue, Connection = _storageConnection)]
string queueItem,
ILogger trace)
{
// Startup
var services = Startup.GetServices();
// Services
var service = services.GetService<IService2>();
}
public static class Startup
{
public static Func<IServiceProvider> GetServices = CreateServices;
public static IConfiguration GetConfiguration()
{
return new ConfigurationBuilder()
.AddEnvironmentVariables()
.Build();
}
private static IServiceProvider CreateServices()
{
var services = new ServiceCollection();
var config = GetConfiguration();
services
.AddSingleton(config)
.AddSingleton<IService2, Service2>();
return services.BuildServiceProvider();
}
}
public class Service2 : IService2
{
public Service2(IConfiguration configuration)
{
}
}
public interface IService2
{
}

Try injecting it as an IConfigurationRoot instead of IConfiguration:
public HomeController(IConfigurationRoot configuration
, IService2 service)
{
_mailService = service;
_to = configuration["emailAddress.Support"];
}
In this case, the line
services.AddSingleton(provider => Configuration);
is equivalent to
services.AddSingleton<IConfigurationRoot>(provider => Configuration);
because the Configuration property on the class is declared as such, and injection will be done by matching whatever type it was registered as. We can replicate this pretty easily, which might make it clearer:
try this and see if it helps.

It appears to be a 'feature' that was introduced with version 2.0.12408.0 of the runtime. It does not happen with 2.0.12382.0.

Related

Unable to add a singleton service due to serviceProvider.GetService<IRabbitMQConsumer>() returning null

I am trying to add a singleton service of the type mentioned in IRabbitMQConsumer with an implementation type in RabbitMQConsumer to the Iservicecollection.
But when I tried using below approach, I am receiving Null Reference exception as the GetService<ILoggerFactory> is returning null value.
This code was working when I was using .Net core 2.2 and appears as a problem when I have migrated to .Net core 3.1.12. I have tried many stack-overflow threads as below, where it seems to be correct but not sure why my code is throwing an exception.
Resolving instances with ASP.NET Core DI from within ConfigureServices,
How to Resolve Instance Inside ConfigureServices in ASP.NET Core
Please find my code below and kindly help me to fix this problem as I am very beginner in these middle-wares.
#Program.cs
internal class Program
{
private static void Main(string[] args)
{
IServiceCollection services = new ServiceCollection();
IServiceProvider serviceProvider = services.BuildServiceProvider();
Startup startup = new Startup();
startup.ConfigureServices(services, logger);
//configure console logging
serviceProvider.GetService<ILoggerFactory>();
var logger = serviceProvider.GetService<ILoggerFactory>(); // logger returns null
//do the actual work here
var client = serviceProvider.GetService<IRabbitMQConsumer>(); // client returns null
client.CreateConnection();
client.ProcessMessages();
}
}
#startup.cs
public class Startup
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
IConfigurationRoot Configuration { get; }
public Startup()
{
LogManager.LoadConfiguration(string.Concat(Directory.GetCurrentDirectory(), "/NLog.config"));
}
public void ConfigureServices(IServiceCollection services, ILoggerFactory loggerFactory)
{
loggerFactory.AddNLog(); // System.NullReferenceException
services.AddSingleton<IRabbitMQConsumer, RabbitMQConsumer>();// System.NullReferenceException
}
}
You need to configure the ServiceCollection before you call BuildServiceProvider.
In your case, you should move the IServiceProvider serviceProvider = services.BuildServiceProvider(); line down below startup.ConfigureServices(services, logger);

ASP.NET Core does not replace IoC with StructureMap

My application is based on ASP.NET Core 2.1 and .NET Core 2.1 (downgraded from 2.2) generic host as Windows Service. So, IHostBuilder is launched first with other services and frameworks and then (if role permits) web service gets launched on top using IWebHostBuilder with all that WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().StartAsync(). Secondary WebHost is another story; it is initialized and works, but I haven't checked yet if IoC replacement has the same trouble as generic host.
For now, generic host initialization:
new HostBuilder().ConfigureServices((hostContext, services) =>
{
services.AddHostedService<LifetimeService>(); // Gets launched when host is up
var container = ContainerBuilder.BuildBaseContainer(services, new WorkingPath());
services.AddSingleton<IContainer>(container);
services.AddStructureMap(); // Has no effect
});
IContainer initialization:
public static Container BuildBaseContainer(IServiceCollection services, IWorkingPath workingPath)
{
var container = new Container();
container.Configure(config =>
{
config.Scan(scan =>
{
workingPath.OwnLoadedAssemblies.Where(asm => !asm.IsDynamic).ForEach(scan.Assembly);
scan.LookForRegistries();
scan.AddAllTypesOf<IPlatformService>();
});
config.For<IContainer>().Use(() => container);
config.Populate(services);
});
container.AssertConfigurationIsValid();
return container;
}
And the trouble is here, in the constructor of that registered hosted service (or anywhere else)
public LifetimeService(IEnumerable<IPlatformService> services,
IServiceProvider sp, IContainer c)
{
var inCollection = services.Any();
var inContainer = c.TryGetInstance<IPlatformService>() != default;
var inProvider = sp.GetRequiredService<IPlatformService>() != default;
}
ps: IServiceProvider and IContainer are for demonstration purposes only, I only need 'services'
When LifetimeService is initialized during container.AssertConfigurationIsValid() I get
inCollection is true
inContainer is true
inProvider is true
IServiceProvider is StructureMapServiceProvider
Actual LifetimeService execution shows that
inCollection is false
inContainer is true
inProvider is false
IServiceProvider is ServiceProviderEngineScope
I don't plan to pass IServiceProvider or IContainer into constructors, but it seems that dependencies are resolved using IServiceProvider, not IContainer, and I get nulls. Silly thing like sp.GetRequiredService<IContainer>().TryGetInstance<IPlatformService>() does work.
There been some happy-path examples using WebHost and Startup classes where injection ought to be working properly. Doesn't seem relevant for generic host ...which might replace WebHost one day, but is little known and not widely used. Well, could be due to .NET Core version downgrade too, but quite unlikely. I've also tried replacing IServiceProvider and IServiceScopeFactory from IContainer during ConfigureServices() without luck. My idea is to replace or forward internal container to StructureMap. I might be misunderstanding how that should work...
Has anyone successfully tried to 'marry' generic host and external IoC?
I've solved the puzzle! Finally, according to a too much simplified example (https://github.com/aspnet/Hosting/blob/master/samples/GenericHostSample/ProgramFullControl.cs), I had to change HostBuilder initialization to
new HostBuilder()
.UseServiceProviderFactory(new StructureMapContainerFactory(workingPath))
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<LifetimeService>();
});
and introduce provider factory itself
public class StructureMapContainerFactory : IServiceProviderFactory<IContainer>
{
private readonly IWorkingPath workingPath;
// pass any dependencies to your factory
public StructureMapContainerFactory(IWorkingPath workingPath)
{
this.workingPath = workingPath;
}
public IContainer CreateBuilder(IServiceCollection services)
{
services.AddStructureMap();
return ContainerBuilder.BuildBaseContainer(services, workingPath);
}
public IServiceProvider CreateServiceProvider(IContainer containerBuilder)
{
return containerBuilder.GetInstance<IServiceProvider>();
}
}
Now internal container is substituted with StructureMap and resolved IServiceProvider in LifetimeService is of type StructureMapServiceProvider.

Error on Inherit Startup in ASP.NET Core 2.1 Functional Tests

I created a project functional testing in ASP.NET Core 2.1 and xUnit. I have a function CreateServer() like this:
public TestServer CreateServer()
{
var path = Assembly.GetAssembly(typeof(TScenarioBase))
.Location;
var hostBuilder = new WebHostBuilder()
.UseContentRoot(Path.GetDirectoryName(path))
.UseStartup<IntergrationTestStartup>();
return new TestServer(hostBuilder);
}
My class IntergrationTestStartup like this:
public class IntergrationTestStartup: Startup
{
public IntergrationTestStartup(IConfiguration configuration) : base(configuration)
{
}
}
and IntergrationTestStartup is inherited from Startup class from main project.
But I got 404 not found when execute any test case. When I replaced IntergrationTestStartup by Startup, it worked as expected, But I cannot customize Startup class for test environment.
Please help me to use class IntergrationTestStartup in functional testing environment.
Thanks,
I had the same problem. During TEST execution i need to use the DocumentRepositoryMock service instead of DocumentRepositoryApiProxy. The registration in Startup class is:
serviceCollection.AddScoped<IDocumentService, DocumentRepositoryApiProxy>();
and my test code is:
[TestInitialize]
public virtual void TestInitialize()
{
...
...
this.Configuration = new ConfigurationBuilder()
.SetBasePath(AppContext.BaseDirectory)
.AddJsonFile("appsettings.json")
.Build();
var host = new WebHostBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.UseConfiguration(this.Configuration)
.UseEnvironment(Extensions.HostingEnvironmentExtensions.IntegrationTest)
.UseStartup<TestStartup>();
this.Server = new TestServer(host);
this.HttpClient = this.Server.CreateClient();
My TestStartup class inherit from Startup and override a method where i make a different service registrations based on the TEST execution:
serviceCollection.AddScoped<IDocumentService, DocumentRepositoryMock>();
However, the HttpClient used in my test return 404.
I found this solution:
var host = new WebHostBuilder()
.UseContentRoot(AppContext.BaseDirectory)
.UseConfiguration(this.Configuration)
.UseEnvironment(Extensions.HostingEnvironmentExtensions.IntegrationTest)
.UseStartup<TestStartup>();
host.ConfigureTestServices(services =>
{
services.AddScoped<IDocumentService, DocumentRepositoryMock>();
});
Using ConfigureTestServices i can registered my Mock service and have it injected in my consumer class, without the need of extend my Startup class.
In any case, if you can add a the second registration services.AddScoped<IDocumentService, DocumentRepositoryMock>() for your test, this will work. Infact having two registration for the same interface, the IoC will give you the last one registered.

How to create a singleton of a custom class and inject it other classes in .Net core 2.0? 'No service for type 'Services' has been registered.'?

I have a class in a .Net core 2.0 console application. The class provides data service and will be used by the Main class. So a singleton of the class will be created for DI.
public class Services
{
private readonly DbContext _context;
public Services(DbContext context)
{
_context = context;
}
public IEnumerable<Items> GetList(string region)
{
return _context.Items;
}
}
The following code is in Program class.
var services = new ServiceCollection();
services.AddTransient<Main>(); // With constructor of public Main(Services service)
var serviceProvider = services.BuildServiceProvider();
....
services.AddDbContext<DbContext>(o => o.UseSqlServer(defaultConn));
services.AddSingleton<Services>();
var s = serviceProvider.GetRequiredService<Services>(); // Error
services.AddSingleton(s);
However, the second to the last line got the error of
'No service for type 'Services' has been registered.'
?
The Main class will need to access a singleton of Services using DI.
Once the service provider is built, it will not have knowledge of any additional services added to the service collection. Only build the provider once all the types have been added.
var services = new ServiceCollection();
services.AddTransient<Main>(); // With constructor of public Main(Services service)
//....
services.AddDbContext<DbContext>(o => o.UseSqlServer(defaultConn));
services.AddSingleton<Services>();
var serviceProvider = services.BuildServiceProvider();
var s = serviceProvider.GetRequiredService<Services>(); // Should work now
When Main is resolved the singleton will be injected
var main = serviceProvider.GetService<Main>(); //will include singleton Service

Unable to resolve service for type IOptions[DataAccessConfiguration] in non-ASP.NET Core app

All of our business services were previously set up to use Dependency Injection with IOptions because they were being consumed by ASP.NET Core apps, like so:
NotificationDataAccess.cs:
public class NotificationDataAccess : BaseDataAccess, INotificationDac<Notification>
{
public NotificationDataAccess(IOptions<DataAccessConfiguration> options, IClaimsAccessor claimsAccessor) :
base(options, claimsAccessor)
{
}
}
NotificationBusinessService.cs:
public class NotificationBusinessServices : INotificationServices<Notification>
{
private readonly INotificationDac<Notification> _notificationDataAccess;
public NotificationBusinessServices(
INotificationDac<Notification> notifcationDataAccess)
{
_notificationDataAccess = notifcationDataAccess;
}
}
Now I'm left with the unenviable task of trying to figure out how to leverage the same pattern from a windows service, which doesn't benefit from the built-in ASP.NET Core features for handling DI. When the service starts up, I execute the following code:
// Set up configuration, services, and logging.
IServiceCollection services = new ServiceCollection();
var startup = new Startup();
startup.ConfigureServices(services);
IServiceProvider serviceProvider = services.BuildServiceProvider();
var configuration = serviceProvider.GetService<IConfigurationRoot>();
var notificationService = serviceProvider.GetService<INotificationServices<Notification>>();// TODO: This errors!
processor = new Processor(configuration, notificationService);
And here is the Startup.cs code, which is supposed to configure the services:
public class Startup
{
IConfigurationRoot Configuration { get; }
public Startup()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Path.Combine(AppContext.BaseDirectory))
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfigurationRoot>(Configuration);
//services.AddMvc();
// Add application services.
Listings.Business.Configuration.Instance = new BusinessLayerConfiguration();
services.Configure<DataAccessConfiguration>(options => Configuration.GetSection("Data").Bind(options));
services.AddScoped(typeof(INotificationDac<Notification>), typeof(NotificationDataAccess));
services.AddScoped(typeof(INotificationServices<Notification>), typeof(NotificationBusinessServices));
}
}
Unfortunately, when I run the windows service it throws an exception when trying to get the notificationService:
var notificationService = serviceProvider.GetService<INotificationServices<Notification>>();
The exception is:
System.InvalidOperationException: 'Unable to resolve service for type
'Microsoft.Extensions.Options.IOptions`1[Rpr.Listings.DataAccess.DataAccessConfiguration]'
while attempting to activate
'Rpr.Listings.DataAccess.NotificationDataAccess'.'
I was hoping my "services.Configure" code would resolve this, but alas no. Clearly I need to register IOptions in my Startup.cs, however I have no idea how to do so. Is this something that usually happens out of the box with ASP.NET MVC? Does "services.AddMvc();" normally register this binding correctly? I can call that, but would need to import a ton of ASP.NET MVC packages into my windows service, which I'm reluctant to do.
Please let me know how to register the IOptions binding correctly, thanks!
It turns out that all I was missing was:
services.AddOptions();
Once I added that, the IOptions binding was registered correctly!
In case it helps anyone, I had this issue in a console app and it was caused by creating the service provider
IServiceProvider serviceProvider = services.BuildServiceProvider();
before I'd registered my config
services.Configure<DataAccessConfiguration>(Configuration.GetSection("Data"));

Categories