Autofac and ASP .NET MVC bad lifetime scope in controller - c#

i have external controller (for e.g. ExtController ) in another assembly ( folder config/extensions ).
Registration:
builder.RegisterControllers(assembly).Named<IController>(t =>
t.Name.Replace("Controller", string.Empty)
);
Getting a controller ( i have own controller factory ):
public override IController CreateController
(System.Web.Routing.RequestContext requestContext, string controllerName)
{
try
{
var ctrl = _base.CreateController(requestContext, controllerName);
return ctrl;
}
catch (HttpException htte)
{
Object ic = null;
if (_container.TryResolveNamed(controllerName, typeof(IController), out ic))
{
return ic as IController;
}
else
throw htte;
}
}
And if i doing request for this controller i get "root" autofac lifetime scope.
In other controllers i got "AutofacWebrequest" scope.
Could you help me ? Maybe is another way for controller creation from another assembly ?
Edit
I resolved my problem but i think is not the best way i can do it.
I changed from:
if (_container.TryResolveNamed(controllerName, typeof(IController), out ic))
to:
if ( (DependencyResolver.Current as Autofac.Integration.Mvc.AutofacDependencyResolver).RequestLifetimeScope.TryResolveNamed(controllerName, typeof(IController), out ic))

Unfortunately, if your integration point is a custom controller factory working against named services, you are probably stuck with what you have. You can use AutofacDependencyResolver.Current rather than casting DependencyResolver.Current yourself, but the principle will still be the same.
However, I did notice you didn't show what the base ControllerFactory is. It appears you have a sort of decorator pattern going with _base being called rather than base (without the underscore). The DefaultControllerFactory already runs controller instantiation through the dependency resolver. Of course, that would mean you need to not register controllers as named services and instead just register them with the standard RegisterControllers method.
Again, if you have to register them named (for whatever reason) and/or if the base call to CreateController isn't going through the standard DefaultControllerFactory, then what you have is correct. If you can stop registering controllers as named services, though, the standard dependency resolution pipeline will "just work" and you won't need all the extra code.
There is detailed documentation on Autofac MVC integration on the Autofac doc site.

Related

Windsor container. Lifetime of objects in ASP.NET MVC application

I have a new project where Windsor container is used for IoC.
Here is simplified code executed in Install
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<BaseController>().LifestyleTransient(),
Component.For<ISecurityManager>().ImplementedBy<SecurityManager>(),
Component.For<IAccountManager>().ImplementedBy<AccountManager>()
........)
}
Information that I found in official documenation is not enough for understand in detail these lines.
Classes.FromThisAssembly().BasedOn<BaseController>().LifestyleTransient(),
This line in Register method enables dependency injection to all classes in my application that are inherited from BaseController.
BaseController is included.
Injection will not be made to other classes than described above.
We show to container that lifetime of all controllers classes will be an instance.
Component.For<ISecurityManager>().ImplementedBy<SecurityManager>()
To all controllers registered above, if they have in constructor interface ISecurityManager will be injected instance of class SecurityManager.
Lifetime of this SecurityManager is singleton as default value. So after application start we will have only one instance of SecurityManager for all controllers till the end of application execution.
Are my ideas correct? It seems that not, at least because LifestyleTransient() for controllers seems to me strange and that injected objects will be singletons too.
From bottom to the top :
Lifetime of this SecurityManager is singleton as default value. So after application start we will have only one instance of SecurityManager for all controllers till the end of application execution.
Exactly this is going to happen.
It seems that not, at least because LifestyleTransient() for controllers seems to me strange and that injected objects will be singletons too.
Controllers are transient because they hold the HttpContext - they have the information about the current user request and the following response. This is why they are transients and not singletons - the HttpContext is per request and it is created every time a browser/client requests something.
So it is understandable why controllers have shorter lifespan than your other services. It greatly depends on the inner architecture of the application. If someone else has a better idea why - I am more than willing to learn.
The Register/Resolve/Release cycle of your controllers can be done by creating a custom controller factory and substituting the default :
public class WindsorControllerFactory : DefaultControllerFactory
{
private readonly IKernel _kernel;
public WindsorControllerFactory(IKernel kernel)
{
_kernel = kernel;
}
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));
}
if (_kernel.GetHandler(controllerType) != null)
{
return (IController)_kernel.Resolve(controllerType);
}
return base.GetControllerInstance(requestContext, controllerType);
}
public override void ReleaseController(IController controller)
{
_kernel.ReleaseComponent(controller);
}
}
and somewhere put :
container.Register(Component.For<IControllerFactory>().ImplementedBy<WindsorControllerFactory>());
My controllers too have singleton dependenices. In this way you can achieve a pipeline programming model - you funnel the request from the controller through a series of objects and than return the results.
If SecurityManager has something to do with Authentication or Authorization it maybe better to use the MVC default Filtering mechanisms like IAuthorizationFilter or AuthorizeAttribute. Of course this maybe data access filter and it may be reasonable to put it in a different object.
Did I answer your questions?

