I'm trying to inject dependencies into a heirarchy with Ninject and I have a question about scoping in deferred injection. I have a basic hierarchical structure containing a parent and a child. The child gets injected into the parent and they both get injected with a "Coat of Arms" property:
public class Parent
{
[Inject]
public Child Child { get; set; }
[Inject]
public CoatOfArms CoatOfArms { get; set; }
}
public class Child
{
[Inject]
public CoatOfArms CoatOfArms { get; set; }
}
public class CoatOfArms
{
}
Since they're in the same family they should both get the same coat of arms, so I set up my bindings to scope them to the CoastOfArms in the parent request:
public class FamilyModule : NinjectModule
{
public override void Load()
{
Bind<Parent>().ToSelf();
Bind<Child>().ToSelf();
Bind<CoatOfArms>().ToSelf().InScope(ctx =>
{
var request = ctx.Request;
if (typeof(Parent).IsAssignableFrom(request.Service))
return request;
while ((request = request.ParentRequest) != null)
if (typeof(Parent).IsAssignableFrom(request.Service))
return request;
return new object();
});
}
}
This all works fine, but let's say I want to change it slightly so that the child is injected later, well after the parent has been injected. I remove the Inject attribute on the Child property, inject the kernel and use it to inject the child in a method:
[Inject]
public IKernel Kernel { get; set; }
public Child Child { get; set; }
public void InjectChild()
{
this.Child = this.Kernel.Get<Child>();
}
This breaks because it's a completely new request and the walk up the request tree stops with this request. I could manually pass in the CoatOfArms as a property but then I would have to remember to do that everywhere else in the code that tries to create a child object. Also the child class may have children and grandchildren of its own etc so I would wind up having to manually pass parameters all down the hierarchy chain again thus losing all the benefits of dependency injection scoping to begin with.
Is there a way to create my child object and somehow link the request to the parent request so that scoping works as if the child had been injected at the same time as the parent?
Use ninject.extensions.contextpreservation (https://github.com/ninject/ninject.extensions.contextpreservation/wiki, available as nuget package) and then change
public void InjectChild()
{
this.Child = this.Kernel.Get<Child>();
}
to
public void InjectChild()
{
this.Child = this.Kernel.ContextPreservingGet<Child>();
}
(the .ContextPreservingGet<Child>() is an extension and may thus need a using Some.Name.Space; to be recognized).
By the way, you could also change your custom scoping to the following:
public class FamilyModule : NinjectModule
{
private const string FamilyScopeName = "FamilyScopeName";
public override void Load()
{
Bind<Parent>().ToSelf().DefinesNamedScope(FamilyScopeName);
Bind<Child>().ToSelf();
Bind<CoatOfArms>().ToSelf().InNamedScope(FamilyScopeName);
}
}
This requires: https://github.com/ninject/ninject.extensions.namedscope (also available as nuget package)
EDIT: you might also need to change from IKernel to IResolutionRoot for resolving.
Related
I am building the asp.net WebAPI project and have used Autofac as IOC. Now i am doing constructor based injection and calls the various methods of the business class from the controller class.
Now i want to pass some additional data into the business class via the public property IncomingUser such as this one :-
My interface looks like this :-
public interface IUserManager
{
string IncomingUser { set; get; }
Task<List<String>> GetUserPofiles(string Name);
}
This readonly property IncomingUser will be used inside various methods defined under the class UserManager.
public class UserManager : IUserManager
{
public string IncomingUser { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public async Task<List<String>> GetUserPofiles(string Name)
{
......Business Logic for the method......
}
}
From the API controller, i am setting the DI like this :-
public class myAPIController : ApiController
{
IUserManager _Manager;
public myAPIController(IUserManager Mang)
{
_Manager = Mang;
}
}
Please suggest, how should i set the property IncomingUser from the API controller class with the help of autofac DI or another way.
In broader terms what I am trying to achieve with Autofac is to pass the dependant (a.k.a. parent) object to its dependencies.
For example:
interface IDependency {}
class Dependant
{
IDependency Dependency { get; set; }
}
class ConcreteDependency : IDependency
{
ConcreteDependency(Dependant dependant) { /* ... */ }
}
I am hoping this could work, because Dependant breaks the dependency loop using property injection (meaning you can create an instance of Dependant, before having to resolve IDependency). Whilst, if both classes used ctor-injection this wouldn't be possible.
Specifically, I am trying to inject the current ASP.NET MVC controller instance to one of its dependencies.
Take a look at:
public abstract class ApplicationController : Controller
{
public ILogger Logger { get; set;}
}
public class SomeController : ApplicationController
{
[HttpPost]
public ActionResult Create(FormCollection formData)
{
// something fails...
this.Logger.Log("Something has failed.");
}
}
public interface ILogger
{
public void Log(string message);
}
public class TempDataLogger : ILogger
{
private ControllerBase controller;
public NullLogger(ControllerBase controller)
{
this.controller = controller;
}
public void Log(string message)
{
this.controller.TempData["Log"] = message;
}
}
In plain English the above code uses TempData as a way of "logging" messages (maybe to print it out in a nice way in view-layout or something...).
Simple enough all controllers are registered in Autofac:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired(); // not strictly necessary
But then, how can I tweak the ILogger registration below to make it work?
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();
Is this even possible in Autofac?
Thank you.
In case anyone else is interested, the solution below is the closest I was able to get so far:
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.PropertiesAutowired() // not strictly necessary
.OnActivating(e => ((ApplicationController)e.Instance).Logger = new TempDataLogger((ApplicationController)e.Instance));
... and therefore, no need to;
builder.RegisterType<TempDataLogger>()
.As<ILogger>()
.InstancePerRequest();
Previously I have had parameterless repositories being injected into my MVC controllers:
ProjectRepository implementation:
public class ProjectRepository : EntityFrameworkRepository<Project>, IProjectRepository
{
public ProjectRepository()
{ }
}
UnityConfig.cs dependency resolution:
container.RegisterType<IProjectRepository, ProjectRepository>();
MVC Controller:
private IProjectRepository _projectRepository { get; set; }
public ProjectController(IProjectRepository projectRepository)
{
_projectRepository = projectRepository;
}
This worked great.
Now I have implemented a Unit of Work pattern into my repository classes so that I can commit transactional changes to data (especially when changes are being made to more than one repository).
The new ProjectRepository implementation accepts a IUnitOfWork in its constructor:
public class ProjectRepository : EntityFrameworkRepository<Project>, IProjectRepository
{
public ProjectRepository(IUnitOfWork unitOfWork): base(unitOfWork)
{ }
}
This means that multiple repositories can share the same IUnitOfWork and changes can be collectively committed using UnitOfWork.SaveChanges().
QUESTION:
How do I now use dependency injection to instantiate the repository with an instance of IUnitOfWork?
public ProjectController(IProjectRepository projectRepository, IUnitOfWork unitOfWork)
{
_projectRepository = projectRepository;
_unitOfWork = unitOfWork;
}
There could also be more than one repository injected into the controller. How can these all be instantiated with the same IUnitOfWork?
When you register your IUnitOfWork instance, use PerResolveLifetimeManager, this will ensure every dependency of IUnitOfWork within a single IUnityContainer.Resolve gets provided the same instance.
For example:
public class SomeDependency
{
}
public class Service
{
public Service(SomeDependency someDependency, SomeDependency someDependency2)
{
Console.WriteLine(someDependency == someDependency2);
}
}
public static void Main()
{
using(var container = new UnityContainer())
{
container.RegisterType<SomeDependency>(new PerResolveLifetimeManager());
container.Resolve<Service>();
}
}
This will output True to the Console.
See the page for Understanding Lifetime Managers for further details.
In a MVC3-application with Ninject.MVC 2.2.0.3 (after merge), instead of injecting repostories directly into controllers I'm trying to make a service-layer that contain the businesslogic and inject the repostories there. I pass the ninject-DependencyResolver to the service-layer as a dynamic object (since I don't want to reference mvc nor ninject there). Then I call GetService on it to get repositories with the bindings and lifetimes I specify in NinjectHttpApplicationModule. EDIT: In short, it failed.
How can the IoC-container be passed to the service-layer in this case? (Different approaches are also very welcome.)
EDIT: Here is an example to illustrate how I understand the answer and comments.
I should avoid the service locator (anti-)pattern and instead use dependency injection. So lets say I want to create an admin-site for Products and Categories in Northwind. I create models, repositories, services, controllers and views according to the table-definitions. The services call directly to the repositories at this point, no logic there. I have pillars of functionality and the views show raw data. These bindings are configured for NinjectMVC3:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ICategoryRepository>().To<CategoryRepository>();
kernel.Bind<IProductRepository>().To<ProductRepository>();
}
Repository-instances are created by ninject via two layers of constructor injection, in the ProductController:
private readonly ProductsService _productsService;
public ProductController(ProductsService productsService)
{
// Trimmed for this post: nullchecks with throw ArgumentNullException
_productsService = productsService;
}
and ProductsService:
protected readonly IProductRepository _productRepository;
public ProductsService(IProductRepository productRepository)
{
_productRepository = productRepository;
}
I have no need to decouple the services for now but have prepared for mocking the db.
To show a dropdown of categories in Product/Edit I make a ViewModel that holds the categories in addition to the Product:
public class ProductViewModel
{
public Product Product { get; set; }
public IEnumerable<Category> Categories { get; set; }
}
The ProductsService now needs a CategoriesRepository to create it.
private readonly ICategoryRepository _categoryRepository;
// Changed constructor to take the additional repository
public ProductsServiceEx(IProductRepository productRepository,
ICategoryRepository categoryRepository)
{
_productRepository = productRepository;
_categoryRepository = categoryRepository;
}
public ProductViewModel GetProductViewModel(int id)
{
return new ProductViewModel
{
Product = _productRepository.GetById(id),
Categories = _categoryRepository.GetAll().ToArray(),
};
}
I change the GET Edit-action to return View(_productsService.GetProductViewModel(id)); and the Edit-view to show a dropdown:
#model Northwind.BLL.ProductViewModel
...
#Html.DropDownListFor(pvm => pvm.Product.CategoryId, Model.Categories
.Select(c => new SelectListItem{Text = c.Name, Value = c.Id.ToString(), Selected = c.Id == Model.Product.CategoryId}))
One small problem with this, and the reason I went astray with Service Locator, is that none of the other action-methods in ProductController need the categories-repository. I feel it's a waste and not logical to create it unless needed. Am I missing something?
You don't need to pass the object around you can do something like this
// global.aspx
protected void Application_Start()
{
// Hook our DI stuff when application starts
SetupDependencyInjection();
}
public void SetupDependencyInjection()
{
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new NinjectDependencyResolver(CreateKernel()));
}
protected IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NhibernateModule(),
new ServiceModule(),
new RepoModule()
};
return new StandardKernel(modules);
}
So in this one I setup all the ninject stuff. I make a kernal with 3 files to split up all my binding so it is easy to find.
In my service layer class you just pass in the interfaces you want. This service class is in it's own project folder where I keep all my service layer classes and has no reference to the ninject library.
// service.cs
private readonly IRepo repo;
// constructor
public Service(IRepo repo)
{
this.repo = repo;
}
This is how my ServiceModule looks like(what is created in the global.aspx)
// ServiceModule()
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IRepo>().To<Repo>();
}
}
Seee how I bind the interface to the repo. Now every time it see that interface it will automatically bind the the Repo class to it. So you don't need to pass the object around or anything.
You don't need worry about importing .dll into your service layer. For instance I have my service classes in their own project file and everything you see above(expect the service class of course) is in my webui project(where my views and global.aspx is).
Ninject does not care if the service is in a different project since I guess it is being referenced in the webui project.
Edit
Forgot to give you the NinjectDependecyResolver
public class NinjectDependencyResolver : IDependencyResolver
{
private readonly IResolutionRoot resolutionRoot;
public NinjectDependencyResolver(IResolutionRoot kernel)
{
resolutionRoot = kernel;
}
public object GetService(Type serviceType)
{
return resolutionRoot.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return resolutionRoot.GetAll(serviceType);
}
}
I'm a newbie when it comes to DI and ninject and I'm struggling a bit
about when the actual injection should happen and how to start the
binding.
I'm using it already in my web application and it working fine there,
but now I want to use injection in a class library.
Say I have a class like this:
public class TestClass
{
[Inject]
public IRoleRepository RoleRepository { get; set; }
[Inject]
public ISiteRepository SiteRepository { get; set; }
[Inject]
public IUserRepository UserRepository { get; set; }
private readonly string _fileName;
public TestClass(string fileName)
{
_fileName = fileName;
}
public void ImportData()
{
var user = UserRepository.GetByUserName("myname");
var role = RoleRepository.GetByRoleName("myname");
var site = SiteRepository.GetByID(15);
// Use file etc
}
}
I want to use property injection here because I need to pass in a
filename in my constructor. Am I correct in saying that if I need to
pass in a constructor parameter, I cannot use constructor injection?
If I can use constructor injection with additional parameters, how do
I pass those parameters in?
I have a console app that consumes by Test class that looks as
follows:
class Program
{
static void Main(string[] args)
{
// NinjectRepositoryModule Binds my IRoleRepository etc to concrete
// types and works fine as I'm using it in my web app without any
// problems
IKernel kernel = new StandardKernel(new NinjectRepositoryModule());
var test = new TestClass("filename");
test.ImportData();
}
}
My problem is that when I call test.ImportData() my repositories are null - nothing has been injected into them. I have tried creating another module and calling
Bind<TestClass>().ToSelf();
as I thought this might resolve all injection properties in TestClass but I'm getting nowhere.
I'm sure this is a trivial problem, but I just can't seem to find out
how to go about this.
You are directly newing TestClass, which Ninject has no way of intercepting - remember there's no magic like code transformation intercepting your news etc.
You should be doing kernel.Get<TestClass> instead.
Failing that, you can inject it after you new it with a kernel.Inject( test);
I think there's an article in the wiki that talks about Inject vs Get etc.
Note that in general, direct Get or Inject calls are a Doing It Wrong smell of Service Location, which is an antipattern. In the case of your web app, the NinjectHttpModule and PageBase are the hook that intercepts object creation - there are similar interceptors / logical places to intercept in other styles of app.
Re your Bind<TestClass>().ToSelf(), generally a StandardKernel has ImplicitSelfBinding = true which would make that unnecessary (unless you want to influence its Scope to be something other than .InTransientScope()).
A final style point:- you're using property injection. There are rarely good reasons for this, so you should be using constructor injection instead.
And do go buy Dependency Injection in .NET by #Mark Seemann, who has stacks of excellent posts around here which cover lots of important but subtle considerations in and around the Dependency Injection area.
OK,
I've found out how to do what I need, thanks in part to your comments Ruben. I've created a new module that basically holds the configuration that I use in the class library. Within this module I can either Bind using a placeholder Interface or I can add a constructor parameter to the CustomerLoader.
Below is the code from a dummy console app to demonstrating both ways.
This might help someone else getting started with Ninject!
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ninject.Core;
using Ninject.Core.Behavior;
namespace NinjectTest
{
public class Program
{
public static void Main(string[] args)
{
var kernel = new StandardKernel(new RepositoryModule(), new ProgramModule());
var loader = kernel.Get<CustomerLoader>();
loader.LoadCustomer();
Console.ReadKey();
}
}
public class ProgramModule : StandardModule
{
public override void Load()
{
// To get ninject to add the constructor parameter uncomment the line below
//Bind<CustomerLoader>().ToSelf().WithArgument("fileName", "string argument file name");
Bind<LiveFileName>().To<LiveFileName>();
}
}
public class RepositoryModule : StandardModule
{
public override void Load()
{
Bind<ICustomerRepository>().To<CustomerRepository>().Using<SingletonBehavior>();
}
}
public interface IFileNameContainer
{
string FileName { get; }
}
public class LiveFileName : IFileNameContainer
{
public string FileName
{
get { return "live file name"; }
}
}
public class CustomerLoader
{
[Inject]
public ICustomerRepository CustomerRepository { get; set; }
private string _fileName;
// To get ninject to add the constructor parameter uncomment the line below
//public CustomerLoader(string fileName)
//{
// _fileName = fileName;
//}
public CustomerLoader(IFileNameContainer fileNameContainer)
{
_fileName = fileNameContainer.FileName;
}
public void LoadCustomer()
{
Customer c = CustomerRepository.GetCustomer();
Console.WriteLine(string.Format("Name:{0}\nAge:{1}\nFile name is:{2}", c.Name, c.Age, _fileName));
}
}
public interface ICustomerRepository
{
Customer GetCustomer();
}
public class CustomerRepository : ICustomerRepository
{
public Customer GetCustomer()
{
return new Customer() { Name = "Ciaran", Age = 29 };
}
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
}