How to create a robust WinForms app that uses async and DI #7190
Replies: 2 comments 3 replies
-
I don't think dependecy injection as we know it today existed back when Windows Forms SDK was conceived (mid 90s), and getting them work together is a challenge (you've already discovered this part).
In a general case this sounds dangerous. It's quite common for forms to share common objects (such as
It depends (c). Some situation dictate that a form can't be closed until all async operations are completed, and in other times it could be perfectly acceptable.
If you're having an issue with EF Core object lifetime management, perhaps asking the question in https://github.com/dotnet/efcore may be a better option. /cc: @dotnet/efteam |
Beta Was this translation helpful? Give feedback.
-
@MisinformedDNA It's worth keeping in mind two things about EF Core:
From your sample, it also looks like the DbContext used by MainForm will never be disposed or go out of scope, so that would also lead to memory continuing to be used by EF. Outside of the EF parts of this, I agree with @RussKie that using D.I. and WinForms like this is going to be challenging. Certainly from an EF perspective there is no requirement to ever use D.I. in the application--a DbContext can just be |
Beta Was this translation helpful? Give feedback.
-
I've been using async and DI for some time, but applying it to WinForms has been a struggle.
Let's start with DI. Forms are registered with a transient lifetime. By default, all child forms will be created in the root scope and, thus, won't be garbage collected until the app closes. That's a serious memory leak. To get around this, I'm creating a scope first:
When the form is closed, the scope is closed, which in turn, starts disposing all the objects injected into the form, even if the form is still using them.
Also, if async operations are still in progress in the handlers, errors are going to occur. For that, I introduced a default error handler in my base form. I also realized that I should cancel existing operations, so I added a
CancellationTokenSource
to my base form as well.Handlers would then look like this:
Even with all this, there is still a memory leak. The form is not always GCed and seems more common if there are still async operations running. Closing and reopening the form causes linear memory growth.
I analyzed many dumps and the form is always somehow referenced by an EF Core context (and the scope referenced below is not the local variable that I'm creating either).
I have no idea how that is happening/possible. I brought it up here, but didn't receive any helpful responses.
Concerns were raised internally that:
A. I shouldn't let the form be closed until all operations are complete or cancelled, even with the exception handling.
B. Disposing
scope
in the form closing event will dispose all the object in the Form, while the Form is using them and might lead to a bad state.Are these valid concerns? Do you see anything else wrong?
Beta Was this translation helpful? Give feedback.
All reactions