Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IoC/DI in the face of winforms and other generated code

When using dependency injection (DI) and inversion of control (IoC) objects will typically have a constructor that accepts the set of dependencies required for the object to function properly.

For example, if I have a form that requires a service to populate a combo box you might see something like this:

// my files
public interface IDataService {
    IList<MyData> GetData();
}

public interface IComboDataService {
    IList<MyComboData> GetComboData();
}

public partial class PopulatedForm : BaseForm {
    private IDataService service;
    public PopulatedForm(IDataService service) {
        //...
        InitializeComponent();
    }
}

This works fine at the top level, I just use my IoC container to resolve the dependencies:

var form = ioc.Resolve<PopulatedForm>();

But in the face of generated code, this gets harder. In winforms a second file composing the rest of the partial class is generated. This file references other components, such as custom controls, and uses no-args constructors to create such controls:

// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
    private void InitializeComponent() {
        this.customComboBox = new UserCreatedComboBox();
        // customComboBox has an IComboDataService dependency
    }
}

Since this is generated code, I can't pass in the dependencies and there's no easy way to have my IoC container automatically inject all the dependencies.

One solution is to pass in the dependencies of each child component to PopulatedForm even though it may not need them directly, such as with the IComboDataService required by the UserCreatedComboBox. I then have the responsibility to make sure that the dependencies are provided through various properties or setter methods. Then, my PopulatedForm constructor might look as follows:

public PopulatedForm(IDataService service, IComboDataService comboDataService) {
    this.service = service;
    InitializeComponent();
    this.customComboBox.ComboDataService = comboDataService;
}

Another possible solution is to have the no-args constructor to do the necessary resolution:

public class UserCreatedComboBox {
    private IComboDataService comboDataService;
    public UserCreatedComboBox() {
        if (!DesignMode && IoC.Instance != null) {
            comboDataService = Ioc.Instance.Resolve<IComboDataService>();
        }
    }
}

Neither solution is particularly good. What patterns and alternatives are available to more capably handle dependency-injection in the face of generated code? I'd love to see both general solutions, such as patterns, and ones specific to C#, Winforms, and Autofac.

like image 873
Kaleb Pederson Avatar asked Feb 11 '11 22:02

Kaleb Pederson


People also ask

What is IoC and DI in C#?

IOC (Inversion of control) is a general parent term while DI (Dependency injection) is a subset of IOC. IOC is a concept where the flow of application is inverted. So for example rather than the caller calling the method.

What is IoC in C# with example?

Inversion of control (IoC) is a design pattern in which the control flow of a program is inverted. You can take advantage of the inversion of control pattern to decouple the components of your application, swap dependency implementations, mock dependencies, and make your application modular and testable.

How do we implement dependency injection?

Dependency Injection is done by supplying the DEPENDENCY through the class's constructor when creating the instance of that class. The injected component can be used anywhere within the class. Recommended to use when the injected dependency, you are using across the class methods.

What is the use of dependency injection in C#?

Dependency Injection (or inversion) is basically providing the objects that an object needs, instead of having it construct the objects themselves. It is a useful technique that makes testing easier, as it allows you to mock the dependencies.


2 Answers

I believe there is no silver-bullet solution here. I would use property injection in this case to leave parameterless constructor. Also I personally do not like injection of services into UI classes, I prefer injecting some kind of Presenters there. Then you have a property Presenter which will be set by IoC container and in setter of this property you will have your initializing code.

Out of your two solutions I do not like second one especially because of referencing IoC container in your code which is bad IMO.

like image 184
Snowbear Avatar answered Oct 14 '22 01:10

Snowbear


I would say that your UI, especially sub-elements of your UI, shouldn't need to be provided with any services.

It's hard to judge how feasible this is for your app, but MVC or MVP is meant to avoid this need.

I would try to redesign so that a controller is responsible for interacting with services and that controller gives the view elements everything they need, rather than letting the view elements ask for what they need.

like image 37
quentin-starin Avatar answered Oct 14 '22 01:10

quentin-starin