I'm trying to inject a dependency into a class used internally in a controller,
I have
//Startup.cs
public void ConfigureServices(IServiceCollection services){
services.AddMvc();//IDocumentService is a WCF service from our legacy stack
services.AddScoped(typeof(IDocumentService),typeof(DocumentServiceClient));
}
//Controller.cs
[Route("api/eci/test/[action]")]
public class Controller{
private IDocumentService injectedDocService;
public Controller(IDocumentService client){
injectedDocService=client;
}
[HttpPost({"id"})]
public void ingestedDocs(string id){
new Logic(injectedDocService).ingest(id);
}
}
//Logic.cs
public class Logic{
private IDocumentService injectedDocServiceActualTarget;
public Logic(IDocumentService injected2){
injectedDocServiceActualTarget=injected2;
}
public void injest(string id){
injectedDocServiceActualTarget.doWork(id);
}
}
It seems a little redundant to have it injected to the target class's parent. Is this the right way of doing things?
You need to register the Logic, then inject it to the controller. DocumentService will be injected to the Logic then.
The idea behind dependency injection is to implement the IoC (inversion of control) principle. In your example, it is only partial, since you explicitly instantiate Logic in your controller. If you want to do the dependency inversion properly - all your dependencies need to be passed as constructor parameters.
You should abstract the Logic behind an interface, only exposing the members that are to be used by dependents.
public interface ILogic {
void injest(string id);
}
Have the Logic class derive from the abstraction.
//Logic.cs
public class Logic : ILogic {
private readonly IDocumentService injectedDocServiceActualTarget;
public Logic(IDocumentService injected2) {
this.injectedDocServiceActualTarget=injected2;
}
public void injest(string id) {
injectedDocServiceActualTarget.doWork(id);
}
}
The Controller should now only explicitly depend on the ILogic interface
//Controller.cs
[Route("api/eci/test/[action]")]
public class Controller {
private readonly ILogic service;
public Controller(ILogic service) {
this.service = service;
}
[HttpPost({"id"})]
public void ingestedDocs(string id) {
service.ingest(id);
}
}
With that the last thing to do is to register all dependencies with the service collection.
//Startup.cs
public void ConfigureServices(IServiceCollection services){
services.AddMvc();
services.AddScoped<IDocumentService, DocumentServiceClient>();
services.AddScoped<ILogic, Logic>();
}
So now when the controller is called all dependencies will be resolved and injected into their respective dependents.
Related
ASP.Net Core Web API
Does the parent class have no empty constructor
derived class Autofac injection ?
If the injection class is added after the parameter, it cannot be used
public class A
{
public A(string e1,string e2){}
}
public class B:A
{
private readonly IProductService _productService;
public B(IProductService productService):base(string e1,string e2)
{
_productService = productService
}
public void test()
{
_productService.AddProduct("");
}
}
AutoFac has no problem configuring
_productService exception occurred
You should try it like this:
public B(IProductService productService, string e1,string e2):base(e1,e2)
{
_productService = productService
}
And then configure Autofac like this for this class registration:
builder.Register(c => new B(c.Resolve<IProductService>(), "e1_val","e2_val"));
If the B class will implement an interface at some point you can use it like this also:
builder.RegisterType<B>().As<IB>()
.WithParameter("e1", "e1value")
.WithParameter("e2", "e2value");
Keep in mind that you have a lot of flexibility with Autofac, please check their documentation at: Autofac Parameters Register for even more information.
I have a class library where I want to access a Connectionstring from appsettings.json.
appsettings.json :
"DatabaseSettings": {
"ConnectionString": "Server=.;Database=Test;Trusted_Connection=True;"
},
In startup.cs I have the following code:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<DatabaseSettings>(Configuration.GetSection("DatabaseSettings"));
services.AddOptions();
services.AddTransient<IConnectionOption, Class1>();
}
And in class library
IConnectionOption.cs
public interface IConnectionOption
{
void ReadValue();
}
Class1.cs
public class Class1 : IConnectionOption
{
private readonly DatabaseSettings test;
public Class1(IOptions<DatabaseSettings> dbOptions)
{
test = dbOptions.Value;
}
public void ReadValue()
{
var r = test;
}
}
Now from index.cshtml I want to Invoke the class Library Class1
public class IndexModel : PageModel
{
public void OnGet()
{
Class1 test = new Class1();
test.ReadValue();
}
}
But of course that doesnt work since there are no Constructor taking zero parameters, I don´t think I should add IOptions as an parameter. But how do I invoke the class library to read the connectionstring? (When I get this to work I will of course read data and return instead of the connectionstring). I have looked at several examples including net core 2.1 Dependency injection
But I don´t understand how to use the class library directly, Is it necessary to use an controller ?!
If DatabaseSettings is accessible to the class library, there really is not much need for tightly coupling Class1 library to IOptions, which is more framework related.
Ideally Class1 can explicitly depend on DatabaseSettings via explicit constructor injection
public class Class1 : IConnectionOption {
private readonly DatabaseSettings test;
public Class1(DatabaseSettings settings) {
test = settings;
}
public void ReadValue() {
var r = test;
//...
}
}
then in Startup, the dependency can be extract from configuration and registered with the DI container
public void ConfigureServices(IServiceCollection services) {
var settings = Configuration.GetSection("DatabaseSettings").Get<DatabaseSettings>();
services.AddSingleton<DatabaseSettings>(settings);
services.AddTransient<IConnectionOption, Class1>();
}
That way when ever Class1 is resolved, the container will know how to inject the DatabaseSettings dependency.
Another option could have also been to use the factory delegate
public void ConfigureServices(IServiceCollection services) {
var settings = Configuration.GetSection("DatabaseSettings").Get<DatabaseSettings>();
services.AddTransient<IConnectionOption, Class1>(_ => new Class1(settings));
}
That way, when the IndexModel depends on IConnectionOption for injection.
public class IndexModel : PageModel {
private readonly IConnectionOption connectionOption;
public IndexModel(IConnectionOption iConnectionOption) {
connectionOption = iConnectionOption;
}
public void OnGet() {
connectionOption.ReadValue();
//...
}
}
the proper dependency will be injected when the page model is initialized.
You are using Dependency Injection but only halfway. This is what you are missing:
Register the service in the container (I'm going to assume a better name for Class1):
services.AddScoped<IConnectionOption, DatabaseConnectionOption>();
Make the page receive the service:
public class IndexModel : PageModel
{
private readonly IConnectionOption _IConnectionOption;
public IndexModel(IConnectionOption iConnectionOption)
{
_IConnectionOption = iConnectionOption;
}
public void OnGet()
{
_IConnectionOption.ReadValue();
}
}
I wrote a simple Data access interface with generic CRUD operations so that every database repository has to implement this interface. For now I implemented this interface for one database say SQL. This SQL implementation of the database takes database settings(connection string and other details) as parameter in it's constructor.
See below code:
namespace MyDataAccessInterfaces
{
public interface IDataAccess
{
read();
create();
update();
delete();
}
}
namespace MyDataAccessImplementations
{
public class SQLAccess
{
SQLAccess(Settings settings){
//do something to get DB context
}
//some properties here
read(){//some code
}
create(){//some code
}
update(){//some code
}
delete(){//some code
}
}
}
Now I want to dependency inject this into a Service controller. I'm looking at constructor dependency injection here. If I pass SQL implementation as dependency in controller constructor as below and add this in startup class->ConfigureServie method, how to pass it's settings?
public class HomeController : APIController
{
private readonly IDataAccess _dataAccess;
public HomeController(IDataAccess dataAccess)
{
_dataAccess= dataAccess;
}
public IActionResult Index()
{
//do something and set result
return Ok(result);
}
}
Below is my ConfigureService method in startup.cs class
public void ConfigureServices(IServiceCollection services)
{
// Add application services.
services.AddSingleton<IDataAccess, SQLAccess>();
}
Question is, How do I pass the constructor parameters and property values in API's Configure Service method?
public void ConfigureServices(IServiceCollection services)
{
// Add application services.
services.AddSingleton<IDataAccess, SQLAccess>(opt=> { new SQLAccess(new
Settings(){
//Set Settings object properties here
});
});
}
Is it good to resolve the dependencies dynamically like the way i'm doing. Everywhere, it is suggested to use Constructor injection. I really don't understand the drawbacks of doing it the way i'm doing it. Code snippets as below..
Employee.cs
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public Department Department { get; set; }
}
IRepository.cs
public interface IRepository<TModel> where TModel : class
{
void Add();
IEnumerable<TModel> GetAll();
IEnumerable<TModel> GetByID();
}
Repository.cs
public class Repository<TModel> : IRepository<TModel> where TModel : class
{
public void Add()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetAll()
{
throw new NotImplementedException();
}
public IEnumerable<TModel> GetByID()
{
throw new NotImplementedException();
}
}
EmployeeController.cs
public class HomeController : ApiController
{
IComponentContext _container;
public HomeController(IComponentContext container)
{
this._container = container;
}
public Repository<TModel> Using<TModel>() where TModel :class
{
var repository = _container.Resolve(typeof(IRepository<TModel>));
return repository as Repository<TModel>;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return Using<Employee>().GetAll();
}
}
Global.asax
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>));
var container = builder.Build(Autofac.Builder.ContainerBuildOptions.None);
var webApiResolver = new AutofacWebApiDependencyResolver(container);
GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
}
Say i've 5 repositories, Constructor injection will resolve all the 5 dependencies for a request i make. I might not use 5 repositories for each and every request. SO i thought of resolving dependencies dynamically by passing the type like i'm doing it in Using<TModel>(). Any suggestions would be appreciated..!! Thank you...!!
Refrain from using the container directly inside your application components; this leads to all kinds of troubles such as maintainability and testability issues. Directly resolving instances from within application code is a well-known anti-pattern known as Service Locator.
As a first refactoring, you can instead apply the Unit of Work pattern. A Unit of Work allows access to underlying repositories. For instance:
public interface IUnitOfWork
{
IRepository<TModel> Repository<TModel>();
}
public sealed class HomeController : ApiController
{
private readonly IUnitOfWork _unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
this._unitOfWork = unitOfWork;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._unitOfWork.Repository<Employee>().GetAll();
}
}
Within the Composition Root (where it is allowed to access the container), we can now create an IUnitOfWork implementation that resolves repositories dynamically:
private sealed class AutofacUnitOfWork : IUnitOfWork
{
private readonly IComponentContext _container;
public AutofacUnitOfWork(IComponentContext container)
{
this._container = container;
}
public IRepository<TModel> Repository<TModel>()
{
return _container.Resolve<IRepository<TModel>>();
}
}
This pattern simplifies your application components considerably and prevents downsides that the Service Locator anti-pattern typically causes.
Although applying the Unit of Work pattern might be a useful step into the right direction, an even better approach is to skip the Unit of Work directly and simply inject a required repository directly into application components:
public sealed class HomeController : ApiController
{
private readonly IRepository<Employee> _employeeRepository;
public HomeController(IRepository<Employee> employeeRepository)
{
this._employeeRepository = employeeRepository;
}
[HttpGet]
public IEnumerable<Employee> GetEmployees()
{
return this._employeeRepository.GetAll();
}
}
Say i've 5 repositories, Constructor injection will resolve all the 5 dependencies for a request i make. I might not use 5 repositories for each and every request.
Note that from a performance perspective, you should typically not be concerned whether dependencies are used or not. Autofac is in most cases fast enough and it is unlikely that this will actually cause any performance problems in your production systems.
From a design perspective however you should be more worried if a class has many dependencies, while methods just use a few of them. This means that the methods in the class have little cohesion. This is an indication that the class should be split up into multiple smaller classes; it has multiple responsibilities.
I have my controller like this
public class MyController : Controller
{
private IEntityRepository accountsRepo;
private IEntityRepository dataRepo;
public MyController(IEntityRepository accs, IEntityRepository data)
{
accountsRepo = accs;
dataRepo = data;
}
.....
}
And I installed container this way:
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IEntityRepository>()
.ImplementedBy<AccountsRepository>()
.Named("accs")
.LifestyleTransient(),
Component.For<IEntityRepository>()
.ImplementedBy<DataRepository>()
.Named("data")
.LifestyleTransient());
}
}
Also I have facilities setted up:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<DbContext>()
.ImplementedBy<AccountsContext>()
.LifestylePerWebRequest(),
Component.For<DbContext>()
.ImplementedBy<DataContext>()
.LifestylePerWebRequest());
}
}
}
...and installed:
public class PersistenceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.AddFacility<PersistenceFacility>();
}
}
So when I'm using my controller both parameters are injected with AccountsRepository instance (which was registered first). Of course I wanna see "data" being DataRepository respectively. Please, explain me proper way to deal with this kind of injection.
EDIT
As #roman suggested I have implemented generic repositories:
public interface IRepository : IDisposable
{
void SaveChanges();
void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams);
}
public interface IEntityRepository<T> : IRepository
{
T Context { get; set; }
DbSet<TEntity> Set<TEntity>() where TEntity : class;
}
public class AccountsRepository : IEntityRepository<AccountsContext>
{
public AccountsContext Context { get; set; }
public AccountsRepository(AccountsContext c)
{
Context = c;
}
public DbSet<TEntity> Set<TEntity>() where TEntity : class
{
return Context.Set<TEntity>();
}
public virtual void ExecuteProcedure(String procedureCommand, params SqlParameter[] sqlParams)
{
Context.Database.ExecuteSqlCommand(procedureCommand, sqlParams);
}
public virtual void SaveChanges()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
Context.Dispose();
}
}
DataRepository looks the same way, my be at some point I will decide to have just one concrete class EntityRepository, but it not relevant to exceptions I receiving.
So after cosmetic interfaces changes my contreller become:
public class HomeController : Controller
{
private IEntityRepository<AccountsContext> accountsRepo;
private IEntityRepository<DataContext> dataRepo;
public HomeController(IEntityRepository<AccountsContext> accs, IEntityRepository<DataContext> data)
{
accountsRepo = accs;
dataRepo = data;
}
....
}
Also I have changed installer code:
container.Register(
Component.For<IEntityRepository<AccountsContext>>()
.ImplementedBy<AccountsRepository>()
.LifestyleTransient(),
Component.For<IEntityRepository<DataContext>>()
.ImplementedBy<DataRepository>()
.LifestyleTransient());
And now during controller resolving proccess
return (IController) kernel.Resolve(controllerType);
I catching
Can't create component 'MyMVCProj.DAL.AccountsRepository' as it has dependencies to be satisfied.
'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
Castle.MicroKernel.Handlers.HandlerException: Can't create component 'MyMVCProj.DAL.AccountsRepository' as it has dependencies to be satisfied.
'MyMVCProj.DAL.AccountsRepository' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
But I have installed AccountsContext in facility logic.
EDIT++
According to #Roman suggestion I have tweaked my facility this way:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<DbContext>()
.ImplementedBy<AccountsContext>()
.Named("accctx")
.LifestylePerWebRequest(),
Component.For<DbContext>()
.ImplementedBy<DataContext>()
.Named("datactx")
.LifestylePerWebRequest());
}
}
and also repositories installler:
public class RepositoriesInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IEntityRepository<AccountsContext>>()
.ImplementedBy<AccountsRepository>()
.Named("accs")
.LifestyleTransient()
.DependsOn(Dependency.OnComponent(typeof (DbContext), "accctx")),
Component.For<IEntityRepository<DataContext>>()
.ImplementedBy<DataRepository>()
.Named("data")
.LifestyleTransient()
.DependsOn(Dependency.OnComponent(typeof (DbContext), "datactx")));
}
}
This is the exception I get now:
Can't create component 'accs' as it has dependencies to be satisfied.
'accs' is waiting for the following dependencies:
- Service 'MyMVCProj.DAL.AccountsContext' which was not registered.
But trying to solve this brute forcing the code I ended with working solution, just installing concrete implementations of DBContext:
public class PersistenceFacility : AbstractFacility
{
protected override void Init()
{
Kernel.Register(
Component.For<AccountsContext>().LifestylePerWebRequest(),
Component.For<DataContext>().LifestylePerWebRequest());
}
}
And kernel's components now are:
AccountsContext PerWebRequest
AccountsRepository / IEntityRepository<AccountsContext> Transient
DataContext PerWebRequest
DataRepository / IEntityRepository<DataContext> Transient
And before they were:
AccountsContext / DbContext PerWebRequest
AccountsRepository / IEntityRepository<AccountsContext> Transient
DataContext / DbContext PerWebRequest
DataRepository / IEntityRepository<DataContext> Transient
So the new questions are:
Have I did all stuff idiomatically?
Why this behaviour - there already was AccountContext with little mention of it dependencies.
The fact that you expect two instances of same interface, yet you require different behavior for them (by injecting them to two different parameters), implies - in my opinion - that they shouldn't be the same interface, because they have different roles, or responsibilities. It would make sense to me more, if IEntityRepository was a generic class and then you would require in MyController two different generic interface types:
public class MyController(IEntityRepository<Account> acc, IEntityRepository<Data> data)
Nevertheless, If you still want to do that kind of thing, I suggest you use a CollectionResolver that will allow MyController class to get an IEnumerable. That way you'll get both instances, but it'll be up to you to select the appropriate one to use depending on your needs, which I'll stress again, I think is the wrong approach for this.
To use CollectionResolver you need to register it with the Windsor container like this:
var container = new WindsorContainer();
container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel));
And then, MyController will look like this:
public class MyController(IEnumerable<IEntityRepository> repositories)
{
accountsRepo = repositories.Where(...);
dataRepo = repositories.Where(...);
}