I have a custom ASP.NET MVC controller that retrieves operations from the user service. I want to pass the operations property to the scenario service using dependency injection.
public abstract class BaseController : Controller {
protected IUserService userService;
public OperationWrapper operations { get; private set; }
public BaseController(IUserService userService) {
this.userService = userService;
this.operations = userService.GetOperations(HttpContext.Current.User.Identity.Name);
}
}
public abstract class ScenarioController : BaseController {
protected IScenarioService scenarioService;
public ScenarioController(IScenarioService scenarioService, IUserService userService)
: base(userService) {
this.scenarioService = scenarioService;
}
}
public class ScenarioService : IScenarioService {
private OperationWrapper operations;
public ScenarioService(OperationWrapper operations) {
this.repo = repo;
this.operations = operations;
}
}
Here is my Windsor installer.
public class Installer : IWindsorInstaller {
public void Install(IWindsorContainer container, IConfigurationStore store) {
container.Register(Classes.FromThisAssembly()
.BasedOn<IController>());
container.Register(Classes.FromThisAssembly()
.Where(x => x.Name.EndsWith("Service"))
.WithService.DefaultInterfaces()
.LifestyleTransient());
}
}
I pretty sure I've done something similar with Ninject a couple of years back. What do I need to add to the installer in order to make this work? Is it even possible?
There are a few of options here:
1. Use LifeStylePerWebRequest() and UsingFactoryMethod()
First, you could register an OperationWrapper as LifestylePerWebRequest() and inject it into both the BaseController and ScenarioService. Windsor will let you register the dependency with a factory method for creating it, which can in turn call other services which have been registered.
container.Register(Component.For<OperationWrapper>()
.LifestylePerWebRequest()
.UsingFactoryMethod(kernel =>
{
var userService = kernel.Resolve<IUserService>();
try
{
return userService.GetOperations(
HttpContext.Current.User.Identity.Name);
}
finally
{
kernel.ReleaseComponent(userService);
}
}));
So, every time Windsor is asked for an OperationWrapper, it will run that call against an instance if IUserService, giving it the Name of the current User. By binding the lifestyle to LifestylePerWebRequest(), you can verify that each request will get its own instance of the OperationWrapper and it won't bleed across requests.
(The only edge case you'd run into is one where a user becomes authenticated mid-request and the OperationWrapper needs to be adjusted as a result. If that's a normal-path use case, this may need some re-thinking.)
Then, modify your base controller to take that registered object in as a dependency:
public abstract class BaseController : Controller {
protected IUserService userService;
protected OperationWrapper operations;
public BaseController(IUserService userService, OperationWrapper operations) {
this.userService = userService;
this.operations = operations;
}
}
2. Use Method Injection
It looks like OperationWrapper is some sort of context object, and those can sometimes be injected into the method instead of into the constructor.
For instance, if your method was:
int GetTransactionId() { /* use OperationWrapper property */ }
You could just modify the signature to look like:
int GetTransactionId(OperationWrapper operations) { /* use arg */ }
In this situation, it makes sense to use it if a small-ish subset of your service's methods use that dependency. If the majority (or totality) of methods need it, then you should probably go a different route.
3. Don't use DI for OperationWrapper at all
In situations where you have a highly-stateful contextual object (which it seems like your OperationWrapper is), it frequently just makes sense to have a property whose value gets passed around. Since the object is based on some current thread state and is accessible from everywhere in any subclassed Controller, it may be right to just keep the pattern you have.
If you can't answer the question "What am I unable to do with OperationWrapper now that DI is going to solve for me?" with anything but "use the pattern/container," this may be the option for this particular situation.
You should set dependency resolver in Application_Start method of global.asax
System.Web.MVC.DependencyResolver.SetResolver(your windsor resolver)
Create a class that inherits from DefaultControllerFactory. Something like this will do:
public class WindsorControllerFactory : DefaultControllerFactory
{
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(
404,
String.Format(
CultureInfo.CurrentCulture,
"The controller for path '{0}' was not found or does not implement IController.",
requestContext.HttpContext.Request.Path
)
);
}
return (IController)_kernel.Resolve(controllerType);
}
public override void ReleaseController(IController controller)
{
Kernel.ReleaseComponent(controller);
}
private readonly IKernel _kernel;
private IKernel Kernel
{
get { return _kernel; }
}
}
In the Application_Start method of your MvcApplication class add the following:
var container = new WindsorContainer();
container.Install(FromAssembly.This());
ControllerBuilder.Current.SetControllerFactory(
new WindsorControllerFactory(container.Kernel)
);
This should work with your existing installer and get you to the point where Windsor will start resolving your dependencies for you. You might have to fill-in a few gaps, but you'll get the point.
I've borrowed heavily from: https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-intro.md
Be wary of using IDependencyResolver as it doesn't make provision for releasing what's resolved.
Related
I have a service layer with the following classes / intefaces (IServices is an empty interface):
public interface IForoChanService<T> : IService
{
T GetById(int id);
IQueryable SearchBy(Expression<Func<T, bool>> predicate);
IEnumerable<T> GetAll();
int Create(T entity);
void CreateMany(IEnumerable<T> entities);
void Delete(T entity);
void Delete(int id);
void DeleteMany(IEnumerable<T> entities);
void Update(T entity);
}
Then I have an abstract class implementing that signature generically:
public abstract class ForoChanServiceBase<T> : IForoChanService<T> where T : EntityBase
{
public T GetById(int id)
{
return ChanDbContext.Set<T>().Find(id);
}
//all the other methods as well
}
And finally the concrete classes:
public class CategoryService : ForoChanServiceBase<Category>
{
}
I am trying to use AutoFac to inject those services (many: category, client, etc) in the constructor: I have a base controller:
public abstract class ForoChanBaseController: Controller
{
protected ForoChanServiceBase<Post> PostService { get; private set; }
protected ForoChanServiceBase<Comment> CommentService { get; private set; }
protected ForoChanServiceBase<Category> CategoryService { get; private set; }
protected ForoChanBaseController()
{
}
protected ForoChanBaseController(
ForoChanServiceBase<Post> postService,
ForoChanServiceBase<Comment> commentService,
ForoChanServiceBase<Category> categoryService)
{
PostService = postService;
CommentService = commentService;
CategoryService = categoryService;
}
}
And I am setting autofac like this:
public static void ConfigureIoc()
{
var builder = new ContainerBuilder();
builder.RegisterType<CommentService>().As<ForoChanServiceBase<Comment>>().InstancePerRequest();
builder.RegisterType<CategoryService>().As<ForoChanServiceBase<Category>>().InstancePerRequest();
builder.RegisterType<PostService>().As<ForoChanServiceBase<Post>>().InstancePerRequest();
builder.Build();
}
The problem is that I am having is when in the controller I need to use any service method that guy (CategoryService) is null:
public ActionResult Create()
{
var p = new PostFormNewVm
{
Categories = CategoryService.GetAll().Select(c => new CategoryVm { Id = c.Id, Title = c.Title })
};
return View(p);
}
Besides this error do am I doing something wrong? I can't make it work.
I tried with the inteface as well.
Your ForoChanBaseController contains multiple constructors, which is an anti-pattern. Because of the existence of this default constructor, there is a derived class that uses this constructor instead of the overloaded one, which is causing the dependencies to be null.
Although this default ctor is the cause for you to post the question here, there are more -less obvious problems- with your design:
Although you can remove the default constructor, prevent having this base class at all. Bases classes are often big Single Responsibility Principle violations and are either used to stuff in cross-cutting concerns or other utility functions. By having this base class derived types are forced to require dependencies that they might not even use at all. This complicates your code and complicates testing.
Since you have the IForoChanService<T> interface, consumers should not depend on the ForoChanServiceBase base class. As a matter of fact, the same advise as before holds: this base class should probably not exist at all.
The IForoChanService<T> is big generic tool box of methods where consumers only use one or two of those methods at a time. This means you are violating the Interface Segregation Principle.
IForoChanService<T> implementations are likely to violate the Liskov Substitution Principle, since there will be implementations that don't allow entities to be deleted. This will cause call to Delete to fail with an exception, instead of the Delete to not exist for that entity.
I have a controller and I want to use Dependency Injection with constructor,this is my code
private readonly IHomeService _iHomeService;
public HomeController(IHomeService iHomeService)
{
_iHomeService = iHomeService;
}
public HomeController()
{
}
When I remove Constructor without any parameter(Second Constructor),I see this error :
No parameterless constructor defined
and When I use Constructor without any parameter,I see my private field is null(_iHomeService = null) because program use constructor without parameter.
How can I resolve this problem for Dependency Injection?
Well, to do dependency injection youll need to either use a framework or use controller factory .
try ninject
public class HomeController : Controller
{
private readonly IWelcomeMessageService welcomeMessageService;
public HomeController(IWelcomeMessageService welcomeMessageService)
{
this.welcomeMessageService = welcomeMessageService;
}
public void Index()
{
ViewModel.Message = this.welcomeMessageService.TodaysWelcomeMessage;
return View();
}
}
public class WelcomeMessageServiceModule : NinjectModule
{
public override void Load()
{
this.Bind<IWelcomeMessageService>().To<WelcomeMessageService>();
}
}
The framework will take control on the controller instance creation and pass the constractor params
It sounds like you are expecting, automatically, the HomeService class to be instantiated and injected into the Controller.
Using an IoC framework like Ninject or StructureMap will do that for you - once you've set it up.
If you don't want to use an IoC framework, you'll need to manually instantiate the HomeService in your constructor.
ASP.NET uses a ControllerFactory to instantiate your controllers on-demand. This class requires that your controller has a parameterless constructor that it can use to create an instance of it.
You'll need to use a dependency injection framework to create your controllers and inject the required dependencies. ASP.NET has some dependency injection capability, but I understand that it is flawed. I suggest using Castle Windsor to manage your dependency injection. It integrates very well with ASP.NET, and there's a tutorial on integrating it here.
If you go down this route, you'd end up with an installer for your controllers and service:
public class Installer : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<IController>().LifestyleTransient());
container.Register(Component.For<IHomeService>.ImplementedBy<HomeService>());
}
}
..and a new ControllerFactory to create them:
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path));
}
return (IController) _kernel.Resolve(controllerType);
}
}
Finally, you'd create a container and set a new controller factory:
var container = new WindsorContainer().Install(new Installer());
var controllerFactory = new WindsorControllerFactory(_container.Kernel);
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
You could also use Ninject or StructureMap.
I'm using NInject with NInject.Web.Mvc.
To start with, I've created a simple test project in which I want an instance of IPostRepository to be shared between a controller and a custom model binder during the same web request. In my real project, I need this because I'm getting IEntityChangeTracker problems where I effectively have two repositories accessing the same object graph. So to keep my test project simple, I'm just trying to share a dummy repository.
The problem I'm having is that it works on the first request and that's it. The relevant code is below.
NInjectModule:
public class PostRepositoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IPostRepository>().To<PostRepository>().InRequestScope();
}
}
CustomModelBinder:
public class CustomModelBinder : DefaultModelBinder
{
[Inject]
public IPostRepository repository { get; set; }
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
repository.Add("Model binder...");
return base.BindModel(controllerContext, bindingContext);
}
}
public class HomeController : Controller
{
private IPostRepository repository;
public HomeController(IPostRepository repository)
{
this.repository = repository;
}
public ActionResult Index(string whatever)
{
repository.Add("Action...");
return View(repository.GetList());
}
}
Global.asax:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}
Doing it this way is actually creating 2 separate instances of IPostRepository rather than the shared instance. There's something here that I'm missing with regards to injecting a dependency into my model binder. My code above is based on the first setup method described in the NInject.Web.Mvc wiki but I have tried both.
When I did use the second method, IPostRepository would be shared only for the very first web request, after which it would default to not sharing the instance. However, when I did get that working, I was using the default DependencyResolver as I couldn't for the life of me figure out how to do the same with NInject (being as the kernel is tucked away in the NInjectMVC3 class). I did that like so:
ModelBinders.Binders.Add(typeof(string),
DependencyResolver.Current.GetService<CustomModelBinder>());
I suspect the reason this worked the first time only is because this isn't resolving it via NInject, so the lifecycle is really being handled by MVC directly (although that means I have no idea how it's resolving the dependency).
So how do I go about properly registering my model binder and getting NInject to inject the dependency?
The ModelBinders are reused by MVC for multiple requests. This means they have a longer lifecycle than request scope and therefore aren't allowed to depend on objects with the shorter request scope life cycle.
Use a Factory instead to create the IPostRepository for every execution of BindModel
It's actually really simple to get the Ninject factory extension up and running, but that wasn't clear to me from the existing answers.
The factory extensions plugin is a prerequisite, which can be installed via NUGet:
Install-Package Ninject.Extensions.Factory
You just need the factory injected into your model binder somewhere, eg:
private IPostRepositoryFactory _factory;
public CustomModelBinder(IPostRepositoryFactory factory) {
_factory = factory;
}
Then create an interface for the factory. The name of the factory and the name of the method doesn't actually matter at all, just the return type. (Good to know if you want to inject an NHibernate session but don't want to have to worry about referencing the correct namespace for ISessionFactory, also useful to know if GetCurrentRepository makes what it actually does more clear in context):
public interface IPostRepositoryFactory {
IPostRepository CreatePostRepository();
}
Then, assuming your IPostRepository is already being managed by Ninject correctly, the extension will do everything else for you just by calling the .ToFactory() method.
kernel.Bind<IPostRepository().To<PostRepository>();
kernel.Bind<IPostRepositoryFactory>().ToFactory();
Then you just call your factory method in the code where you need it:
var repo = _factory.CreatePostRepository();
repo.DoStuff();
(Update: Apparently naming your factory function GetXXX will actually fail if the service doesn't already exist in the session. So you do actually have to be somewhat careful with what you name the method.)
I eventually managed to solve it with a factory as suggested. However, I just could not figure out how to accomplish this with Ninject.Extensions.Factory which is what I would've preferred. Here is what I ended up with:
The factory interface:
public interface IPostRepositoryFactory
{
IPostRepository CreatePostRepository();
}
The factory implementation:
public class PostRepositoryFactory : IPostRepositoryFactory
{
private readonly string key = "PostRepository";
public IPostRepository CreatePostRepository()
{
IPostRepository repository;
if (HttpContext.Current.Items[key] == null)
{
repository = new PostRepository();
HttpContext.Current.Items.Add(key, repository);
}
else
{
repository = HttpContext.Current.Items[key] as PostRepository;
}
return repository;
}
}
The Ninject module for the factory:
public class PostRepositoryFactoryModule : NinjectModule
{
public override void Load()
{
this.Bind<IPostRepositoryFactory>().To<PostRepositoryFactory>();
}
}
The custom model binder:
public class CustomModelBinder : DefaultModelBinder
{
private IPostRepositoryFactory factory;
public CustomModelBinder(IPostRepositoryFactory factory)
{
this.factory = factory;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
IPostRepository repository = factory.CreatePostRepository();
repository.Add("Model binder");
return base.BindModel(controllerContext, bindingContext);
}
}
The controller:
public class HomeController : Controller
{
private IPostRepository repository;
public HomeController(IPostRepositoryFactory factory)
{
this.repository = factory.CreatePostRepository();
}
public ActionResult Index(string whatever)
{
repository.Add("Action method");
return View(repository.GetList());
}
}
Global.asax to wire up the custom model binder:
protected override void OnApplicationStarted()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(string), kernel.Get<CustomModelBinder>());
}
Which in my view, gave me the desired output of:
Model binder
Action method
I'm trying to use the [Inject] attribute on a BasicRoleProvider : RoleProvider provider.
In my provider, I did:
public class BasicRoleProvider : RoleProvider
{
[Inject]
private IAuthenticationService authenticationService;
/*Other stuff here*/
}
My Global.asax.cs file is as follows:
public class MvcApplication : NinjectHttpApplication
{
/* Other stuff here */
#region Inversion of Control
protected override IKernel CreateKernel()
{
return Container;
}
static IKernel _container;
public static IKernel Container
{
get
{
if (_container == null)
{
_container = new StandardKernel(new SiteModule());
}
return _container;
}
}
internal class SiteModule : NinjectModule
{
public override void Load()
{
//Set up ninject bindings here.
Bind<IAuthenticationService>().To<AuthenticationService>();
this.Kernel.Inject(Roles.Provider);
}
}
#endregion
}
Whenever a method in the BasicRoleProvider gets executed and is using the authenticationService, its null. I think my problem lies in the Global.ascx.cs file. Am I doing the injection right?
It seems possible that you are using Ninject in an unsupported way.
From https://github.com/ninject/ninject/wiki/Changes-in-Ninject-2
Things that were in Ninject 1.x that are not in Ninject 2:
Field injection: This is a bad
practice, and has been cut for
minimization.
Because you tagged your question MVC 3, I assume you are linking to Ninject 2. As far as I know, Ninject 1 in an MVC 3 app would be a dead end.
The Inject attribute still exists, and fields must still be a valid target for it, which is why you do not get a compile time error.
But Ninject 2 will happily ignore that Inject attribute on the fields, which is why it is null for you.
I'm using ninject as my IoC and I wrote a role provider as follows:
public class BasicRoleProvider : RoleProvider
{
private IAuthenticationService authenticationService;
public BasicRoleProvider(IAuthenticationService authenticationService)
{
if (authenticationService == null) throw new ArgumentNullException("authenticationService");
this.authenticationService = authenticationService;
}
/* Other methods here */
}
I read that Provider classes get instantiated before ninject gets to inject the instance. How do I go around this? I currently have this ninject code:
Bind<RoleProvider>().To<BasicRoleProvider>().InRequestScope();
From this answer here.
If you mark your dependencies with [Inject] for your properties in your provider class, you can call kernel.Inject(MemberShip.Provider) - this will assign all dependencies to your properties.
I do not understand this.
I believe this aspect of the ASP.NET framework is very much config driven.
For your last comment, what they mean is that instead of relying on constructor injection (which occurs when the component is being created), you can use setter injection instead, e.g:
public class BasicRoleProvider : RoleProvider
{
public BasicRoleProvider() { }
[Inject]
public IMyService { get; set; }
}
It will automatically inject an instance of your registered type into the property. You can then make the call from your application:
public void Application_Start(object sender, EventArgs e)
{
var kernel = // create kernel instance.
kernel.Inject(Roles.Provider);
}
Assuming you have registered your role provider in the config. Registering the provider this way still allows great modularity, as your provider implementation and application are still very much decoupled.