0

Update 2

Thanks again for getting even more detailed. I haven't been able to get your GlobalFilterProvider to work correctly, but it seems that no matter which approach I take here, I won't be able to take advantage of the lifetime scoping that becomes available with a nested container. Currently, using the StructureMap.MVC5 NuGet package a nested container is created on Application_BeginRequest and disposed of on Application_EndRequest, which allows me to scope objects to UniquePerRequest and have them tore down when the nested container is disposed. It seems like no matter what a filter needs to get added on Application_Start and any dependencies still need to be added using the container, which on Application_Start is only the parent at that time. I don't even think using a Decoraptor will help me in this scenario.

Is there any way to achieve this?

This problem has been solved - see my other SO question.

Update 1 - Comments on NightOwl888's Answer

I understand what is happening in the "Filling Setter's of an Object" piece you linked to, as that code is what the StructureMapFilterProvider is doing to build up the dependencies of the action filter. What I was trying to understand is the IContainer reference in the StructureMapFilterProvider. Is the current DependencyResolver set in MVC what is doing the resolving of that IContainer reference? I just want to understand where that is coming from. Also, adding .Singleton() did actually fix the problem, so thanks for pointing that out.

I'm still trying to understand that GlobalFilterProvider you linked to, and I do understand the benefits of using constructor injection instead. I'm just trying to wrap my head around it all.


Original Post

I am using StructureMap for my DI needs, and have included it in my ASP.NET MVC 5 project via the StructureMap.MVC5 NuGet package. I have the typical logging action filter where I want to inject a dependency (ILogger) into the action filter I have created. While I have found and understand the passive attributes method for bypassing setter injection, I also became intrigued with the method of using a StructureMapFilterProvider like so...

public class StructureMapFilterProvider : FilterAttributeFilterProvider
{
    private readonly IContainer _container;

    public StructureMapFilterProvider(IContainer container)
    {
        _container = container;
    }

    public override IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
    {
        var filters = base.GetFilters(controllerContext, actionDescriptor);

        foreach (var filter in filters)
        {
            _container.BuildUp(filter.Instance);
        }

        return filters;
    }
}

However, the IContainer dependency of the StructureMapFilterProvider seems to be a little magical. My first question would be how does it even get resolved? What is doing the resolving if the container itself is what needs to be resolved. Second, it seems to have a problem with the nested containers the StructureMap.MVC5 setup creates and disposes of per request at Application_BeginRequest and Application_EndRequest.

On Application_Start, the filter provider works fine, presumably because it's not a nested container yet, and the IContainer instance resolved looks like this:

enter image description here

Yet if I run the same action again now that the application is started, the filter provider bombs and the IContainer instance now looks like this:

enter image description here

What am I doing wrong? Is there a fix for this?


The rest of my code is below if that helps.

Logger implementation:

public class Logger : ILogger
{
    public void Log(string message)
    {
        Debug.WriteLine(message);
    }
}

public interface ILogger
{
    void Log(string message);
}

Logger action filter:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class LoggerAttribute : ActionFilterAttribute
{
    public ILogger Logger { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        Logger.Log("Testing: Executing an action");
    }
}

Additional DI setup added to DependencyResolution/IoC.cs class provided by the StructureMap.MVC5 package:

public static class IoC
{
    public static IContainer Initialize()
    {
        var container = new Container(c =>
        {
            c.AddRegistry<DefaultRegistry>();
            c.For<IFilterProvider>().Use<StructureMapFilterProvider>();
            c.For<ILogger>().Use<Logger>();
            c.Policies.SetAllProperties(p =>
            {
                p.OfType<ILogger>();
            });
        });

        return container;
    }
}

Controller:

public class HomeController : Controller
{
    [Logger]
    public ActionResult Index()
    {
        return Content("worked");
    }
}
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
ryanulit
  • 4,983
  • 6
  • 42
  • 66

1 Answers1

1

Setter Injection

No magic. Other DI packages hack the MVC framework to provide setter injection of FilterAttribute subclasses. FilterAttributes are typically loaded up by the FilterAttributeFilterProvider, but in this case it is subclassed by the StructureMapFilterProvider in order to provide an extension point to inject MVC FilterAttributes.

Have a look at "Filling Setter's of an Object" in the StructureMap Setter Injection Documentation - this is basically the way you can use setter injection without attributes. You just need a container registration:

x.For<IGateway>().Use(theGateway);
x.Policies.SetAllProperties(y => y.OfType<IGateway>());

and a call to the container:

var target = new BuildUpTarget1();
container.BuildUp(target);

IMO that is the best way to use setter injection, since it means you don't have to reference StructureMap in order to use its .NET attributes.

I could be wrong, but the code in the post you linked to is likely broken because they are not removing the default FilterAttributeFilterProvider from MVC's FilterProviders.Providers static property and they are adding the StructureMapFilterProvider as well (through DI), which I believe will register the attributes twice (once with the container dependencies and once with null dependencies).

