How To Get the Context from an Entity Framework Object

Despite what I may have theorized on the topic in the heady days of yore, a descendant of ObjectContext in a Windows application is a very special lady who must not be treated in the same way as her web-bound sisters. And no, she’s not standing over my shoulder making me write that. Shut up.

Start Again

What I’m trying to say is that, unlike in a web context, it is within reason for a Windows app to allow its users to have a good handful of forms open all at once for editing data, and that in such a case, a separate Entity Framework context should be created for each form so that our dear indecisive users can Cancel or Save changes to their heart’s content while still receiving the rainbow-y, unicorn-y experience they have come to expect from the modern computer program.

Simple enough. But our code begins to sprout hair when, in an attempt to be good little concern separatists, we make efforts to ensure that the application’s business rules are housed squarely in the definitions of the entities themselves.

I mean, if we’re creating context instances up in the User Interface layer, how can our entities down in the Model validate themselves against other data in the same context? Passing the context into an object’s validation code is awkward at best, and impossible if that validation occurs in event handler methods whose signatures are dictated by the gods themselves.

Wait—no big. An entity holds a reference to its context. Right?

no-context_thumb

Um.

It would seem that said gods have missed a spot. Of course, I will press on arrogantly enough to not only call them out on it, but to just go ahead and fix that up for them, too.

Hand me that roll of duct tape, won’t you? And my pinking shears.

Make It Work

First, through the miracle of interfaces, we can add a Context property to our entities:

public interface IEntityWithContext
{
    AdventureWorksEntities Context { get; set; }
}

Then, in a partial class extending our context, we’ll deftly thrust some code upon the ObjectMaterialized event that sets this Context property to reference the context doing the materializing:

public partial class AdventureWorksEntities
{
    partial void OnContextCreated()
    {
        // set the Context reference whenever an entity is loaded
        this.ObjectMaterialized += (sender, e) => {
            (e.Entity as IEntityWithContext).Context = this;
        };
    }
}

Finally, we’ll ensure that each of the entities defined in our model implement our IEntityWithContext interface, like this:

partial class Contact : IEntityWithContext
{
    /// <summary>
    /// Gets the entity's ObjectContext.
    /// </summary>
    public AdventureWorksEntities Context { get; set; }
 
    // ...
}

Now when we are validating changes to an entity, we can compose queries against its context using our shiny new Context property:

partial class Contact : IEntityWithContext
{
    /// <summary>
    /// Gets the entity's ObjectContext.
    /// </summary>
    public AdventureWorksEntities Context { get; set; }
 
    /// <summary>
    /// Ensure that e-mail addresses 
    /// remain unique to each contact.
    /// </summary>
    /// <param name="value"></param>
    partial void OnEmailAddressChanging(string value)
    {
        bool exists = (from p in this.Context.Contacts
                       where p.EmailAddress == value
                          && p != this
                       select p).Any();
        if (exists)
            throw new ArgumentException("A contact with this e-mail address already exists.");
    }
}

Unicorns, rainbows, and tempting the wrath of the gods with adhesive silvery cloth as advertised.

Thanks to my team mate Mike Getty for bringing this one to my attention, and to this guy for coincidentally agreeing with me.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s