I am about to create a Desktop App (with .NET windows forms)
Essentially, I want to create an n-tier app but I also want loose coupling between layers. However, I am not very sure if this is a good approach for windows forms
And now I just wonder if it would be really a wise choice to use any IoC (StructureMap, Ninject, Spring.Net), I have used them before for Asp.Net web applications but what makes me doubt now is the fact that working with windows forms my business entities will persist when I navigate through tabs and unlike than web forms or mvc apps where it would be necesary to inject my business entity for every new request that is performed, I mean this because of the Asp.Net page life cycle where is performed the initialization and controls instantiation.
This it is kind of a long-term development project, it combines maintenance tracking, inventory, work orders, and management reporting. I am currently working on a proposal for its architecture.
Maybe I am misunderstanding the point of using an IoC, so please tell me what do you think would be a better choice?
Any point of view would be appreciated.
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:
- Meanwhile... on the command side of my architecture
- Meanwhile... on the query side of my architecture
- 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