Nancy IoC per request auto registration

We have an ASP.NET REST service implemented with Nancy (and TinyIoc), which interfaces with a quite large legacy application. We would like to transition to full dependency injection. At the moment most of our modules instantiate the controllers directly, and takes a context class which holds the request information using the NancyContext Context property. Something like this:
public FooModule(ILegacyService legacyService) {
Get["/"] = parameters => new FooController(new RequestContext(Context), legacyService).GetFoo();
}
We would like to inject the controllers directly in the module, and get everything handled by the super-duper-happy-path :)
Our problems all stem from the fact that we need information from NancyContext. The url, headers etc. So we have attempted various approaches to get to dependency injection nirvana.
Attempting to inject controllers failed, since they're instantiated in application scope, and so any dependency to RequestContext would not have the current context information. Even registering RequestContext in ConfigureRequestContainer did not ripple through to all dependants, and they would hold references to an out of date RequestContext.
We have tried to property inject the context using IRequestStartup, and this seemed successful, until we ran into concurrency issues. Simultaneous requests in different threads would overwrite the application scope RequestContext.
We found out that we could call container.AutoRegister() in ConfigureRequestContainer, but this resulted in severe lag, since registrations takes seconds with the amount of types we have.
Using AsPerRequestSingleton() in ConfigureApplicationContainer seems like it will register once, and then instantiate per request, but there doesn't seem to be a way to get auto registration to adhere to this.
It seems like we need to register all types manually, and keep this configuration up to date manually. Is this the case? We really would prefer to have some type of auto registration with per request lifetime.
I have created a small test project (https://github.com/Rassi/NancyIocTest) in which I attempted some of these solutions.
Using DefaultNancyAspNetBootstrapper you can create your own auto registering like this:
public class Bootstrapper : DefaultNancyAspNetBootstrapper
{
protected override void ConfigureApplicationContainer(TinyIoCContainer container)
{
var assemblyClasses = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsClass);
foreach (var assemblyClass in assemblyClasses)
{
var interfaces = assemblyClass.GetInterfaces();
if (interfaces.Count() == 1)
{
container.Register(interfaces[0], assemblyClass).AsPerRequestSingleton();
}
}
}
Everything is then instantiated per request, and you can inject context information using RequestStartup():
protected override void RequestStartup(TinyIoCContainer container, IPipelines pipelines, NancyContext context)
{
base.RequestStartup(container, pipelines, context);
var requestUrl = container.Resolve<IRequestUrl>();
requestUrl.Context = context;
}
}
This is a simple proof of concept, which will find classes which implement one interface, and register it to that. A couple of issues should probably be handled, such as: Registering classes implementing multiple interfaces (perhaps using the convention of registering Name to IName). And also handling multiple classes registering the same interface.

StructureMap thinks it has to inject to constructor and throws exception

