Hangfire configuration and Ninject configuration - c#

I have an MVC 5 application which uses Ninject and I am adding Hangfire to it.
When I have added Ninject, I have used the NinjectWebCommon nuget package because of its simplicity in the configuration. So for now Ninject is configured through the NinjectWebCommon class which create a standard kernel and add the bindings.
Moreover I have created some custom module that I load when creating the kernel
private static IKernel CreateKernel() {
var kernel = new StandardKernel( new MyCustomModule() );
try {
kernel.Bind<Func<IKernel>>().ToMethod( ctx => () => new Bootstrapper().Kernel );
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices( kernel );
return kernel;
}
catch {
kernel.Dispose();
throw;
}
}
The Ninject Web Common is registered through the WebActivatorEx class
[assembly: WebActivatorEx.PreApplicationStartMethod( typeof( MyProject.Web.NinjectWebCommon ), "Start" )]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute( typeof( MyProject.Web.NinjectWebCommon ), "Stop" )]
Now the problem is related on how to make Hangfire to see the Ninject configuration. By looking at the Hangfire.Ninject package I can read
The package provides an extension method for IGlobalConfiguration
interface:
var kernel = new StandardKernel();
GlobalConfiguration.Configuration.UseNinjectActivator(kernel);
Now my question are:
Because of the IGlobalConfiguration interface, I should add the Hangfire Ninject configuration inside the OWIN startup method (where the Hangfire config is already placed). How should I get the current Ninject Kernel (the one that NinjectWebCommon has configured?
What about the order of execution? Is the WebActivatorExexecuting before or after the OWIN startup?
What happens if I try to execute the configuration twice?
More generally, how can I share the Ninject configuration between the two?

How should I get the current Ninject Kernel
Looking at the code of Ninject.Web.Common.Bootstrapper shows that it stores a single static instance of the kernel, and exposes it via the Kernel property. This means that you can do this inside the OWIN startup method:
GlobalConfiguration.Configuration.UseNinjectActivator(
new Ninject.Web.Common.Bootstrapper().Kernel);
and you'll have the same IKernel instance, complete with whatever bindings you configured in NinjectWebCommon.RegisterServices
What about the order of execution? Is the WebActivatorEx executing before or after the OWIN startup?
Before. You can verify this (as I did) by setting breakpoints in each. More info
More generally, how can I share the Ninject configuration between the two?
What happens if I try to execute the configuration twice?
The kernel configuration is the "composition root." According to Mark Seemann, a preeminent expert on the subject, there should only be one of these in the application, and it should be as close as possible to the application's entry point.

Related

Upgrade to .NET Core 3.1 - Can't instantiate singleton PhysicalFileProvider in services

I have a .NET Core solution containing an API project that was targeting .NET Core 2.1, and I upgraded it to .NET Core 3.1. I've realized that there is a number of breaking changes from doing that, which I have gone through and modified to be compatible (such as converting UseMvc to UseRouting and so). But now I am stuck on one:
When I try to run the API project, I get this runtime error:
Some services are not able to be constructed (Error while validating
the service descriptor 'ServiceType:
Microsoft.Extensions.FileProviders.IFileProvider Lifetime: Singleton
ImplementationType:
Microsoft.Extensions.FileProviders.PhysicalFileProvider': No
constructor for type
'Microsoft.Extensions.FileProviders.PhysicalFileProvider' can be
instantiated using services from the service container and default
values.)
In Startup.cs, ConfigureServices, there is:
services.AddSingleton<IFileProvider, PhysicalFileProvider>();
which of course is the issue. But I'm not sure how do I convert this to be both compatible and have the API work the way it has been.
I did find an article on file providers that states:
The FileProviderSample sample app creates the provider in the
Startup.ConfigureServices method using
IHostEnvironment.ContentRootFileProvider:
var physicalProvider = _env.ContentRootFileProvider;
But it's not clear as to exactly where within ConfigureServices that is supposed to go... or if that replaces the AddSingleton... or how that's going to affect the API's behavior. So I'm not sure what to do with this.
How would DI create an instance of PhysicalFileProvider without specifying a root directory?
You could create an instance by yourself specifying the root, then registering that instance into the services collection.
This works for me:
public void ConfigureServices(IServiceCollection services) {
// (...)
IFileProvider fp = new PhysicalFileProvider("C:\\path\\to\\by\\root");
// or, you could get the file provider from the environment:
// IFileProvider fp = _env.ContentRootFileProvider;
services.AddSingleton<IFileProvider, PhysicalFileProvider>(_ => fp);
// (...)
}
Then, I just inject it into my DI managed class:
IFileProvider _fileProvider;
public FilesController(IFileProvider fp) {
this._fileProvider = fp;
}
As an alternative, and If you don't mind changing the constructor parameters of your class, you could inject IWebHostEnvironment instead. Then you could have easy access to the file provider from environment.

How to avoid reload resource in ASP.NET core controller

I am new to ASP.net core. Having a web API connect to database using EntityFramework core. The controller take request, do some analysis, and send the response as below
public class CentoDataController : ControllerBase
{
private readonly CentoWebDBContext _context;
private HPOSubSimHolder _hpoSubSimHolder;
public CentoDataController(CentoWebDBContext context)
{
_context = context;
_hpoSubSimHolder = new HPOSubSimHolder(hpofile);
}
[HttpGet("{id}")]
public ActionResult<CentoData> GetCentoData(string id)
{
IQueryable<CentoData> r = AnalysisMethod(id, _hpoSubSimHolder);
return r;
}
The code works, but _hpoSubSimHolder will be reloaded once a new request comes in. I guess I shouldn't share controller between requests. But how can I avoid reloading _hpoSubSimHolder ?
I can see that you're using .net core dependency injection
If you want that service to be shared across requests, think of making it a Singleton.
You can choose between AddScoped, AddTransient and AddSingleton when registering dependencies.
In your startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
// some code
services.AddSingleton<HPOSubSimHolder>(new HPOSubSimHolder());
}
Singleton means only a single instance will ever be created. That instance is shared between all components that require it. The same instance is thus used always.
Scoped means an instance is created once per scope. A scope is created on every request to the application, thus any components registered as Scoped will be created once per request.
Transient The services created using transient lifetime will be created each time they are requested. This lifetime works best for lightweight services.
(Source)
Controllers are always instantiated per request. To control lifetime of any resources or dependencies the controller should use, you can use the build in Dependency Injection (DI).
Most examples setup DI in your startup.cs ConfigureServices method.
The DI container allows 3 different lifetime states, in your case I guess you can try to add the HPOSubSimHolder as singleton.
I have no idea what HPOSubSimHolder is and what the implementation details are, hence its hard to tell if that'll work for you.
But it would be the "normal" way of setting this up ;)

