I'm using vNext implementation of DI.
How to pass parameters to constructor?
For example, i have class:
public class RedisCacheProvider : ICacheProvider
{
private readonly string _connectionString;
public RedisCacheProvider(string connectionString)
{
_connectionString = connectionString;
}
//interface methods implementation...
}
And service register:
services.AddSingleton<ICacheProvider, RedisCacheProvider>();
How to pass parameter to constructor of RedisCacheProvider class?
For example for Autofac:
builder.RegisterType<RedisCacheProvider>()
.As<ICacheProvider>()
.WithParameter("connectionString", "myPrettyLocalhost:6379");
You can either provide a delegate to manually instantiate your cache provider or directly provide an instance:
services.AddSingleton<ICacheProvider>(provider => new RedisCacheProvider("myPrettyLocalhost:6379"));
services.AddSingleton<ICacheProvider>(new RedisCacheProvider("myPrettyLocalhost:6379"));
Please note that the container will not explicitly dispose of manually instantiated types, even if they implement IDisposable. See the ASP.NET Core doc about Disposal of Services for more info.
If the constructur also has dependencies that should be resolved by DI you can use that:
public class RedisCacheProvider : ICacheProvider
{
private readonly string _connectionString;
private readonly IMyInterface _myImplementation;
public RedisCacheProvider(string connectionString, IMyInterface myImplementation)
{
_connectionString = connectionString;
_myImplementation = myImplementation;
}
//interface methods implementation...
}
Startup.cs:
services.AddSingleton<IMyInterface, MyInterface>();
services.AddSingleton<ICacheProvider>(provider =>
RedisCacheProvider("myPrettyLocalhost:6379", provider.GetService<IMyInterface>()));
You can use :
services.AddSingleton<ICacheProvider>(x =>
ActivatorUtilities.CreateInstance<RedisCacheProvider>(x, "myPrettyLocalhost:6379"));
Dependency Injection : ActivatorUtilities will inject any dependencies to your class.
Here is the link to the MS docs: Microsoft.Extensions.DependencyInjection.ActivatorUtilities.CreateInstance
Also: See #poke's answer here for more information. Basically it pulls from the provided services and any other params you pass, like a composit constructor.
You can use something like the example code below.
Manager class:
public class Manager : IManager
{
ILogger _logger;
IFactory _factory;
public Manager(IFactory factory, ILogger<Manager> logger)
{
_logger = logger;
_factory = factory;
}
}
Startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFactory, Factory>(sp =>
{
var logger = sp.GetRequiredService<ILogger<Factory>>();
var dbContext = sp.GetRequiredService<MyDBContext>();
return new Factory(dbContext, logger);
});
services.AddTransient<IManager, Manager>(sp =>
{
var factory = sp.GetRequiredService<IFactory>();
var logger = sp.GetRequiredService<ILogger<Manager>>();
return new Manager(factory, logger);
});
}
You can read the full example here: DI in Startup.cs in .Net Core
A bit late to the party, but you could DI inject a factory that creates and exposes an instance of your provider class.
Related
Bit of a newbie question. I am having trouble getting access to dependency injected services from within my own custom class in ASP.NET Core 3.1
I can access services fine from within a controller or razor page e.g. I can get hold of configuration and data context information:
public class DetailModel : PageModel
{
private readonly MyDataContext _context;
private readonly IConfiguration _config;
public DetailModel(MyDataContext context, IConfiguration config)
{
_context = context;
_config = config;
}
etc......
}
I now wish to access these from the constructor of a custom class that is not a controller or razor page. e.g. I am using:
public class ErrorHandling
{
private readonly MyDataContext _context;
private readonly IConfiguration _config;
public ErrorHandling(MyDataContext context, IConfiguration config)
{
_context = context;
_config = config;
}
}
The problem is that when I instantiate my class it insists on me passing the service values into the constructor:
var myErrorHandler = new ErrorHandling(`<wants me to pass context and config values here>`)
This defeats the whole point of DI. I think I am missing something fundamental here!
What am I missing?
You can register ErrorHandling as a service too, in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
// other stuff..
services.AddScoped<ErrorHandling>(); // this should work as long as both 'MyDataContext' and 'IConfiguration' are also registered
}
If you need an instance of ErrorHandling in your page model, you can specify it in the constructor and ASP.NET Core will resolve it for you at runtime.
This way you won't have to new it:
public class DetailModel : PageModel
{
private readonly MyDataContext _context;
private readonly IConfiguration _config;
private readonly ErrorHandling _errorHandling;
public DetailModel(ErrorHandling errorHandling, MyDataContext context, IConfiguration config)
{
_context = context;
_config = config;
_errorHandling = errorHandling;
}
}
This article can be useful: Dependency injection in ASP.NET Core
If you don't want register as a service, you can use ActivatorUtilities.CreateInstance to resolve ErrorHandling.
Instantiate a type with constructor arguments provided directly and/or from an IServiceProvider.
e.g.:
// IServiceProvider serviceProvider = ...;
var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(serviceProvider);
BUT you need to be careful about this solution:
ServiceProvider scope should equal with dependency object (MyDataContext, IConfiguration). Otherwise, you will get an exception like:
var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(app.ApplicationServices);
// An exception of type 'System.InvalidOperationException' occurred
// in Microsoft.Extensions.DependencyInjection.dll but was not handled in user cod
// e: 'Cannot resolve scoped service 'three.MyDataContext' from root provider.'
For this, you can create an scope to resolve ErrorHandling:
using (var scope = app.ApplicationServices.CreateScope())
{
var errorHandling = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
}
Dependency injection service would not call Dispose on IDisposable instances even out of scope.
For this, you should call Dispose() by yourself:
using (var scope = app.ApplicationServices.CreateScope())
{
using var disposablClass = ActivatorUtilities.CreateInstance<DisposablClass>(scope.ServiceProvider);
}
ActivatorUtilities.CreateInstance will new an instance even you use the same ServiceProvider:
using (var scope = app.ApplicationServices.CreateScope())
{
var errorHandling1 = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
Console.WriteLine(errorHandling1.GetHashCode());
// 11903911
var errorHandling2 = ActivatorUtilities.CreateInstance<ErrorHandling>(scope.ServiceProvider);
Console.WriteLine(errorHandling2.GetHashCode());
// 40026340
}
I have the following asp.net c# code
{
public class Test
{
ISomeService _someService;
public Test()
{
}
public void DoSomething()
{
_someService.Do();
}
}
I need to provide ISomeService to Test class, and I dont know how to do it. I am not allowed to add additional construction which would make entire problem go away, for example
public Test(ISomeService someService)
{
_someService = someService;
}
I tried using setter injection or method injection but that didnt do the trick.
Implementation of ISomeService in SomeService class also uses constructor injection, such as
public SomeService(IService1 service1, Iservice2 service2)
Not sure what to do here.
HERE IS A COMPLETE CODE
public class Startup
{
private IService _service;
public Startup()
{
}
public Startup(IService service)
{
_service = service;
}
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
container.RegisterType<IService, Service>();
config.DependencyResolver = new UnityDependencyResolver(container);
app.UseWebApi(config);
_service.DoSomething());
}
}
_service is null
I would suggest you use a factory to create your object. That would have an instance of ISomeService injected on the constructor.
Then in a CreateTest() method on your factory set the ISomeService property directly.
public class Factory
{
private readonly ISomeService someService;
public Factory(ISomeService someService)
{
this.someService = someService ?? throw new ArgumentNullException(nameof(someService));
}
public TestClass CreateTestClass()
{
var instance = new TestClass();
instance.SomeService = this.someService;
return instance;
}
}
You should note that most DI providers have built in functionality to allow factory semantics without the need to create your own factories.
What I ended up doing is this
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IService, Service>();
// create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
_service = ActivatorUtilities.CreateInstance<Service>(serviceProvider);
_service.DoSomething();
Thanks to this answer Dependency Injection with classes other than a Controller class
I need the ASP.Net Core dependency injection to pass some parameters to the constructor of my GlobalRepository class which implements the ICardPaymentRepository interface.
The parameters are for configuration and come from the config file and the database, and I don't want my class to go and reference the database and config itself.
I think the factory pattern is the best way to do this but I can't figure out the best way to use a factory class which itself has dependencies on config and database.
My startup looks like this currently:
public class Startup
{
public IConfiguration _configuration { get; }
public IHostingEnvironment _environment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
_configuration = configuration;
_environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IDbRepository, DbRepository>();
var connection = _configuration.GetConnectionString("DbConnection");
services.Configure<ConnectionStrings>(_configuration.GetSection("ConnectionStrings"));
services.AddDbContext<DbContext>(options => options.UseSqlServer(connection));
services.AddScoped<ICardPaymentRepository, GlobalRepository>();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IRFDbRepository rFDbRepository)
{
...
}
}
The GlobalRepository constructor looks like this:
public GlobalRepository(string mode, string apiKey)
{
}
How do I now pass the mode from configuration and the apiKey from the DbRepository into the constructor from Startup?
Use the factory delegate overload when registering the repository
//...
string mode = "get value from config";
services.AddScoped<ICardPaymentRepository, GlobalRepository>(sp => {
IDbRepository repo = sp.GetRequiredService<IDbRepository>();
string apiKey = repo.GetApiKeyMethodHere();
return new GlobalRepository(mode, apiKey);
});
//...
Alternative using ActivatorUtilities.CreateInstance
//...
string mode = "get value from config";
services.AddScoped<ICardPaymentRepository>(sp => {
IDbRepository repo = sp.GetRequiredService<IDbRepository>();
string apiKey = repo.GetApiKeyMethodHere();
return ActivatorUtilities.CreateInstance<GlobalRepository>(sp, mode, apiKey);
});
//...
You might want to also check these links...
https://github.com/Microsoft/AspNetCoreInjection.TypedFactories
https://espressocoder.com/2018/10/08/injecting-a-factory-service-in-asp-net-core/
With regard to the last link the code is basically:
public class Factory<T> : IFactory<T>
{
private readonly Func<T> _initFunc;
public Factory(Func<T> initFunc)
{
_initFunc = initFunc;
}
public T Create()
{
return _initFunc();
}
}
public static class ServiceCollectionExtensions
{
public static void AddFactory<TService, TImplementation>(this IServiceCollection services)
where TService : class
where TImplementation : class, TService
{
services.AddTransient<TService, TImplementation>();
services.AddSingleton<Func<TService>>(x => () => x.GetService<TService>());
services.AddSingleton<IFactory<TService>, Factory<TService>>();
}
}
I think castle windsor's typed factories dispose of all they created when they themselves are disposed (which may not be always the best idea), with these links you would probably have to consider if you are still expecting that behaviour. When I reconsidered why I wanted a factory I ended up just creating a simple factory wrapping new, such as:
public class DefaultFooFactory: IFooFactory{
public IFoo create(){return new DefaultFoo();}
}
I'll show the minimal example for the factory that resolves ITalk implementation by a string key. The solution can be easily extended to a generic factory with any key and entity type.
For the sake of example let's define the interface ITalk and two implementations Cat and Dog:
public interface ITalk
{
string Talk();
}
public class Cat : ITalk
{
public string Talk() => "Meow!";
}
public class Dog : ITalk
{
public string Talk() => "Woof!";
}
Now define the TalkFactoryOptions and TalkFactory:
public class TalkFactoryOptions
{
public IDictionary<string, Type> Types { get; } = new Dictionary<string, Type>();
public void Register<T>(string name) where T : ITalk
{
Types.Add(name, typeof(T));
}
}
public class TalkFactory
{
private readonly IServiceProvider _provider;
private readonly IDictionary<string, Type> _types;
public TalkFactory(IServiceProvider provider, IOptions<TalkFactoryOptions> options)
{
_provider = provider;
_types = options.Value.Types;
}
public ITalk Resolve(string name)
{
if (_types.TryGetValue(name, out var type))
{
return (ITalk)_provider.GetRequiredService(type);
}
throw new ArgumentOutOfRangeException(nameof(name));
}
}
Add extension method for simple implementations registration:
public static class FactoryDiExtensions
{
public static IServiceCollection RegisterTransientSpeaker<TImplementation>(this IServiceCollection services, string name)
where TImplementation : class, ITalk
{
services.TryAddTransient<TalkFactory>();
services.TryAddTransient<TImplementation>();
services.Configure<TalkFactoryOptions>(options => options.Register<TImplementation>(name));
return services;
}
}
And register the Cat and Dog implementations:
services
.RegisterTransientSpeaker<Cat>("cat")
.RegisterTransientSpeaker<Dog>("dog");
Now you can inject the TalkFactory and resolve the implementation by the name:
var speaker = _factory.Resolve("cat");
var speech = speaker.Talk();
The trick here is Configure<TOptions(). This method is additive, which means you can call it multiple times to configure the same instance of TalkFactoryOptions.
As I said this example can be converted into a generic factory and add the ability to register factory delegate instead of a concrete type. But the code will be too long for SO.
I've been running up against the same issue and solved this by registering a set of open generics for IFactory<TService>, IFactory<T, TService>, IFactory<T1, T2, TService> etc. A single call on startup to add this facility then allows any IFactory<...> to be injected / resolved, which will instantiate an instance of TService for a given set of argument types, provided a constuctor exists whose last parameters match the T* types of the factory generic. Source code, NuGet package and explanatory blog article below:
https://github.com/jmg48/useful
https://www.nuget.org/packages/Ariadne.Extensions.ServiceCollection/
https://jon-glass.medium.com/abstract-factory-support-for-microsoft-net-dependency-injection-3c3834894c19
An alternative to the other answers. Follow the options pattern.
First introduce a strong type for your configuration;
public class RespositoryOptions {
public string Mode { get; set; }
public string ApiKey { get; set; }
}
public GlobalRepository(IOptions<RespositoryOptions> options) {
// use options.Value;
}
You could still use a service factory method to unwrap the IOptions<RespositoryOptions> if you prefer. But then you lose the ability to verify that your service dependencies have all been met.
Then you can seed your options from configuration;
public void ConfigureServices(IServiceCollection services) {
...
services.Configure<RespositoryOptions>(_configuration.GetSection(name));
...
}
And write another service to update that options instance from other services, like a database;
public class ConfigureRespositoryOptions : IConfigureOptions<RespositoryOptions> {
private readonly IDbRepository repo;
public ConfigureRespositoryOptions(IDbRepository repo) {
this.repo = repo;
}
public void Configure(RespositoryOptions config) {
string apiKey = repo.GetApiKeyMethodHere();
}
}
I want to register Repository with Autofac so that it is the same as in parameterless constructor? Which I then intend to remove.
Example code:
public class GroupController : ApiController
{
protected IRepository Repository;
protected ISettings Settings;
protected GroupController()
{
Settings = new Settings();
Repository = new Repository(User.Identity.IsAuthenticated ? Settings.ConfigAuth : Settings.Config)
}
public GroupController(ISettings settings, IRepository repository)
{
Repository = repository;
Settings = settings;
}
}
This is an api controller in .Net framework.
Settings are written in web.config.
This is how autofac config looked like before I had only one config.
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
var settings = new Settings();
Builder.RegisterType<Repository>()
.As<IRepository>()
.WithParameter(new TypedParameter(typeof(RepoConfig), settings.Config))
.InstancePerRequest();
There is currently only one implementation of IRepository.
public class Repository : IRepository
{
private readonly RepoConfig _config;
public(RepoConfig config)
{
_config = config;
}
}
You can use Lambda Registrations. From Autofac documentation
You can register components using lambda expressions and make a
runtime choice right in the registration for how to handle things.
Note this may have an effect on performance depending on the expense
of the runtime check and how often it gets executed, but it’s an
option.
http://docs.autofac.org/en/latest/register/registration.html (Check lambda expression components)
Based on your example using Lambda Registrations, you can do autofac reg as below (I have not tested)
var builder = new ContainerBuilder();
builder.Register(c => new Settings()).As<ISettings>();
builder.Register<Repository>((c) =>
{
ISettings s = c.Resolve<ISettings>();
Settings repoSettings = User.Identity.IsAuthenticated ? s.ConfigAuth : s.Config;
return new Repository(repoSettings);
}
)
.As<IRepository>()
.InstancePerRequest();
Container = builder.Build();
Then remove your parameter-less constructor and add
public GroupController(IRepository repository)
{
Repository = repository;
}
hope it helps.
I implemented an workaround for this implementation. Not the best way but it works.
This way initialization of Repository doesn't need parameter and I set it with a propertie in constructor of Controller.
public class GroupController : ApiController
{
protected IRepository Repository;
protected ISettings Settings;
public GroupController(ISettings settings, IRepository repository)
{
Settings = settings;
Repository = repository;
Repository.RepoConfig = User.Identity.IsAuthenticated ? Settings.ConfigAuth : Settings.Config
}
}
public class Repository : IRepository
{
private readonly RepoConfig _config;
public RepoConfig RepoConfig
{
get => _config;
set
{
_config = value;
}
}
...
}
And for autofac there is standard DI refister:
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
Builder.RegisterType<Repository>()
.As<IRepository>()
.InstancePerRequest();
I am doing some dependency injection with Microsoft.Practices.Unity.
For some classes, I am using injection factories like this:
container.RegisterType<ICar>(new InjectionFactory(o => {return new Car("Toyota")}));
Later in my code, I want to be able to find out if I have used or not an injection factory for a given interface.
I see that I can get regitrations in container.Registrations, but these objects do not give me injection members.
A possible way to get them would be to implement a wrapper around my IUnityContainer, that records the injection members.
But maybe there is some better way that directly leverages unity API ? Is there a way to get these injection members directly from the unity container ?
as suggested in my comment (but not with unity). Just copied it from my project.
public void ConfigureServices(IServiceCollection services)
{
var lgr = LogManager.GetCurrentClassLogger();
var logger = new NlogLogger(lgr);
services.AddSingleton<ILogger>(provider => logger);
services.AddSingleton<IMachineConfigFactory, MachineConfigFactory>();
services.AddSingleton<IMemoryCacheService, MemoryCacheService>();
services.AddSingleton<IServerManagerService, ServerManagerService>();
services.AddSingleton<ISubscriberServerHubService, SubscriberServerHubService>();
services.AddSingleton<IPurgeService, PurgeService>();
var configuration = GetConfiguration(option);
services.AddSingleton(configuration);
services.AddOptions();
services.Configure<HostConfig>(configuration.GetSection("HostConfig"));
services.AddSingleton<ServerManager>();
services.AddSingleton<CacheManagerService>();
return services;
}
and then:
public class HealthChecker : IHealthChecker
{
private readonly HealthCheckerConfig _config;
private readonly IAssetProvider _assetProvider;
public HealthChecker(IOptions<HealthCheckerConfig> config, IAssetProvider assetProvider)
{
_config = config.Value;
_assetProvider = assetProvider;
}
}
or am i missing something?