Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding Autofac to WPF MVVM application

I can't seem to find an solution to this problem. I've seen several questions about this, but none really give me a solution. I am totally new to Autofac and haven't really done much WPF + MVVM, but know the basics.

I have a WPF application (using ModernUI for WPF) which I'm trying to add Autofac to, and I am having a hard time figuring out how to resolve my services within all the views, since they have no access to my container. I have a main view, which is my entry point, where I set up my container:

public partial class MainWindow : ModernWindow
{
    IContainer AppContainer;

    public MainWindow()
    {

        SetUpContainer();

        this.DataContext = new MainWindowViewModel();
        InitializeComponent();

        Application.Current.MainWindow = this; 
    }

    private void SetUpContainer()
    {
        var builder = new ContainerBuilder();

        BuildupContainer(builder);

        var container = builder.Build();

        AppContainer = container;
    }

    private void BuildupContainer(ContainerBuilder builder)
    {
        builder.RegisterType<Logger>().As<ILogger>();
        ...
    }
}

The problem I'm having is figuring out how I can resolve my logger and other services within my other views, where I inject all my dependencies through the ViewModel constructor, like so:

public partial class ItemsView : UserControl
{
    private ItemsViewModel _vm;

    public ItemsView()
    {
        InitializeComponent();

        IFileHashHelper fileHashHelper = new MD5FileHashHelper();
        ILibraryLoader libraryLoader = new LibraryLoader(fileHashHelper);
        ILogger logger = new Logger();

        _vm = new ItemsViewModel(libraryLoader, logger);
        this.DataContext = _vm;
    }
}

Some views have a ridiculous amount of injected parameters, and this is where I want Autofac to come in and help me clean things up.

I was thinking of passing the container to the ViewModel and storing it as a property on my ViewModelBase class, but I've read that this would be an anti-pattern, and even then I don't know if that would automatically resolve my objects within the other ViewModels.

I managed to put together a simple Console Application using Autofac

class Program
{
    static void Main(string[] args)
    {

        var builder = new ContainerBuilder();
        builder.RegisterType<Cleaner>().As<ICleaner>();
        builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

        var container = builder.Build();

        using (var scope = container.BeginLifetimeScope())
        {

            ICleaner cleaner = container.Resolve<ICleaner>();
            cleaner.Update(stream);
        }
    }
}

but that was simple since it has a single entry point.

I'd like some ideas on how to add Autofac to my WPF app. I'm sure that I'm doing something wrong. Your help is appreciated.

like image 684
Ben Avatar asked Apr 28 '17 21:04

Ben


People also ask

How do I add Autofac?

Open the solution that want to use Autofac in, then select Manager NuGet Packages for Solution... by going to: Tools -> NuGet Package Manager -> Manager NuGet Packages for Solution... Installing through NuGet will automatically add Autofac in the References of the projects which were selected during installation.

What is Mvvm dependency injection?

The main idea of dependency injection is to resolve all dependencies centrally. This means that you have a separate block in your program to initialize new class instances and pass parameters to them.

What is dependency injection in WPF?

What is Dependency Injection? Dependency Injection helps us to achieve the Inversion of Control (IoC) design principle and help in separating object creation and consumption. The Dependency Injection framework facilitates object creation, object lifetime maintenance, and supplying the required dependency at runtime.

Is Autofac A IoC?

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.


2 Answers

Expanding on my comment above:

I use Autofac with all my WPF MVVM applications, I believe it to be one of the better DI frameworks - this is my opinion, but I think it is valid.

Also for me PRISM should be avoided 99% of the time, it's a 'solution looking for a problem' and since most people don't build dynamically composable runtime solutions in WPF it is not needed, i'm sure people would\will disagree.

Like any architectural patterns there is a setup\configuration phase to the application life-cycle, put simply in your case before the first View (window) is shown there will be a whole of setup done for Dependency Injection, Logging, Exception Handling, Dispatcher thread management, Themes etc.

I have several examples of using Autofac with WPF\MVVM, a couple are listed below, I would say look at the Simple.Wpf.Exceptions example:

https://github.com/oriches/Simple.Wpf.Exceptions

https://github.com/oriches/Simple.Wpf.DataGrid

https://github.com/oriches/Simple.MahApps.Template

like image 87
AwkwardCoder Avatar answered Sep 20 '22 14:09

AwkwardCoder


You can use a similar technique as your console application:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        var builder = new ContainerBuilder();
        builder.RegisterType<Cleaner>().As<ICleaner>();
        builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

        // Add the MainWindowclass and later resolve
        build.RegisterType<MainWindow>().AsSelf();

        var container = builder.Build();

        using (var scope = container.BeginLifetimeScope())
        {
            var main = scope.Resolve<MainWindow>();
            main.ShowDialog();
        }
    }
}

Be sure to mark Main with [STAThread]. Then in the project's properties, under the Application tab, set the Startup object to the Program class.

However, I am not certain of the implications of not running App.Run() and of running MainWindow.ShowDialog() instead.

To do the same using App.Run(), do the following:

1) delete StartupUri="MainWindow.xaml" from App.xaml

2) Add the following to App.xaml.cs

protected override void OnStartup(StartupEventArgs e)
{
    var builder = new ContainerBuilder();
    builder.RegisterType<Cleaner>().As<ICleaner>();
    builder.RegisterType<Repository>().AsImplementedInterfaces().InstancePerLifetimeScope();

    // Add the MainWindowclass and later resolve
    build.RegisterType<MainWindow>().AsSelf();

    var container = builder.Build();

    using (var scope = container.BeginLifetimeScope())
    {
        var window = scope.Resolve<MainWindow>();
        window.Show();
    }
}
like image 42
Swoogan Avatar answered Sep 16 '22 14:09

Swoogan