Register multiple .Net Core IHostedService in Dependency Injection - Background service in .net core

I have a .Net core (v2.2) API. That API uses some external service calls. We want to make them as fire and forget calls. We used .net core Background service to implement that. We have multiple background services. Now if I register all those IHostedService in Dependency Injection, The last registered background service works, and others come as null in constructor injection of project. Registering dependencies like:
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
And also, I tried adding them as:
services.AddSingleton<IHostedService, BackgroundServiceA>();
services.AddSingleton<IHostedService, BackgroundServiceB>();
In both the cases, only BackgroundServiceB works, BackgroundServiceA comes as null in constructor injection.
To handle this, I was using ServiceProvider to get the object.
var services = serviceProvider.GetServices<IHostedService>();
_backgroundServiceA = services.First(o => o.GetType() == typeof(BackgroundServiceA)) as BackgroundServiceA;
Is it a good way to handle such an issue or I am missing something while registering dependencies.
Also, can we trigger both the background calls parallel?
(edit, I initially answered the wrong question....)
Dependency injection doesn't work with multiple implementations of the same interface. Since each hosted service is registered as an implementation of IHostedService, only one service can be easily injected into other types.
However you can register each service again, with a factory method to locate the background singleton instance;
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
services.AddSingleton<BackgroundServiceA>(p => p.GetServices<IHostedService>().OfType<BackgroundServiceA>().Single());
services.AddSingleton<BackgroundServiceB>(p => p.GetServices<IHostedService>().OfType<BackgroundServiceB>().Single());
Edit: It's probably better to implement this the other way around;
services.AddSingleton<BackgroundServiceA>();
services.AddSingleton<IHostedService>(p => p.GetService<BackgroundServiceA>());
Try to register them via AddHostedService call in separate ConfigureServices call outside startup, as stated in docs:
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.ConfigureServices(services =>
{
services.AddHostedService<BackgroundServiceA>();
services.AddHostedService<BackgroundServiceB>();
});
avoid direct access to hosted service as discussed here
use intermediate service instead as describe in 'Queued background tasks' example

