Search code examples
asp.net-mvc-4architecturedependency-injectionsimple-injector

Configuring DI container for global filters with services in their constructors


I have a site using SimpleInjector and MVC, and I'm trying to determine where I'm going wrong architecturally.

I have my DI container being set up:

public static class DependencyConfig
{
    private static Container Container { get; set; }

    public static void RegisterDependencies(HttpConfiguration configuration)
    {
          *snip*

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, Container);
    }
}

And my RegisterGlobalFilters looks like this:

public static class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters, Container container)
    {
        filters.Add(new HandleErrorAttribute());

        filters.Add(container.GetInstance<OrderItemCountActionFilterAttribute>());

        if (container.GetInstance<ISiteConfiguration>().ConfiguredForExternalOrders)
        {
            filters.Add(container.GetInstance<StoreGeolocationActionFilterAttribute>());
        }

        filters.Add(container.GetInstance<StoreNameActionFilterAttribute>());
    }
}

The store can take orders (through this website) at in-store kiosks or online from home. External orders would need to geolocate to display information to the customer regarding their closest store. But this means I have to use the container as a service locator in my global filters, which means I have to hide the call to the global filters in my DI container. This all seems to me like an anti-pattern or that there should be a better way to do this.


Solution

  • After a bit of discussion with a system architect, we came to the (embarrassingly simple) conclusion that the best answer for our architecture would be to create two Register functions in our DI container - one called RegisterCorporateWebSiteDependencies() and another RegisterStoreWebsiteDependencies().

    The natural extension of that is to also have 2 global filter configs called after dependency composition, (again) one for RegisterCorporateGlobalFilters() and one for RegisterStoreGlobalFilters().

    This results in one overall if statement running the registers ex:

    if (Convert.ToBoolean(ConfigurationManager.AppSettings["IsCorporate"]))
    {
        DependencyConfig.RegisterCorporateWebSiteDependencies(GlobalConfiguration.Configuration);
    }
    else
    {
        DependencyConfig.RegisterStoreWebSiteDependencies(GlobalConfiguration.Configuration);
    }
    

    Which is much more straightforward, and removes the logic from the other locations where it can be confusing.