I am working on a Win form application with Autofac Here we resolve the dependencies as following: As seen in doc
using (var scope = DIConfig.container.BeginLifetimeScope())
{
var us = scope.Resolve<IUsersService>();
usersGrid.DataSource = us.GetUsers();
}
However in Web MVC project we could resolve all dependencies Eg as in
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
So that there is no need to resolve the scope each time as in and use the simple code
usersGrid.DataSource = us.GetUsers();
How could we do a similar resolution in Winform so that there is no need of more code in use? How to Resolve dependency in Winforms?Is there a Autofac integration library for WinForm ?
¶ Autofac is an addictive IoC container for . NET. It manages the dependencies between classes so that applications stay easy to change as they grow in size and complexity. This is achieved by treating regular .
From Visual Studio, you can get it via NuGet. The package name is Autofac. Alternatively, the NuGet package can be downloaded from the GitHub repository (https://github.com/autofac/Autofac/releases).
I've successfully migrated a WinForms application to use Autofac
in the near past. I wanted a design which has the following motivations.
I came up with choosing a Form
to serve as a scope, so for example if I inject a view model or a repository, it should get disposed when the form is closed, etc. This way I can use constructor injection in my Form
s just as in Controller
s in a web app. For example
public class UserEditForm : Form {
private reaodnly UserEditViewModel viewModel;
public UserEditForm(UserEditViewModel viewModel) {
this.viewModel= viewModel;
}
public void Load(int userId) {
this.viewModel.Load(userId);
}
...
}
public class UserEditViewModel {
private readonly UserRepository repository;
public UserEditViewModel(UserRepository repository) {
this.repository = repository;
}
...
}
To avoid the service locator pattern as much as possible, I've designed an IFormFactory
interface which looks as simple as this.
public interface IFormFactory
{
Form CreateForm(Type formType);
}
I decided to allow accessing this statically thorough the UI layer (but only there), so I've implemented the following static class.
public static class FormFactory
{
private static IFormFactory factory;
public static void Use(IFormFactory factory)
{
if (FormFactory.factory != null)
{
throw new InvalidOperationException(@"Form factory has been already set up.");
}
FormFactory.factory = factory;
}
public static Form Create(Type formType)
{
if (factory == null)
{
throw new InvalidOperationException(@"Form factory has not been set up. Call the 'Use' method to inject an IFormFactory instance.");
}
return factory.CreateForm(formType);
}
public static T Create<T>() where T : Form
{
return (T)Create(typeof(T));
}
}
Now at this point I abstracted the creation process of forms. Note, that until this point I didn't even care about any potantial DI container. Here I already could start refactoring the entire view layer to use this pattern, for example like this.
using (var form = FormFactory.Create<UserEditorForm>()) {
form.Load(userId);
form.ShowDialog();
}
And here came Autofac (or any other DI container). I've implemented the IFormFactory
using Autofac like this.
public class AutofacFormFactory : IFormFactory
{
private readonly ILifetimeScope currentScope;
public AutofacFormFactory(ILifetimeScope currentScope)
{
this.currentScope = currentScope;
}
public Form CreateForm(Type formType)
{
// begin a new lifetime scope for each form instance
var scope = this.currentScope.BeginLifetimeScope();
var form = (Form) scope.Resolve(formType);
form.Disposed += (s, e) =>
{
// finish the scope when the form is disposed (closed)
scope.Dispose();
};
return form;
}
}
Also, don't forget to include the binding in the Autofac configuration.
builder.RegisterType<AutofacFormFactory>().As<IFormFactory>();
Then finally I initialize this all at Program.cs
like this.
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
var container = builder.Build();
FormFactory.Use(container.Resolve<IFormFactory>());
Hope it can still be helpful for anyone.
I think that in general, for a WinForms or other non-request based application, using a specific scope with using
is appropriate: when needing operation-specific components.
The first step, is to first identify components that can be resolved for the entire application - trivially this includes "singleton" services, of which a good chunk should be. These components are added to the default Autofac scope builder.
Then each form (eg. in the OnLoad event) uses property injection to load the form/application-wide components that it requires - this can itself be wrapped in a global method, but keeps knowing the Container to one place in the Form to avoid a Service Locator pattern bleed.
(If all the Forms are created via the IoC container then constructor injection can also be used; I've found that using property injection is "sufficient" for WinForms and WebForms.)
One of the injected components knows how to create the appropriate operation scopes. In this case the injected property might be the following, where IOperationScope is an ILifetimeScope that handles the "operation UoW", whatever that may be. This scope replaces the pre-request scope as found in MVC/WCF:
public Func<IOperationScope> BeginOperationScope { get; set; }
(The OperationScope should probably be a named scope so that components can be registered to just this named scope, instead of globally.)
Then in the operation itself, we have:
using (var op = BeginOperationScope())
{
op.Resolve<..>(..);
}
This has a bit of bleed in that there is still a Resolve directly in the WinForm event code - it's not terrible, but still leaves the feeling of not-DI (as it's a very slight SL bleed) and mixing-UI-and-logic.
It is possible to create a simple method-injection-resolver so that the actual code could be called as so:
public void OperationMethod(ISomeOperationService service)
{
// "method parameter injection"
}
using (var op = BeginOperationScope())
{
op.ExecuteMethod(OperationMethod);
}
There is no standard form of this method-injection-resolver but it can be written with just a little bit of reflection for a trivial case.
While at first this might appear to be a duplicate method per Event, it actually helps add isolation between the UI and any "Controller" logic established. The UI events then only wrap to the applicable logic methods that have dependencies resolved automatically.
In this case the scope itself might simply be an injected disposable Component with a composite ILifetimeScope that it created, used to inject components, and cleans up itself in Dispose
.
using (var op = BeginOperationScope())
{
op.DoSomething();
}
This case is arguably the most "pure" and it has a nice separation. However, it should still be noted that the using
approach is still used and the Controller/scope itself controls the applicable per-Operation Autofac lifetime.
Since the lifetime is still limited to the operation, make sure not to bleed IQueryable or non-forced/lazy IEnumerable objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With