e36M3
e36M3

Reputation: 6032

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?

Upvotes: 5

Views: 2895

Answers (3)

Wouter Schut
Wouter Schut

Reputation: 926

One completely different way is writing all changes to the database directly (with short lived database contexts). And then add some kind of draft/versioning on top of that. That way you can still offer ok/undo/cancel functionality.

Upvotes: 0

ChrisNel52
ChrisNel52

Reputation: 15173

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();
        }

Upvotes: 2

Denis Troller
Denis Troller

Reputation: 7501

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"...

Upvotes: 3

Related Questions