Does DI make sense in a desktop app?

99封情书 提交于 2019-12-04 05:47:43

It still makes sense to use DI / IoC in your case, because it's all about surrendering control of where the dependencies come from, who manages their life-time, etc.

In that sense, the principle Inversion of Control is pretty much platform independent, it may work slightly differently in terms of wire-up between ASP.NET and WinForms, but the principal is the same.

So whereas in ASP.NET typically the Inversion of Control is typically realised via Dependency Injection and mostly via Constructor Injection into Controllers, etc, that doesn't mean you have to follow the same pattern in Windows Forms - for example, you could just make a global instance of your IoC Container available to all Forms, and have them get their depdendencies via something like

var SomeBusinessObject = container.Get<SomeBOType>(); //Ninject style.

In this case it's still IoC (the form doesn't directly create the dependency, it has no idea if the container is giving it a brand new instance, or a single static instance that's globally shared, it doesn't know when it will be destroyed, etc) but it's not strictly speaking Dependency Injection - however you gain all of the benefits of having complex dependency graphs managed for you by the IoC framework.

Also bear in mind that you can always handle the event where the user switches tabs, and treat that as though it were a brand new "request", throwing away and re-getting your dependencies then, if for some reason that is important for how the app should work.

Your question is odd. You're question implies that you would write your business layer for a Winforms app in a different way than you would in a web application, but if you apply layering correctly, the business layer should be completely independent of the used technology. So in that sense, if you would apply the Dependency Injection pattern in the business layer of a web app, you should also apply it in the business layer of a desktop application.

I'm currently working on a Winforms project myself and using the Dependency Injection pattern (and an IoC container) extensively. For me there's not a question whether DI should be used; it follows naturally from applying the SOLID principles. Whether or not you should use a IoC container however, is a totally different question, although for the types of application's I write and the type's of architectures I use, I can't imagine life without it.

Although desktop applications are very different in nature than web applications are, I use the same patterns on both types of applications. For instance, I use constructor injection in my Windows Forms classes and those forms mainly depend on a few generic interfaces, namely:

  • IRepository<TEntity> (the repository pattern) for loading an entities.
  • IQueryHandler<TQuery, TResult> for doing complex or custom queries of all sorts.
  • ICommandHandler<TCommand> for execution of use cases (processing user actions).

I use the same abstractions in the web applications I build.

Those interfaces helped me a few months back to change this desktop application from a 2-tier application (all business logic ran in the desktop application) to a 3-tier application (where all business logic is now moved to a WCF service). We were able to do this without having to change any code in the Forms.

In the 2-tier model we didn't inject ICommandHandler<TCommand> implementations directly, but injected a (singleton) proxy class that would create a new implementation each time it was called. For instance, when the form called the injected ICommandHandler<ProcessOrder>, the actual CommandHandlerProxy<ProcessOrder> would start a lifetime scope (a lifestyle that mimics the per-request lifestyle of a web application) and would create the real ProcessOrderCommandHandler class that would do the actual logic. By doing this we ensured that a single unit of work (Entity Framework's DbContext in our case) would be injected in all classes within this 'request'. All of course with dependency injection all the way down the call graph.

In the new 3-tier model, the forms are injected with an WcfProxyCommandHandler<TCommand> which will serialize the given command to JSON and send it over to the WCF service, which will pick it up, deserializes the command, creates the ProcessOrderCommandHandler and executes the command.

But bear in mind that this model is probably very different than what you're probably used to do. For instance:

  • The real entities are hidden behind the WCF service. The desktop application knows nothing about them.
  • Instead DTOs are returned from the WCF service when data is requested through the IQueryHandler<TQuery, TResult> abstraction.
  • We use Entity Framework 5 (POCO classes with T4 and designer).
  • Those DTOs are (mostly) used for reading; they're not sent back to the server for updating.
  • Any request for a change of state (the execution of a use case) is done by sending a command message to the server (through the ICommandHandler<TCommand> abstraction).
  • One single use case is encapsulated in single class that implements the ICommandHandler<TCommand> interface.

And as I said, it's Dependency Injection all the way down and this and the described design gives us much flexibility. For instance:

  • We find it very easy to add new functionality.
  • We find it very easy to add new cross-cutting concerns.
  • It lowers the mental barrier; it makes the application easier to maintain and much less likely to break in unexpected ways.

There is however one thing I found out in the process:

  • Binding in winforms is optimized to work with DataSets. If you try anything else (Poco's, Entity Framework entities, etc) you will get some very frustrating moments where you find out that the support for anything else but DataSets is minimal. It's clear that Microsoft hasn't invested in this area and won't investing in this area anymore. To workaround these limitations we wrote our own BindingList<T> implementation and found it very hard to create an implementation that works correctly with sorting and filtering (especially since our DTOs don't implement INotifyPropertyChanged). We also wrote our own infrastructure to add DataAnnotations validation support to Winforms.

If you want to read more about the designs I use, please read these articles:

  1. Meanwhile... on the command side of my architecture
  2. Meanwhile... on the query side of my architecture
  3. Writing Highly Maintainable WCF Services

Yes, Today there is a greater focus than ever on reusing existing components and wiring together disparate components to form a cohesive architecture. But this wiring can quickly become a daunting task because as application size and complexity increase, so do dependencies. One way to mitigate the proliferation of dependencies is by using Dependency Injection (DI), which allows you to inject objects into a class, rather than relying on the class to create the object itself.

Containers provide a layer of abstraction in which to house components. DI containers, in particular, reduce the kind of dependency coupling I just described by providing generic factory classes that instantiate instances of classes. These instances are then configured by the container, allowing construction logic to be reused on a broader level.

You can read more here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!