5

This is from docs about lifetime of DBContext:

When working with Windows Presentation Foundation (WPF) or Windows Forms, use a context instance per form. This lets you use change-tracking functionality that context provides.

I've read that lifetime of a DBContext should be short as possible, but obviously in the case of WinForms (which I am using), this rule doesn't apply and it is recommended something else.

The Current Setup

Also my application follows MVC pattern and in my Controller, I have initialized DBContext. Now when the form is closed, among other things, the controller instance is disposed as well, and that is where context dies. Also I am using Database First approach, if matters.

Now having a context always opened (per form) seems that it has its own conveniences (you can easily track changes on a context), but for example, I find myself (as an EF beginner) in some doubts... Take a look at this situation:

I have IList of users (user entity) that is initialized when the form is created. And I run two (or even more) instances of my application because it is meant to work in multi-user environment. Now, say that user A and user B, both load the same record (user management screen, so both of them are admins)... Now when things go like this:

user A makes changes (update) on some record user B makes changes (update) on the same record and save it to a database user A tries to save a DbUpdateConcurrencyException is thrown...

Now this is fine, I am aware of a term Optimistic Concurrency, and say that both user A and B were updating the same row (or same column), I would handle an exception like this (correct me if I am missing something):

catch (DbUpdateConcurrencyException ex)
{
    ex.Entries.Single().Reload();
    //Here I reload UI with a new changes, and ask a user to try again         
}

Now this seem to be working... Because my context is opened as long as my form is alive, when I do Reload() on ex.Entries.Single(), a specific user from a list (remember that I have mentioned a list of users before) is refreshed with actual database values, so when I reload the view, the user gets the current state.

The Actual Problem

So far so good...But for example in this situation where:

user A deletes the row

user B tries to update and save the changes on the same (already deleted row)

I run into multiple problems, and I don't know what would be a right way to handle it.

I guess, I should update the users list, but don't know how to do this without disposing a current context. So that would be a question... How to reload user list from a database, without disposing a context in this block of code:

catch (DbUpdateConcurrencyException ex)
{
    // Reload the user list with navigation properties fully initialized (note that user list is what would context.korisnik.ToList() would return)
    // Reload the UI
}

What would be a proper way to handle Optimistic Concurrency errors in situations like this?

CSDev
  • 3,177
  • 6
  • 19
  • 37
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
  • You could use shorter lived contexts or dispose of the context and recreate it as soon as it becomes invalid (ie. you encounter a concurrency exception). As far as the UI you should *probably* show an error message and only allow the user to close the form OR reload the content (using a new DbContext instance). That will give them a chance to copy / take note of their changes that they wanted to commit before reloading/backing out. – Igor Jul 27 '17 at 13:11
  • @Igor I had that in mind ( re-creating a context when concurrency exception is throw). Have to try. Also, for the people who voted to close...The answer to this question doesn't fall into *Opinion Based* category, because I am asking how to reload `context.user` with database values without closing the context and I am asking for some thoughts about this implementation... – Whirlwind Jul 27 '17 at 13:19
  • You cold detach the entity from the `DbContext` and then try to retrieve it again (*in the event it exists ie it's not a delete*). – Igor Jul 27 '17 at 13:38
  • @Igor When I do Reload(); a deleted row (entity) is already detached... Or I am not following what you said? What I did, is re-initialized the context, as well as the user list, and have reload the UI so it works as I wanted... But still, I wonder what would be a correct way to handle this without re-creating a new context... – Whirlwind Jul 27 '17 at 21:32
  • That is what I meant, detach the now deleted entity from the existing `DbContext`. – Igor Jul 28 '17 at 10:55
  • @Igor Yeah, thank you, actually it worked like that even without creating a new context. I just did a `ex.Entries.Single().Reload();` and after that I reinitialized my user list `_context.user.Include(u => u.privilege).ToList();` – Whirlwind Jul 28 '17 at 11:45
  • Does creating new context have some impact on the performance? – Hp93 Oct 29 '19 at 04:44

0 Answers0