C# – Entity Framework: Working with detached objects and attached ones


Firstly, let me set out what I'd like to do. Assume I have three types of EntityObject, MetaData, Data1 and Data2. MetaData is, as one would expect, has a reference to one instance each of Data1 and Data2. Now, for each MetaData, I can calculate a value.

So far so simple. Now, I would like the user to play around with various combinations of Data1 and Data2, and see what values they can get. This obviously requires creation of instances of MetaData. Now, if I don't want to cluter the database with all these entries of MetaData, then I'd like to create the entity objects in the in-memory context without calling a SaveChanges() to write it back to the DB. However, this presents a problem in that whenever I try to access the Data1 and Data2 references of the in-memory MetaData, I end up with the following exception:

InvalidOperationException is unhandled

The source query for this EntityCollection or EntityReference cannot be returned when the related object is in either an added state or a detached state and was not originally retrieved using the NoTracking merge option.

If I do as suggested, and "committ" the object to DB, I end up with the clutter problem.

Anyhow, the guilty code looks something like this:

MetaData temp = MetaData.CreateMetaData(0);

MetaData.Data1 = <existing Data1 from context>;
MetaData.Data2 = <existing Data2 from context>;

//Exception here
if (!MetaData.Data1Reference.isLoaded)

It seems this guy had a similar problem.

Best Solution

IsLoaded is only relevant for properties of instances which have been materialized from a database. As you've discovered, it does not return useful information for instances which have not been materialized from a database.

Therefore, you should change the way you test whether or not you want to call Load(). If you know that you will be working with instances of MetaData which you have only created in memory and not materialized from a database, then you could write code like this:

if ((temp.EntityState != System.Data.EntityState.Added) && 
    (!temp.Data1Reference.IsLoaded)) temp.Data1Reference.Load();

I'm glossing over some subtleties, here. For starters, EntityState is declared with FlagsAttribute, so it can contain Added without being equal to Added. Also, none of this is necessary if Data1Reference is non-null, so you might just want to test for that first. The point is, you can write code which suits your situation, but it must account for the full state of temp, not just its properties.