Ask a questionAsk a question
 

AnswerEntityKey With MergeOption.NoTracking

  • Wednesday, May 14, 2008 2:38 PMDavid DeWinter - MSFT Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I recently installed the SP1 Beta, and a coworker and I have been trying to figure out why a particular unit test is failing now after the installation. We found out later that the reason for the failure was that the EntityKey property on EntityReference objects from entities in an ObjectQuery is null after setting the MergeOption of the ObjectQuery to MergeOption.NoTracking. For example, given the following code...

    foreach (CustomerApplicationMap cam in entities.CustomerApplicationMap)

    {

        cam.CustomerReference.Load();

        Console.WriteLine(cam.Customer.ShopperReference.GetId<long?>());

    }


    ...and the GetId method defined as...

    /// <summary>

    /// Gets the ID of an associated <see cref="EntityReference"/> instance.

    /// </summary>

    /// <typeparam name="TValue">The type of the ID.</typeparam>

    /// <param name="reference">The <see cref="EntityReference"/> itself.</param>

    /// <returns>The ID of the relationship, if it exists. If not, returns the default TValue.</returns>

    public static TValue GetId<TValue>(this EntityReference reference)

    {

        if (reference == null)

        {

            return default(TValue);

        }

     

        EntityKey key = reference.EntityKey;

        if (key != null)

        {

            EntityKeyMember[] entityKeys = key.EntityKeyValues;

            if (entityKeys.Length > 0)

            {

                return (TValue)entityKeys[0].Value;

            }

        }

     

        return default(TValue);

    }


    Then we would retrieve the ShopperId just fine. However, when we insert this line before the foreach loop:

    entities.CustomerApplicationMap.MergeOption = System.Data.Objects.MergeOption.NoTracking;


    Then, the ShopperReference's EntityKey property returns null, so we don't get our ID. I'm fairly certain this behavior didn't occur before the SP1 Beta. Is this an intended change?


Answers

All Replies

  • Wednesday, May 14, 2008 3:17 PMPatrick Magee - MSFT Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

     

    This is by design. You don't get EntityKeys on EntityRefs when you use the NoTracking option.
  • Wednesday, May 14, 2008 3:20 PMDavid DeWinter - MSFT Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks for the quick response, Patrick.
  • Wednesday, May 14, 2008 3:20 PMGreg Bachraty Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I'm fairly certain this did occur with the before beta1 bits, I even posted about it somewhere. I seem to recall that in my scenario it would have only required the inclusion of the foreign key fields in the select clause of the generated SQL with no additional joins or conditions, so I'm really puzzled about why it is omitted. Currently with NoTracking only those relation keys are set where both ends are loaded (via .Include).

  • Wednesday, May 14, 2008 4:36 PMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    The reason for this behavior is that from just the model (before the mapping is introduced--which means before runtime since the mapping could be changed as a configuration change after the app is deployed) it's impossible to tell if bringing along an extra EntityKey for relationship info will just be a simple include of a few extra properties from the same DB row already being accessed or if it will require a join or some other thing.  For NoTracking queries we want to give the developer the cleanest experince with the most control, so we don't want the query system to do the same kind of implicit rewrite of the query that you get in the tracking scenarios.

     

    You can, of course, always change your query to be a projection which includes both the entity you want and the related EntityKey in a DbDataRecord in order to retrieve this information yourself with a NoTracking query.  This is, in fact, what happens under the covers in a regular tracking query.

     

    - Danny

     

  • Friday, June 13, 2008 9:05 AMGraham Hay Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Personally, I am a bit puzzled by this.

     

    Since the vast majority of queries in my application are readonly, I would like to use the MergeOption NoTracking and do my own entity cache management for certain entity types and the EntityKey would seem to be a natural choice for a cache index.

     

    Having the (Foreign) EntityKey available on the EntityReferences would mean that the related entity can be obtained directly from a cache without the need for loading the related entity from the store or having to explicitly add the foreign key on the child entity in some way (presumably using ESQL?) on the original query.

     

    Maybe another MergeOption, something like NoTrackingWithReferences (but a better name than that) that has a similar effect to NoTracking but includes the Key values for EntityReferences ?

     

    Meanwhile, I will try to work out how to query the EntityReferences explicitly.

     

    Any comments ?

    Regards

    Graham

     

  • Friday, June 13, 2008 2:11 PMDaniel Simmons - MSFTOwnerUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I'm sure everyone is tired of hearing this from me, but mechanisms to make this much easier are things we are considering for v2.  In the meantime, you can query for the EntityKey of a related entity in EntitySQL using the ref operator--something like this:

     

    select o, ref(o.Customer)

    from orders as o

     

    This will return DbDataRecords where the first column is an order and the second column is an EntityKey of the related Customer.

     

    In LINQ to Entities, it's possible to easily return the entire related customer or to return the key values of the customer, but for v1 we don't have a direct mechanism to construct the full EntityKey.  This means the equivalent query ends up being something like this:

     

    from o in ctx.Orders

    select new { Order = o, CustID = o.Customer.ID };

     

    Which returns an anonymous type just with the order and the customer id property (it does NOT return the whole customer object even though you specify what you want as though it did).  If you want actual EntityKeys, though, you then have to post-process the results a little bit to construct the key from the CustID value.

     

    - Danny

  • Friday, June 20, 2008 8:17 AMGraham Hay Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Thanks for taking the time to respond (I expect you are all very busy with preparing for RTM).

    Your help is greatly appreciated.

     

  • Thursday, August 13, 2009 5:15 PMnelsestu Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I agree.  The failure to load the entity keys is a total mystery.  The foreign keys are present in the select statement and are retrieved into the object store, yet are simply not populated on the Reference properties.

    I am using EF in a multi-tiered application and therefore need to work with entities in a detached manner.  It would be an enormous performance benefit if I could retrieve data with MergeOption = NoTracking and then use the EntityReference properties to lookup the related objects on demand (in most cases I don't need any more than the key).  Instead I am forced to retrieve with tracking turned on, incur all the memory and processing impacts of tracking objects, only to turn around and detach these objects to send them up the architecture. 

    There is an important distinction here.  With Tracking turned on, the EntityKeys are populated regardless of whether any Include is applied.  With NoTracking, EntityKeys are not populated.  The generated SQL is the same, and the foreign keys are retrieved in both cases.  This implies that with the NoTracking option, the framework skips populating the EntityReference's EntityKey even though it has the data.

    I plan on benchmarking performance for the following scenarios:
     1. NoTracking turned on, using Include to retrieve the full entityreferences and thus the key values I need
     2. Tracking turned on, which populates the key values, then immediately detaching to free up the memory and allow proper disposal of the context.

    These are disappointing options to say the least.