EF6: Accessing properties set by the data provider

Today I came across an Entity Frameworks edge case: I needed to retrieve the value of a property set by the data provider when an entity is added to the context. In this post I’m going to outline the problem, explain why it happens, and provide two solutions.

The problem

In my case, I’m using Entity Frameworks 6 to access an accountancy package and the property I’m retrieving is the invoice number, which is automatically assigned when an invoice is created. (Another example which springs to mind might be a “date created” property.)

Let’s forget what we know about EF for a second and try the below.

As expected, this does not result in an invoice number being written to the console. We’ve not assigned InvoiceNumber anywhere and we’ve not pulled any values from the database. There’s no reason for it to be anything other than null.

So how about this?

Thanks to the magic of Entity Frameworks, the Id really will be populated. The astute among you will have noticed that this is an example of the same problem we’re trying to address: the Id has been set by the data provider, but is made available in the original object. Sadly, this is a special case and (as shown in snippet 1) we can’t rely on this for properties other than the primary key.

EF Caching

Anyway, if you try the snippet above, you’ll find that the InvoiceNumber is still null. What gives? Well, EF has tried to be clever and has cached the invoice object. Here’s what’s happening:

Sequence diagram of EF caching

If you’re using EF in a typical database situation, caching makes a lot of sense. Why incur the performance penalty of another database hit when you could cache the data instead? In our situation though, caching is preventing us from accessing the invoice number because the cache doesn’t know about it, but (incorrectly) assumes it knows about everything the data provider does.

So, how do we bypass the cache and get the value generated by the data provider?

Solution 1: Data annotations

If you have full control of the model you’re working with, the elegant way to do this is DatabaseGenerated data annotation, which would look something like this:

Those clever EF developers have thought of everything!

Solution 2: AsNoTracking()

Unfortunately, the data model I’m using is provided by a 3rd party and I’m not able to modify it. DatabaseGenerated is off-limits here.

Luckily, our bacon is saved by AsNoTracking(): an extension method for objects implementing IQueryable. It allows us to bypass the EF cache on a query-by-query basis. Because it’s an extension method which also returns an IQueryable, we are able to chain AsNoTracking() as so:

This time, we are successful! The cache has been bypassed and InvoiceNumber now has a value.

Summary

Except for primary keys, properties generated by the data provider aren’t automatically loaded into the entity framework cache. Because the cache is assumed to have a full representation of entities added to the context, it can block these properties from being accessed. To overcome this, there are two options. The more elegant approach is to define this in the model itself using the DatabaseGenerated data annotation. If this isn’t an option (and it wasn’t for me), the cache can be bypassed at the point of data retrieval using the AsNoTracking() extension method.

Leave a Reply