Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long living Entity Framework context - best practice to avoid data integrity issues

As a very rudimentary scenario, let's take these two operations:

UserManager.UpdateFirstName(int userId, string firstName)
{
    User user = userRepository.GetById(userId);
    user.FirstName = firstName;
    userRepository.SaveChanges();
}

InventoryManager.InsertOrder(Order newOrder)
{
    orderRepository.Add(newOrder);
    orderRepository.SaveChanges();
}

I have only used EF in web projects and relied heavily on the stateless nature of the web. With every request I would get a fresh copy of the context injected into my business layer facade objects (services, managers, whatever you want to call them), all of the business managers sharing the same instance of the EF context. Currently I am working on a WPF project and I'm injecting the business managers and subsequently the repositories that they use directly into the View Model.

Let's assume a user is on a complicated screen and his first button click calls the UpdateFirstName() method. Let's assume SaveChanges() fails for whatever reason. Their second button click will invoke the InsertOrder() method.

On the web this is not an issue as the context for operation #2 is not related (new http request) to the context used by operation #1. On the desktop however, it's the same context across both actions. The issue arises in the fact that the user's first name has been modified and as such is tracked by the context. Even though the original SaveChanges() didn't take (say the db was not available at that time), the second operation calling SaveChanges() will not only insert the new order, it will also update the users first name. In just about every cause this is not desirable, as the user has long forgotten about their first action which failed anyway.

This is obviously being a silly example, but I always tend to bump into these sort of scenarios where I wish I could just start fresh with a new context for every user action as opposed having a longer lived (for the life of the WPF window for example) context.

How do you guys handle these situations?

like image 963
e36M3 Avatar asked Dec 28 '11 17:12

e36M3


2 Answers

A very real interrogqation when coming from ad hoc desktop apps that hit the database directly.

The answer that seems to go with the philosophy for ORMs is to have a context that has the same lifespan as your screen's. The data context is a Unit Of Work implementation, in essence. For a web app, it is obviously the request and it works well.

For a desktop app, you could have a context tied to the screen, or possibly tied to the edit operation (between loading and pressing "save"). Read only operations could have a throwaway context (using IDs to reload the object when necessary, for example when pressing a "delete button" in a grid). Forget about a context that spans the entire application's life if you want to stay aware of modifications from other users (relationship collections are cached when first loaded). Also forget about directly sharing EF entities between different windows, because that effectively makes it an app-wide and thus long lived context.

It seems to me ORMs tend to enforce a web-like design on desktop apps because of this. No real way around this I'm afraid. Not that it is necessary a bad thing in itself. You should normally not be attacking the database at the desktop level if it is to be shared between multiple instances. In which case you would be using a service layer, EF would be in its element and your problem would be shifted to the "service context"...

like image 67
Denis Troller Avatar answered Oct 12 '22 08:10

Denis Troller


We have a large WPF application that calls WCF services to perform CRUD operations like ‘UpdateFirstName()’ or ‘InsertOrder()’. Every call into the service creates a new ObjectContext, therefore we don’t need to worry about inconsistent ObjectContexts hanging around.

Throw away the ObjectContext as soon as you are done with it. Nothing’s wrong with generating a new ObjectContext on the fly. There’s unnoticeable overhead in creating a new ObjectContext and you’ll save yourself many future bugs and headaches.

Suppose you do create a new ObjectContext each time a method is called on your repository, the below snippet would still give you transaction support by using the TransactionScope class. For example,

        using (TransactionScope scope = new TransactionScope())
        {
            UserManager.UpdateFirstName(userid,firstName);
            InventoryManager.InsertOrder(newOrder);

            scope.Complete();
        }
like image 43
ChrisNel52 Avatar answered Oct 12 '22 09:10

ChrisNel52