I'm using StructureMap and ASP.Net Identity in my application. When I have this line in my Application_Start
ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory());
and this is StructureMapControllerFactory:
public class StructureMapControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null && requestContext.HttpContext.Request.Url != null)
throw new InvalidOperationException(string.Format("Page not found: {0}",
requestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture)));
return ObjectFactory.GetInstance(controllerType) as Controller;
}
}
return ObjectFactory.GetInstance(controllerType) as Controller; throws a StructureMapConfigurationException exception saying:
No default Instance is registered and cannot be automatically determined for type 'IUserStore<Person>'
but if I remove ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory()); line everything goes fine so it's StructureMap's problem not my code.
I think the exception is clear. It's probably about the AccountController from the Identity template, which comes with 2 constructors.
StructureMap will use the most greediest constructor by default. The template comes with 2 constructors, one default constructor and one constructor with an ApplicationUserManager object.
Without StructureMap the default constructor is called and the ApplicationUserManager will be resolved using the service locator anti pattern.
StructureMap now must create the ApplicationUserManager and because this is a concrete type it will try. If it was an abstract it will throw the exception right there. The ApplicationUserManager however has a single constructor which needs an IUserStore<Person>. Because this is an abstract and the container has no registration of this type, StructureMap is unable to create the type for you.
To solve this you should remove the default constructor and Register the ApplicationUserManager and depending services which is at least some component which, implements IUserStore.
Edit:
While the solution you mentioned in the comments may work, this is not the preferable solution because of:
Having multiple constructors is an anti-pattern
You are now using the service locator anti pattern for resolving the ApplicationUserManager from the HttpContext
The templates that come with VS2013 need some work to use with dependency injection. This will cost some time. The good news is, it is possible and doable and will greatly improve your knowledge of Owin, Asp.Net Identity, Dependency Injection and SOLID design.
There are several blogs on how to start refactoring the templates to work with dependency injection. You could read about that here and here

Inject instance via Controller Factory

I'm studying about Dependency Injection, and I need some help to a better understand about Controller Factory.
I need inject the SqlReceitaRepository via Constructor Injection in HomeController.
HomeController Constructor
private readonly IReceitaRepository repositorio;
public HomeController(IReceitaRepository repositorio)
{
if (repositorio == null)
throw new ArgumentException("repositorio");
this.repositorio = repositorio;
}
With SqlReceitaRepository implemented, I can now set up ASP.NET MVC to inject
an instance of it into instances of HomeController, but how can I do that ?
Detail: I'm using NHibernate instead Entity Framework.
If needed, the classes that will be created for accomplish this, will belong which layer ?
I read some articles and I saw that I need put a new line in my Global.asax
Global.asax
var controllerFactory = new ReceitaControllerFactory();
ControllerBuilder.Current.SetControllerFactory(controllerFactory);
I'm assuming that ReceitaControllerFactory should contain the IControllerFactory implementation.
But looking at the IControllerFactory
public interface IControllerFactory
{
IController CreateController(RequestContext requestContext, string controllerName);
SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, string controllerName);
void ReleaseController(IController controller);
}
We can see the method CreateController, but how can I inject a instance of SqlReceitaRepository into HomeController's instance?
The simple answer:
IController CreateController(RequestContext requestContext, string controllerName)
{
return new HomeController(new SqlReceitaRepository());
}
But, as you might have noticed, this will only work for one controller type as it is written. More importantly, it's not very maintainable. So the right answer is to get a popular DI framework like Ninject, and get the plugins you need for your frameworks (Ninject MVC, e.g.), and then define your bindings and let the framework take care of figuring out the dependencies:
Bind<IReceitaRepository>().To<SqlReceitaRepository>();

ASP.NET MVC3 controller AOP proxy not intercepting all methods, only IController.Execute

