2

Having a very difficult time trying to use pure DI (i.e. no framework) with WPF following MVVM. I have Mark Seemann's book; however, his solution to this seems pretty similar to what I've come up with:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        string connectionString = @"Server=(localdb)\MSSQLLocalDB;Database=RouteMiningDB;Trusted_Connection=True;";
        RouteMiningDAL.RouteMiningDataContext db = new RouteMiningDAL.RouteMiningDataContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options);
        IZIPCodeInfoRepository zipCodeRepo = new RouteMiningDAL.SQLZIPCodeInfoRepository(db);
        ZIPCodeInfoService zipCodeInfoService = new ZIPCodeInfoService(zipCodeRepo);

        ZIPCodeInfoViewModel zipCodeInfoViewModel = new ZIPCodeInfoViewModel(zipCodeInfoService);
        ZIPCodeInfoView zipCodeInfoView = new ZIPCodeInfoView(zipCodeInfoViewModel);

        MainWindow mainWindow = new MainWindow();
        mainWindow.Content = zipCodeInfoView;
        mainWindow.Show();
    }
}

Per other resources, as well as Mark's book, OnStartup is used as the Composition Root. All seems well above, however, I feel very limited as to what I can do. For example, I have set the ZIPCodeInfoView to the mainWindow.Content. Obviously with many child Windows such as:

Window w/Child Windows

This presents some challenges with layout because I can't really just set it to xxxx.Content (I can I guess, but I don't want to construct the layout in code). How do I go about this? Am I overlooking the ability to do this in XAML? It seems XAML needs a parameterless constructor which obviously does not work for DI's Constructor Injection. Thanks!

Disclaimer: I want to use pure DI.

Community
  • 1
  • 1
keelerjr12
  • 1,693
  • 2
  • 19
  • 35
  • I've not read the book. I hope it's not about wpf though. Connection strings could be defined in a config file. Views and viewmodels can be switched out using a resource dictionary ( or two ) . Oh. And it's a bad idea to retain a dbcontext for the lifespan of an app unless it's always only ever going to be using a local database on the user's machine. I would usually only switch out viewmodel dependencies (for automated testing). – Andy Mar 28 '18 at 08:25
  • You’re correct it’s poor use of Dbcontext for the life of the application but it’s my understanding that it should be ‘session per form’ also known as a lifetime per view, no? – keelerjr12 Mar 28 '18 at 13:59
  • That's application entry point you have there. The user starts the app up, it opens a connection. He walks away and has an 8 hour meeting... The connection is still open. – Andy Mar 28 '18 at 14:17
  • Instantiating a dbcontext per window isn't an awful idea for small apps with few users or a local database. You get change tracking that way. A lot of companies are small and only really have like 10 people will use a database in an app. If you have more users and central database then it's not so good. – Andy Mar 28 '18 at 14:20

1 Answers1

0

Nice that you would like to use pure DI. Your question is good. I hoped that the 2nd edition of the book would answer it. It didn't have a plain example/answer for that if I recall it correctly.

However, the idea of Pure DI is that all the dependencies are visible (using construction injection) up front in the app entry point a.k.a. the composition root.

In your case I would chain it up in the following way: public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);

    string connectionString = @"Server=(localdb)\MSSQLLocalDB;Database=RouteMiningDB;Trusted_Connection=True;";
    RouteMiningDAL.RouteMiningDataContext db = new RouteMiningDAL.RouteMiningDataContext(new DbContextOptionsBuilder().UseSqlServer(connectionString).Options);
    IZIPCodeInfoRepository zipCodeRepo = new RouteMiningDAL.SQLZIPCodeInfoRepository(db);
    ZIPCodeInfoService zipCodeInfoService = new ZIPCodeInfoService(zipCodeRepo);
    StockHistoryService stockHistoryService = StockHistoryService();
    StockHistoryViewModel stockHistoryViewModel = new StockHistoryViewModel(stockHistoryService);

    ZIPCodeInfoViewModel zipCodeInfoViewModel = new ZIPCodeInfoViewModel(zipCodeInfoService, stockHistoryViewModel);
    ZIPCodeInfoView zipCodeInfoView = new ZIPCodeInfoView(zipCodeInfoViewModel);

    MainWindow mainWindow = new MainWindow();
    mainWindow.Content = zipCodeInfoView;
    mainWindow.Show();
}

}

In that way the main window depends on the StockHistoryViewModel which depends on the StockHistoryService. For some view model (pop-up/modal window etc) I would use factory pattern with DI so that view model would be created only when/if needed. But it hides the view model's dependecies... Dependency Injection vs Factory Pattern

J Pollack
  • 2,788
  • 3
  • 29
  • 43