15

I am using Autofac with ASP.NET MVC integration, all my controllers receive dependencies and Autofac resolves nested dependencies automatically. Great it works

But how can I resolve a dependency outside the scope of controller instantiation? In some places deep in my code I need to ask the resolver for my Logger. On the one hand it seems wrong to be passing the Logger as a dependency down to every little object I create, and on the other it seems to wrong to depend on dependency resolver so deep in my code

For example, I have a class called Result which is returned from many actions. It's a consistent used object that my application code can rely on coming back from the deeper layers. When the deeper layered code adds a UI error to this object I want to automatically add it to the logger, which needs resolving. Having every class take a dependency on logger would just get in the way

Any help appreciated thanks

Typo Johnson
  • 5,974
  • 6
  • 29
  • 40

4 Answers4

38

The thing you're looking for is MVC's DependencyResolver.Current:

var logger = DependencyResolver.Current.GetService<ILogger>();
Nicholas Blumhardt
  • 30,271
  • 4
  • 90
  • 101
7

Well you can use eventing (pub/sub approach) if dependency in every object irritates you but i dont thing there is anything wrong with dependency on central Logger resolver. If you really need to log from every class then you cetrtainly can approach logging as very core aspect of your application and you mentally aproach it as other common library types like String or Ints which are ubiquitous and safe to depend on. But i would suggest you something else. IMHO you should rething the architecture and dont log in every class. If your logging is only (or mostly) about writing the errors (exceptions) then dont polute your domain model with it. Lets place it in Service layer insteád. This kind of orchestrating layer can correctly evaluate each catched exception and log only what is neccesary. Let's bubble those exceptions to the lower possible palce in the stack trace and handle them as last very thing.

Boris Bucha
  • 630
  • 4
  • 6
  • 2
    You're right. It seemed such a natural place to put logging but I'm just adding additional responsibilities to classes. Now I add ui errors to the ActivityLog in BaseController.OnActionExecuted(). First from the ModelState dictionary. Secondly, I now have a single Result object in BaseController that all actions use when they call domain/repo methods. In OnActionExecuted() again, I add these errors to my ActivityLog. So it's all still centralized but things are in the their proper place. Thanks for steering me away from a bad solution. This is much better, and keeps things testable – Typo Johnson Apr 25 '11 at 15:03
  • Just to add to this now I've settled on a solution - my service layer returns a Result object which encapsulates any exceptions, and has already downgraded user input exceptions to just error messages ready to be displayed at the ui. The Result object is in my base controller for all controllers, and basecontroller's OnActionExecuted checks Result.Exceptions, this is the only place I log exceptions and it's just a simple loop and a one liner to log each exception. Exceptions occurring in the controller's themselves are handled by basecontroller's OnException – Typo Johnson Oct 11 '11 at 10:27
3

Using DependencyResolver.Current is definitely a way of solving your problem in ASP .NET MVC given that it is a framework feature. However, I would first try to follow the following recommendation from the "Best Practices" section of the Autofac Wiki

"Giving components access to the container, storing it in a public static property, or making functions like Resolve() available on a global 'IoC' class defeats the purpose of using dependency injection. Such designs have more in common with the Service Locator pattern. If components have a dependency on the container, look at how they're using the container to retrieve services, and add those services to the component's (dependency injected) constructor arguments instead. Use relationship types for components that need to instantiate other components or interact with the container in more advanced ways."

Only if not possible to restate a dependency as suggested above would I use the DependencyResolver.

Eonasdan
  • 7,563
  • 8
  • 55
  • 82
  • The Autofac best practices documentation is now at http://autofac.readthedocs.io/en/latest/best-practices/index.html – Chris Morgan Jul 25 '16 at 15:33
0

People looking for another solution to this issue might have a look at this other SO answer that takes advantage of IComponentContext resolver service injected directly by Autofac.

Community
  • 1
  • 1
superjos
  • 12,189
  • 6
  • 89
  • 134