I have a program in c#, dotnet 5.0, running on Linux Ubuntu, that registers a Singleton dependency:
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
services.AddGrpcReflection();
services.AddSingleton(Configuration);
services.AddSingleton<IExchangeService, HuobiAPI>();
services.AddSingleton<IMarketWriter, MarketCSVWriter>();
}
where HuobiAPI is
public class HuobiAPI : ExchangeServices
And HuobiAPI accepts gRPC calls. One of the gRPC methods defined in ExchangeService is SubscribeMarket2UDP:
public override Task<SuccessReply> SubscribeMarket2UDP(
MarketSubscriptionMessage request,
ServerCallContext context)
Now, whenever this method is called (from a remote program), a new instance of ExchangeService is created. However, my understanding is that this should be a singleton.
Is there any obvious reason for a AddSingleton dependency to create multiple instances of the same class?
I finally found a solution to the problem in "Dependency Injection: Principles, Practices, and Patterns - Steven van Deursen & Mark Seemann". Changing the registration of the dependency solved the problem. For some reason that I don't yet fully understand doing:
services.AddSingleton<IExchangeService, HuobiAPI>();
is bad and I guess it is called a 'Torn Lifestyle'.
Now doing:
services.AddSingleton<HuobiAPI>();
services.AddSingleton<IExchangeService>(c => c.GetRequiredService<HuobiAPI>());
Works fine and only one instance of the HuobiAPI class is created.
If anybody has any comments or explanation for this, feel free to comment. I'll have to dig more into the book to understand what is really going on here.
Related
I have a Blazor server-side application that uses Quartz.NET to run a scheduled job. The job merely calls an injected helper class...
public class DownloadEmailsJob : IJob {
private readonly EmailDownloadHelper _edh;
public DownloadEmailsJob(EmailDownloadHelper edh) =>
_edh = edh;
public async Task Execute(IJobExecutionContext context) =>
await _edh.DownloadEmails();
}
EmailDownloadHelper uses injection to get various services, such as the DbContext, etc. That part is all working fine.
I now have a need to call a SignalR hub method from inside the helper class. Following the code in Microsoft's documentation, in order to create the hub connection, I need to do something like this...
_hubConnection = new HubConnectionBuilder()
.WithUrl(_navigationManager.ToAbsoluteUri(EmailNotifierHub.Endpoint))
.Build();
...where EmailNotifierHub contains the following line...
public const string Endpoint = "/EmailHub";
This means that I need an instance of NavigationManager in my job. I tried injecting it in the constructor (as I do with the other dependencies in this class), but when it tries to create the hub connection, I get an exception...
System.InvalidOperationException' in Microsoft.AspNetCore.Components.dll
'RemoteNavigationManager' has not been initialized
The instance of NavigationManager is injected fine, meaning it's not null, and the exception only comes when it tries to call ToAbsoluteUri
I've done this sort of thing before in injected classes, but not in a Quartz.NET job, which is where I suspect the problem lies.
Anyone able to explain what's going wrong, and how I fix it? Thanks
Your main issue is that NavigationManager does not work properly outside a Circuit, and Quartz is (presumably) set up once for the application. The DI, therefore, does not have a scope in which it can provide a NavigationManager.
In similar situations, I usually resign to setting the base url of the site in appsettings.json, and use that to construct the absolute urls I need.
Scenario
I am trying to change my existing HttpClient to IHttpClientFactory. When I verified the existing code, its using using{...} statement which causes issues and it is mentioned here. So I thought of implementing singleton Http client and reached another blog related to this and it is here.
From all these, I understood that the best one is IHttpClientFactory introduced in .NET Core.
Implementation Plan
As this application is in ASP.NET MVC 4 and does not use DI, I have to do something to use without the DI framework. Based on my search, got answers from StackOverflow and planned to implement the same way. Meanwhile, I also got another project, which already removed all the dependencies and is ready to use in earlier projects without doing all things. The repo is HttpClientFactoryLite.
Question
Now I can use HttpClientFactoryLite by initializing this class? The description also mentioned it can be used along with the existing DI framework so that ClientFactory can be registered as a singleton. Please find the wordings from the readme
using HttpClientFactoryLite;
var httpClientFactory = new HttpClientFactory(); //bliss
If you are using dependency injection, make sure that IHttpClientFactory is registered as a singleton.
In my scenario, I don't have any DI framework added. So I am going to initialize the factory wherever I needed. Here I am confused that in 2 things
Is it necessary to make a singleton class for HttpClientFactoryLite?
How is this HttpClientFactory class disposed? Is there a need to dispose of it as part of the controller or same using statement etc?
Based on the answer from this, Microsoft.Extensions.Http provides the HttpClientFactory only, not the new optimized HttpClient. This is only available in .NET Core 2.1. So any difference in implementing IHttpClientFactory?
Please advise
ASP.NET 3.1:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IHttpClientFactory, HttpClientFactory>();
}
ASP.NET will automatically pass the correct singleton to controllers which demand an IHttpClientFactory in their constructor.
Poormans variation without DI-Container:
public static class Singleton<TInterface>
{
private static TInterface instance;
public static TInterface Instance
{
get => instance;
private set => instance ??= value;
}
public static void Add<TConcrete>() where TConcrete : TInterface, new()
=> Instance = new TConcrete();
public static void Add<TConcrete>(TConcrete instance) where TConcrete : TInterface
=> Instance = instance;
// put dispose logic if necessary
}
Usage:
// Application Entrypoint
Singleton<IHttpClientFactory>.Add<HttpClientFactory>();
// Class/Controller Property
private readonly IHttpClientFactory httpClientFactory
= Singleton<IHttpClientFactory>.Instance;
I am using Azure Functions version 2.x. It has built-in support for dependency injection.
So I can register my service IMyService for DI at singleton scope using:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IOther, DefaultOther>();
builder.Services.AddSingleton<IMyService, DefaultMyService>(); // IMyService depends on IOther.
}
}
An instance of DefaultMyService is created the first time the function gets executed. This means the first request is slower because it does heavy initialization that happens inside DefaultMyService (it populates cache, etc.).
Question: Is there a way to have DefaultMyService created earlier than the first request?
A similar question was asked for asp.net core, and the answers there suggests a few solutions, but none of them works in the context of a function app:
Option 1: Create an instance of my service (initialization happens here), and then register the instance (instead of registering the type)
var foo = new Foo();
services.AddSingleton<IFoo>(foo);
This doesn't work because in my case IMyService depends on other services, which are not instantiated at the time when I am registering IMyService in the Configure method. It fails with an error that's described here.
Option 2: Other suggestion is to use overloaded Configure method:
public void Configure(IApplicationBuilder app, IFoo foo)
{
...
}
This also doesn't work because in case of function app, the only configure method that gets executed is Configure(IFunctionsHostBuilder builder), and other overloads are not called.
because it does heavy initialization that happens inside DefaultMyService
This is where the core of the problem lies. As Mark Seemann explained here, Injection constructors should do nothing more checking for null and storing incoming dependencies. Any time you do any I/O or invoke the class's dependencies inside the constructor, you'll get in trouble.
Your question seems similar to this q/a, and my advise would be the same: extract the initialization logic out of the constructor and either do the following:
Do the initialization before wiring the object graph and supply the DI configuration with an initialized object, or
Resolve and invoke an object graph directly after the registration phase, and before the first request, in such way that the data can be initialized.
I am having a configuration setup for Simple Injector where I have moved all of my registrations to OWIN pipeline.
Now the problem is I have a controller AccountController which actually takes parameters as
public AccountController(
AngularAppUserManager userManager,
AngularAppSignInManager signinManager,
IAuthenticationManager authenticationManager)
{
this._userManager = userManager;
this._signInManager = signinManager;
this._authenticationManager = authenticationManager;
}
Now my Owin Pipeline configurations looks something like this
public void Configure(IAppBuilder app)
{
_container = new Container();
ConfigureOwinSecurity(app);
ConfigureWebApi(app);
ConfigureSimpleinjector(_container);
app.Use(async (context, next) =>
{
_container.Register<IOwinContext>(() => context);
await next();
});
_container.Register<IAuthenticationManager>(
() => _container.GetInstance<IOwinContext>().Authentication);
_container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}
private static void ConfigureOwinSecurity(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "AppNgCookie",
//LoginPath = new PathString("/Account/Login")
});
}
private static void ConfigureWebApi(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
private static void ConfigureSimpleinjector(Container container)
{
SimpleInjectorInitializer.Initialize(container);
}
And Simple Injector Initializer looks something like this
private static void InitializeContainer(Container container)
{
container.Register<DbContext, AngularAppContext>();
container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();
container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
//container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();
container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
//container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);
//container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
// For instance:
// container.Register<IUserRepository, SqlUserRepository>();
}
Now the problem is The controller is not able to register IAuthenticationManager. I tried using
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
But that Leaves me with Exception as:
System.InvalidOperationException: No owin.Environment item was found
in the context.
In this line
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
I also tried instead of using HttpContext.Current.GetOwinContext().Authentication with the configuration mentioned above in public void Configure(app) method to register using app.Use(). And then later resolve it via container to get the IAuthenticationManager. But every possibilities have left me failed.
What am I missing here? Why HttpContext.Current.GetOwinContext().Authentcation is failing to resolve authentcation from OwinContext?
And if thats not, why is the same configuration via app.Use also not working?
As TrailMax already mentioned, the exception you got probably got raised during the call to container.Verify(). At application start-up time there is no HttpContext, hence the exception.
Although the removal of the call to container.Verify() will 'solve' the problem, I would advise against doing this and I will suggest a better solution below.
NightOwl888 references an old article of Mark Seemann (which I highly respect for his work on DI). In that article Mark explains why he thinks that verifying the container is useless. This article however seems outdated, and conflicts with newer articles from Mark. In a newer article Mark explains that one of the big advantages of using Pure DI (that is Dependency Injection without using a DI container) is that it provides the fastest feedback about correctness that you can get. Mark, and the rest of us, obviously values both the compiler's feedback and the feedback from static code analysis tools, as rapid feedback mechanism. Both Simple Injector's .Verify() and the Diagnostic Services attempt to bring this fast feedback back. In my view, Simple Injector's .Verify() method takes on the job that the compiler would do for you when doing Pure DI and the Diagnostic Services is in a sense static code analysis tool specialized to your DI configuration.
While it is indeed not possible for a container to do a 100% verification of its configuration, verifying still proved a valuable practice to me. It would be silly to think that a simple call to .Verify() would result in a completely bug free or even a working application. If somebody may think that this is what ‘verifying’ your DI configuration means, I understand why they would argue that this functionality is worthless. Sounds like a statement of truism. There is no container out there, including Simple Injector, which pretends having such a feature.
You are off course still responsible for writing integration and/or unit tests for e.g. detecting if the order of applied decorators is correct or if all implementations of ISomeService<T> are indeed registered in the container.
I want to mention 2 specific arguments from Mark’s blog against verifying the container.
It is easy to get into the situation that the container verifies, but still breaks at runtime.
I agree with that, but I do think that the Simple Injector documentation has got some great guidance on how to approach this here.
When doing convention over configuration it's easy to get registrations that shouldn't be in the container anyway.
I never had this problem, because I think it is a sane practice to prevent getting in this situation anyway.
Back to the question:
Although one of the tips in the Simple Injector documentation is to use abstract factories, I wouldn't do that in this case. Creating a factory for something that already exists, sounds pretty weird to me. Maybe it is just a problem of correct naming, but why would an AccountController need a AuthenticationFactory or AuthenticationContext? In other words, why should the application know anything about us having problem wiring things up because of some design quirks in ASP.NET Identity?
Instead, by adjusting the registration for the IAuthenticationManager we can return an Authentication component from a newly created OwinContext at startup/verify time and return the 'normal' or configured AuthenticationManager at runtime. This will remove the need for a factory and moves the responsibility to the Composition Root where it should be. And lets you inject the IAuthenticationManager everywhere you need it, while still being able to make a call to .Verify().
The code looks like:
container.RegisterPerWebRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
An even more SOLID solution however would be to not depend on the IAuthenticationManager at all, because depending on this interface causes us to violate the Interface Segregation Principle, making it hard to create a proxy implementation for it that delays the creation.
You could do this by defining an abstraction which fits your needs and only your needs. Looking at the identity template calls to the IAuthenticationManager this abstraction would need nothing more than the .SignIn() and .SignOut() methods. This however would force you to completely refactor the crappy AccountController that you got 'for free' by the Visual Studio template, which can be quite an undertaking.
What you are doing with IAuthenticationManager registration worked for me with no issues. At some point I was getting the same exception as you were getting, but that was caused by line with
container.Verify();
just after the container configuration. It was trying to create all instances of registered objects, but there was no HttpContext.Current present, hence the exception.
Are you not getting any instances out of container before any HTTP request is available? If you really need them, then the only way to work around this is use Factory, as suggested by NightOwl888. If you don't need container before the HTTP request, then refactor, so it is not use outwith HTTP request.
See my answer here.
Although you are accessing a different type, the problem is the same. You cannot rely on properties of HttpContext during application startup because the application is initialized outside of the user's context. The solution is to make an abstract factory to read the values at runtime rather than at object creation and inject the factory rather than the IAuthenticationManager type into your controller.
public class AccountController
{
private readonly AngularAppUserManager _userManager;
private readonly AngularAppSignInManager _signInManager;
private readonly IAuthenticationManagerFactory _authenticationManagerFactory;
public AccountController(AngularAppUserManager userManager
, AngularAppSignInManager signinManager
, IAuthenticationManagerFactory authenticationManagerFactory)
{
this._userManager = userManager;
this._signInManager = signinManager;
this._authenticationManagerFactory = authenticationManagerFactory;
}
private IAuthenticationManager AuthenticationManager
{
get { return this._authenticationManagerFactory.Create(); }
}
private void DoSomething()
{
// Now it is safe to call into HTTP context
var manager = this.AuthenticationManger;
}
}
public interface IAuthenticationMangerFactory
{
IAuthenticationManger Create();
}
public class AuthenticationMangerFactory
{
public IAuthenticationManger Create()
{
HttpContext.Current.GetOwinContext().Authentication;
}
}
// And register your factory...
container.Register<IAuthenticationManagerFactory, AuthenticationMangerFactory>();
I am having a configuration setup for Simple Injector where I have moved all of my registrations to OWIN pipeline.
Now the problem is I have a controller AccountController which actually takes parameters as
public AccountController(
AngularAppUserManager userManager,
AngularAppSignInManager signinManager,
IAuthenticationManager authenticationManager)
{
this._userManager = userManager;
this._signInManager = signinManager;
this._authenticationManager = authenticationManager;
}
Now my Owin Pipeline configurations looks something like this
public void Configure(IAppBuilder app)
{
_container = new Container();
ConfigureOwinSecurity(app);
ConfigureWebApi(app);
ConfigureSimpleinjector(_container);
app.Use(async (context, next) =>
{
_container.Register<IOwinContext>(() => context);
await next();
});
_container.Register<IAuthenticationManager>(
() => _container.GetInstance<IOwinContext>().Authentication);
_container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
}
private static void ConfigureOwinSecurity(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieName = "AppNgCookie",
//LoginPath = new PathString("/Account/Login")
});
}
private static void ConfigureWebApi(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);
}
private static void ConfigureSimpleinjector(Container container)
{
SimpleInjectorInitializer.Initialize(container);
}
And Simple Injector Initializer looks something like this
private static void InitializeContainer(Container container)
{
container.Register<DbContext, AngularAppContext>();
container.Register<IUserStore<Users, Guid>, AngularAppUserStore>();
container.Register<IRoleStore<Roles, Guid>, AngularAppRoleStore>();
container.Register<UserManager<Users, Guid>, AngularAppUserManager>();
container.Register<RoleManager<Roles, Guid>, AngularAppRoleManager>();
//container.RegisterPerWebRequest<SignInManager<Users, Guid>, AngularAppSignInManager>();
container.Register<IdentityFactoryOptions<AngularAppUserManager>, IdentityFactoryOptions<AngularAppUserManager>>();
//container.Register<IAuthenticationManager>(() => HttpContext.Current.GetOwinContext().Authentication);
//container.Register<SignInManager<Users, Guid>, AngularAppSignInManager>();
// For instance:
// container.Register<IUserRepository, SqlUserRepository>();
}
Now the problem is The controller is not able to register IAuthenticationManager. I tried using
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
But that Leaves me with Exception as:
System.InvalidOperationException: No owin.Environment item was found
in the context.
In this line
container.Register<IAuthenticationManager>(
() => HttpContext.Current.GetOwinContext().Authentication);
I also tried instead of using HttpContext.Current.GetOwinContext().Authentication with the configuration mentioned above in public void Configure(app) method to register using app.Use(). And then later resolve it via container to get the IAuthenticationManager. But every possibilities have left me failed.
What am I missing here? Why HttpContext.Current.GetOwinContext().Authentcation is failing to resolve authentcation from OwinContext?
And if thats not, why is the same configuration via app.Use also not working?
As TrailMax already mentioned, the exception you got probably got raised during the call to container.Verify(). At application start-up time there is no HttpContext, hence the exception.
Although the removal of the call to container.Verify() will 'solve' the problem, I would advise against doing this and I will suggest a better solution below.
NightOwl888 references an old article of Mark Seemann (which I highly respect for his work on DI). In that article Mark explains why he thinks that verifying the container is useless. This article however seems outdated, and conflicts with newer articles from Mark. In a newer article Mark explains that one of the big advantages of using Pure DI (that is Dependency Injection without using a DI container) is that it provides the fastest feedback about correctness that you can get. Mark, and the rest of us, obviously values both the compiler's feedback and the feedback from static code analysis tools, as rapid feedback mechanism. Both Simple Injector's .Verify() and the Diagnostic Services attempt to bring this fast feedback back. In my view, Simple Injector's .Verify() method takes on the job that the compiler would do for you when doing Pure DI and the Diagnostic Services is in a sense static code analysis tool specialized to your DI configuration.
While it is indeed not possible for a container to do a 100% verification of its configuration, verifying still proved a valuable practice to me. It would be silly to think that a simple call to .Verify() would result in a completely bug free or even a working application. If somebody may think that this is what ‘verifying’ your DI configuration means, I understand why they would argue that this functionality is worthless. Sounds like a statement of truism. There is no container out there, including Simple Injector, which pretends having such a feature.
You are off course still responsible for writing integration and/or unit tests for e.g. detecting if the order of applied decorators is correct or if all implementations of ISomeService<T> are indeed registered in the container.
I want to mention 2 specific arguments from Mark’s blog against verifying the container.
It is easy to get into the situation that the container verifies, but still breaks at runtime.
I agree with that, but I do think that the Simple Injector documentation has got some great guidance on how to approach this here.
When doing convention over configuration it's easy to get registrations that shouldn't be in the container anyway.
I never had this problem, because I think it is a sane practice to prevent getting in this situation anyway.
Back to the question:
Although one of the tips in the Simple Injector documentation is to use abstract factories, I wouldn't do that in this case. Creating a factory for something that already exists, sounds pretty weird to me. Maybe it is just a problem of correct naming, but why would an AccountController need a AuthenticationFactory or AuthenticationContext? In other words, why should the application know anything about us having problem wiring things up because of some design quirks in ASP.NET Identity?
Instead, by adjusting the registration for the IAuthenticationManager we can return an Authentication component from a newly created OwinContext at startup/verify time and return the 'normal' or configured AuthenticationManager at runtime. This will remove the need for a factory and moves the responsibility to the Composition Root where it should be. And lets you inject the IAuthenticationManager everywhere you need it, while still being able to make a call to .Verify().
The code looks like:
container.RegisterPerWebRequest<IAuthenticationManager>(() =>
AdvancedExtensions.IsVerifying(container)
? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication);
An even more SOLID solution however would be to not depend on the IAuthenticationManager at all, because depending on this interface causes us to violate the Interface Segregation Principle, making it hard to create a proxy implementation for it that delays the creation.
You could do this by defining an abstraction which fits your needs and only your needs. Looking at the identity template calls to the IAuthenticationManager this abstraction would need nothing more than the .SignIn() and .SignOut() methods. This however would force you to completely refactor the crappy AccountController that you got 'for free' by the Visual Studio template, which can be quite an undertaking.
What you are doing with IAuthenticationManager registration worked for me with no issues. At some point I was getting the same exception as you were getting, but that was caused by line with
container.Verify();
just after the container configuration. It was trying to create all instances of registered objects, but there was no HttpContext.Current present, hence the exception.
Are you not getting any instances out of container before any HTTP request is available? If you really need them, then the only way to work around this is use Factory, as suggested by NightOwl888. If you don't need container before the HTTP request, then refactor, so it is not use outwith HTTP request.
See my answer here.
Although you are accessing a different type, the problem is the same. You cannot rely on properties of HttpContext during application startup because the application is initialized outside of the user's context. The solution is to make an abstract factory to read the values at runtime rather than at object creation and inject the factory rather than the IAuthenticationManager type into your controller.
public class AccountController
{
private readonly AngularAppUserManager _userManager;
private readonly AngularAppSignInManager _signInManager;
private readonly IAuthenticationManagerFactory _authenticationManagerFactory;
public AccountController(AngularAppUserManager userManager
, AngularAppSignInManager signinManager
, IAuthenticationManagerFactory authenticationManagerFactory)
{
this._userManager = userManager;
this._signInManager = signinManager;
this._authenticationManagerFactory = authenticationManagerFactory;
}
private IAuthenticationManager AuthenticationManager
{
get { return this._authenticationManagerFactory.Create(); }
}
private void DoSomething()
{
// Now it is safe to call into HTTP context
var manager = this.AuthenticationManger;
}
}
public interface IAuthenticationMangerFactory
{
IAuthenticationManger Create();
}
public class AuthenticationMangerFactory
{
public IAuthenticationManger Create()
{
HttpContext.Current.GetOwinContext().Authentication;
}
}
// And register your factory...
container.Register<IAuthenticationManagerFactory, AuthenticationMangerFactory>();