How can I use ILogger<T> from an Azure Functions V2 function?

I have some services that were initially designed to be called from my ASP.NET Core 2.1 web application. They have dependencies injected to their constructors using Microsoft.Extensions.DependencyInjection package stuff. Some of them have a dependency of ILogger logger.
public GroupService(ILogger<GroupService> logger)
{
...
}
I am building a service provider within the function so that they can still work as expected however I'm wondering what I should do about the logger dependencies. An Azure Function (V2) gets an ILogger injected into it by default but that can't be used in the DI container to create the additional loggers that the services require.
Is there a LoggerFactory registered somewhere "under the covers" that I can get access to to be used in my DI container? I think that would allow me to add additional loggers that log to the functions output window or am I completely misunderstanding how logging in a function would work?
Do I just need to set up a new LoggerFactory and make that log to the ApplicationInsights instance used by the functions project?
Using the most recent Azure Function runtime, you can now have the runtime inject your dependencies through constructors.
You can now remove the static keywords from your Azure Function and have the runtime.
Here is how you can set this up:
[assembly: WebJobsStartup(typeof(StartUp))]
public class StartUp : IWebJobsStartup
{
public void Configure(IWebJobsBuilder webJobsBuilder)
{
// Enables logging and sets the minimum level of logs.
webJobsBuilder.Services.AddLogging(loggingBuilder =>
{
loggingBuilder.SetMinimumLevel(LogLevel.Debug);
});
// Registers your services.
webJobsBuilder.Services.AddTransient<IGroupService, GroupService>();
}
}

Autofac Injection of data into OWIN Startup class

I've been looking around on SO for a while and can't find the answer to my particular question:
How do you use Autofac to inject data (via constructor or property) into the OWIN Startup class?
I'm using Autofac in a Windows service which also handles creation of back-end services etc. so I'm doing all the configuration reading and container building in there.
I want to be able to register the Startup class so I can inject allowed origins for CORS but even when I register the object with a property like so:
var builder = new ContainerBuilder();
// This will actually be read from config.
var origins = new[]
{
"http://localhost",
"http://localhost:8082",
};
builder.Register(c => new Startup(origins));
var container = builder.Build();
At runtime when the Startup class is instantiatedthe callstack shows it comes from external code and my Autofac builder didn't push the property in.
I'd ideally like to keep my Autofac registrations in a single place (the Windows service class) and just inject the required data to Startup so I can just do something like this below (where allowedOrigins is set as a property or injected via the constructor)
public void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration();
...
var origins = string.Join(",", allowedOrigins);
var cors = new EnableCorsAttribute(origins, "*", "*") { SupportsCredentials = true };
configuration.EnableCors(cors);
....
}
Any ideas would be much appreciated.
Thanks
peteski
UPDATE
I should add that after attempting Autofac registration and building, I was kicking off the OWIN self host app by doing:
var webApp = WebApp.Start<Startup>(baseAddress);
From a conversation with a friend they suggested creating the Startup object and passing it to the WebApp:
var startup = new Startup(origins);
var webApp = WebApp.Start(startup, baseAddress);
Here, I'd add the IEnumerable<string> origins to the ctor for the Startup class. This does actually work! But feels like it goes around using Autofac to handle giving the Startup class what it needs.
Out of the box Autofac doesn't support what you're looking for.
In an IIS or other non-self-hosted environment, there's no hook for building a container and then injecting into the startup class. It's sort of a chicken/egg problem - the startup class is your hook to start building the container.
In a self-hosted environment, the suggestion you mentioned - creating the startup object and passing that into the call to WebApp.Start - is your best bet.
Basically, at some point you're going to hit the entry point for your application (usually in OWIN that's the startup class) and that's the point at which you have to build your container and be the bootstrapper for resolving and starting things up. Autofac can't help you with that - that's up to the framework(s) you're using (e.g., MVC, OWIN, Web API, etc.).

Categories