Automatically inject Winforms user controls using Autofac

Viewed 102

Using Autofac 6.2.0, I'd like to be able to have properties automatically injected if they belong to any class deriving from a specific list of base classes such as UserControl and BarItem (from DevExpress).

The reason for this is that UserControl objects nested in a Form are constructed inside generated code, not by Autofac, and must have default constructors to avoid breaking the forms designer in Visual Studio. Basically I can't register user controls with Autofac, but I need to be able to pass in services to those objects.

Yes, I'm well aware that injecting services into my views and code-behinds is ultimately an anti-pattern and I should be splitting that business logic away from the views to ultimately make this problem moot. However realistically, I'm dealing in a lot of legacy code and I can't refactor the whole code base just to make it easier to pass around dependencies. So as a stop-gap, I need this to work. I suspect this will get brought up so I just wanted to move us past this right away.

Here's a rough sequence of what I want to happen:

  1. Register all top-level Form classes (like modal dialogs) with Autofac
  2. Later, resolve the form using a factory (e.g. Func<MyModalDialog>)
  3. MyModalDialog gets constructed by Autofac.
  4. Autofac scans the properties of MyModalDialog for any that inherit from UserControl. If one is found, run InjectProperties() or something on it to assign values to properties representing types already registered in the container.
  5. Any properties on that user control matching UserControl or BarItem, recursively begin processing it as was done in the previous step.

The requirements:

  • UserControls that need to be "dependency injected" are exposed via public properties to signal to Autofac that it needs to be DI injected
  • UserControls should be recursively injected (in case I have form nesting)

I did this in UnityContainer previously using BuilderStrategy, with BuilderStrategy.PreBuildUp(). I'm hoping I can carry over that logic to some other mechanism that works similar to Autofac.

Last thing I'll say is that I did read about the Autofac resolution pipeline, as well as the "Middleware" classes it supports. I feel like this is the direction to go but I'm confused about what each of the PipelinePhase enums represent. Yes they're documented, but even though I read that, it still isn't quite clear to me which one is the best place to do this work. I'm not sure how to pick which phase to do something in based on my needs. I'll put a first attempt I had at this below. I haven't tested this at runtime yet, so it's possible it doesn't work, but I'm mostly putting this here to show that I've put in some effort.

public class AutofacUserControlPropertyInjectionMiddleware : IResolveMiddleware
{
    private static readonly List<Type> CONTROL_TYPES = new List<Type>
    {
        typeof(UserControl),
        typeof(BarItem)
    };

    public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next)
    {
        if (CONTROL_TYPES.Any(x => x.IsInstanceOfType(context.Service)))
        {
            context.InjectProperties(context.Service);
        }

        next(context);
    }

    public PipelinePhase Phase => PipelinePhase.ParameterSelection;
}
0 Answers
Related