0

I just started using Castle.Windsor for a couple of reasons, one of which is to get away from using both static classes and the singleton pattern.

I'm not new to the general concepts of DI, but am a little new to implementation details. The one I'm dealing with now: how do I get the instance of my class that I registered from within the Application_BeginRequest() method in the Global.asax file? I obviously can't add parameters to this method, and I setup the container in the Application_Start() method of this class so it's not like I can create a constructor to inject it.

Is this an edge case where I would have to use the Service Locator pattern or what is the proper way to do this?

Kate Orlova
  • 3,225
  • 5
  • 11
  • 35
BVernon
  • 3,205
  • 5
  • 28
  • 64
  • Exactly why do you need to get the reference during startup? To use in registration in another service? Can it wait until the end of registration? Do note that you can use a [singleton lifestyle](https://stackoverflow.com/q/7108062) with DI to replace static classes and methods. – NightOwl888 Mar 20 '18 at 08:48
  • Because I need to get the referrer, and yes using the singleton lifestyle is exactly what I want to do. – BVernon Mar 20 '18 at 18:44
  • Well, technically I'm just registering an instance but same effect. – BVernon Mar 20 '18 at 18:46
  • Yes it's mvc. Thanks – BVernon Mar 22 '18 at 17:23

2 Answers2

1

Usually you initialize the container in Application_Start(). Just make a static variable with reference of the initialized container and use it in Application_BeginRequest().

Ognyan Dimitrov
  • 6,026
  • 1
  • 48
  • 70
1

Application_BeginRequest is a legacy ASP.NET classic method that runs outside of the context of MVC. The only way to provide an instance there is to keep an instance of the container around after the Application_Start method runs.

However, MVC has better alternatives. If you have a crosscutting concern that you need to run in each request, you should make a global filter to run that logic.

public class MyFilter : IActionFilter
{
    private readonly ISomeDependency someDependency;

    public MyFilter(ISomeDependency someDependency)
    {
        this.someDependency = someDependency 
            ?? throw new ArgumentNullException(nameof(someDependency));
    }

    // Runs after action method
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }

    // Runs before action method
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        someDependency.DoSomething();
    }
}

Usage

If you can get away with registering the filter as a singleton, you just need to register it at application startup in the static GlobalFilterCollection.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters, IWindsorContainer container)
    {
        filters.Add(new MyFilter(container.Resolve<ISomeDependency>()));
        filters.Add(new HandleErrorAttribute());
    }
}

If you have dependencies that would be captive (such as DbContext) if you registered as singleton, you can either inject a factory into your filter, or use a custom filter provider to resolve your global filter from the Castle Windsor container.

You can read more details about filter providers here.

NOTE: This is really just the tip of the iceberg. You could control which actions your filter will run on by making a custom attribute to decorate your actions with and using Reflection to scan for it in your filter (see this example). While MVC sort of does this with its ActionFilterAttribute extension, it is not DI-friendly to use it. Instead, make DI-friendly filters and make the attributes passive.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
  • Will this still work if they visit an aspx page? 99% of our site is mvc but there are a couple of aspx pages around from previous version. Honestly don't think it will be important for this to run on those pages, but just curious. – BVernon Mar 22 '18 at 17:29
  • Because this is part of MVC, the answer is no, that wouldn't work. One option would be to keep an instance of the DI container so you can use it in `Application_BeginRequest`. Another option might be to make an HTTP Module to ensure the logic runs for both MVC and web forms (although HTTP Modules are notoriously DI-unfriendly). But it is better to use MVC filters if you can. – NightOwl888 Mar 22 '18 at 17:45
  • That's what I figured. Thanks! – BVernon Mar 22 '18 at 17:48