Constructor Injection

However, constructor injection is a much better and more consistent way to use DI in your application. IMO, you should never use another option if constructor injection is possible, and in this case it most certainly is.

One thing that could use some improvement in Mark Seemann's Passive Attributes post is the ability to control lifetime in globally registered filters, so you can inject non-singletons into the filter (think DbContext). If you register the filter in the GlobalFilters.Filters static collection, all dependencies are captive dependencies of a static singleton, which makes them singleton by definition. It turns out that is pretty easy to work around this problem using a custom filter provider. But instead of building up with property injection, we can split our attribute classes (if needed) from our filter classes and just resolve the dependency graph of the filters directly from the container.

There you have it - a single infrastructure component registered with MVC in the composition root along with registration of filters (and their dependencies), and then new filters "just work" as soon as they are registered. They don't even care where their dependencies come from. No awkward SetAllProperties call during registration or decorating your attribute class with container-specific .NET attributes for setter injection (which in turn require DLL references where they don't belong). No need to create public properties on FilterAttributes which mysteriously get populated with dependencies (or not). With proper guard clauses, there is no need to worry about null setter-injected dependencies. This is head and shoulders above using setter injection in part of your application just because you are too lazy to add the proper extensions to MVC in order to use constructor injection and too lazy to follow the SRP by separating services (filters) from meta-data (.NET attributes) and tossing FilterAttribute and all of its derivatives out the window when you need to inject dependencies.

I run the same action again now that the application is started, the filter provider bombs

What am I doing wrong? Is there a fix for this?

Since the filter provider is being registered through DI with a lifetime of Per Request (the default), it is being instantiated on every request. I'm betting that you have no static access to the container after it is built, so you are getting the error because the container goes out of scope after application startup.

If you change the lifetime to singleton (or register it through the static FilterProviders.Providers property), it will hold its reference to the global DI container captive so it won't go out of scope.

c.For<IFilterProvider>().Singleton().Use<StructureMapFilterProvider>();

What I was trying to understand is the IContainer reference in the StructureMapFilterProvider. Is the current DependencyResolver set in MVC what is doing the resolving of that IContainer reference?

Actually, if you put a breakpoint just before you call DependencyResolver.SetResolver, and then run the following line in the immediate window, you can see that StructureMap registers an instance of itself in the container.

container.GetInstance<IContainer>()

You can prove it by attempting to resolve one of the types that are registered in the resolved container.

container.GetInstance<IContainer>().GetInstance<ILogger>()

So it is StructureMap that is exhibiting this behavior. Most other DI containers I have worked with do not self-register like this (but it is possible to do it manually).

Clearly the IContainer instance that is self-registered is not being registered as a singleton. Therefore, as I alluded to before, any infrastructure component that you inject IContainer into should be a singleton. If it is not, it will lose its reference to IContainer. I don't recommend you attempt to use IContainer as a singleton, as there must be a good reason why it does not self-register that way.

I'm still trying to understand that GlobalFilterProvider you linked to

Actually, it is exactly the same concept. But in that case I made the decision to register IDependencyResolver with the DI container, so it can be used to service the infrastructure component.

// Register other container components...

// Create Dependency Resolver
var dependencyResolver = new StructureMapDependencyResolver(container);

// Register the dependency resolver with StructureMap
For<IDependencyResolver>().Use(dependencyResolver);

// Set the dependency resolver for MVC
DependencyResolver.SetResolver(dependencyResolver);

Which is the reason why injecting IDependencyResolver works in that example.

Ok, on the surface this looks unnecessary because you can automatically get a reference to the container simply by injecting IContainer into an infrastructure component. But using IDependencyResolver (part of MVC) instead makes it easier to switch to another DI container if you later decide the one you picked isn't as good as the new shiny DI container you just read about on some blog. Not to mention, it is more useful to those here on StackOverflow who may not know how to implement one that is specific to their DI container - a generic one works better for informational purposes.

In my view, the less you depend on the DI container, the safer your position is if you need to change the container down the line (which is another argument against using setter injection, because each DI container does it a little differently). Also, using IDependencyResolver (which is part of MVC) clearly delineates the component as an MVC infrastructure component that is only used as a broker between your application and MVC. This component should be considered part of the composition root and IMO that means its implementation should be part of the MVC project, not farmed off into a DLL somewhere. But in this case, it can also just be considered part of the MVC extension code, which means you can put it where you put other MVC extensions without worrying about the composition root part too much. It doesn't need to change when you change your composition root.

That said, there is one downside to this approach. It forces the MVC application to use IDependencyResolver rather than IControllerFactory as it DI integration point. If you inject IContainer into the component, you are free to use IControllerFactory if you so choose.

Community
  • 1
  • 1
NightOwl888
  • 55,572
  • 24
  • 139
  • 212