I have a project with several layers - among them the web front end (ASP.NET MVC3) and the service back end (mainly business logic). The project is a few months old, so everything is working as expected. Now I am trying to add a logging aspect to some of the MVC3 controller methods using custom [Log] attributes.
I am using Castle Windsor for dependency injection. To get a logging aspect I leverage Castle DynamicProxy through SNAP. Controllers are being resolved using WindsorControllerFactory from Krzysztof Koźmic's helpful tutorial - but I modified it to look for the default interface for the controller (see below).
In my service layer:
[Log(LoggingLevel.Info)]
public void Save(MyBusinessDto dto)
{
// business logic and other checks
this.repository.Save(mbo);
}
In my web front end's IWindsorInstaller for controllers:
private static BasedOnDescriptor FindControllers()
{
return AllTypes
.FromThisAssembly()
.BasedOn<IController>()
.WithService.DefaultInterface();
}
In my (slightly customized) WindsorControllerFactory that looks for the default interface for the controller:
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
throw new HttpException(404, string.Format(Error404, requestContext.HttpContext.Request.Path));
}
string controllerName = controllerType.Name;
string defaultInterfaceName = 'I' + controllerName;
Type defaultInterface = controllerType.GetInterface(defaultInterfaceName);
object controller = this.kernel.Resolve(defaultInterface);
return (IController)controller;
}
In my controllers:
public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
[Log(LoggingLevel.Debug)]
public ActionResult CreateOrUpdate(MyBusinessFormModel fm)
{
// Convert form model to data transfer object,
// perform validation and other checks
this.service.Save(dto);
return View(fm);
}
}
This all works fine in the service project, but in the controllers the methods are not being intercepted.
I have confirmed that the WindsorControllerFactory returns proxied controllers.
I have confirmed that the controllers have the interceptor registered.
I have confirmed that the MasterProxy in SNAP intercepts the controller - but it only intercepts IController.Execute(RequestContext requestContext).
How can I intercept all controller methods that have my [Log] attribute?
Update 1: I have considered using DynamicProxy directly instead of SNAP, but this is secondary to getting it to work for controllers as well.
Update 2+4: It seems that SNAP is missing from github back on github.
Update 3: This is what I see in the Visual Studio debugger when breaking in the WindsorControllerFactory (see above). The inspected controller variable is what is returned to MVC, and it is indeed proxied.
controller {Castle.Proxies.IMyBusinessControllerProxy}
__interceptors {Castle.DynamicProxy.IInterceptor[1]}
[0] {Snap.MasterProxy}
__target {My.Business.Web.Controllers.MyBusinessController}
service {Castle.Proxies.IMyBusinessServiceProxy}
(other contructor injections)
MyInjectedProperty {My.Business.Useful.MyOtherType}
In IController GetControllerInstance(...), don't serve interface proxies, serve class proxies with virtual methods.
The user-implemented methods in the controller returned from IController GetControllerInstance(...) will not be accessed through the proxied IMyBusinessController interface, but cast from IController to to the actual class of the controller; for example MyBusinessController. Use a class proxy instead, to make MVC3's cast return the proxy. Also, mark methods as virtual, otherwise the intercepting proxy won't be able to intercept the method calls and check for custom attributes.
In the controllers, add virtual to your methods with attributes:
public class MyBusinessController : MyBusinessControllerBase, IMyBusinessController
{
[Log(LoggingLevel.Debug)]
public virtual ActionResult CreateOrUpdate(MyBusinessFormModel fm)
{
// Convert form model to data transfer object,
// perform validation and other checks
this.service.Save(dto);
return View(fm);
}
}
Why is only Execute(...) intercepted? The IController interface only contains Execute(...). Execute is called on the returned controller interface proxy, thus it can be intercepted. But once MVC3's internal ControllerBase.Execute(...) gets the call, it performs the cast to the class it expected from the ControllerFactory.
The problem is similar to this leaking, in that both bypass the interface proxy. I guess it could be solved in a number of ways; perhaps by creating a custom type converter, creating a class proxy from the interface proxy's target in the factory, a clever Windsor configurations etcetera.
Krzysztof Koźmic's IController installer and WindsorControllerFactory should work out of the box. Interface proxies may be recommended in the bigger picture (and they work well until using interceptors in the controllers) but in this case there might be a reason not to go that far, to avoid further side effects.
Thanks to Marius for pointing me in the right direction!
Since DynamicProxy (SNAP uses dynamicproxy) can't intercept non-virtual methods I am guessing that the returned proxy is a derived class of your controller and thus, the non virtual methods are ignored. You either need to make SNAP (don't know how this works though) return an interface proxy with target (your implementation) or simply try to make your controller methods virtual.

Categories