Question

I have the following model (simplified):

abstract class CartItem { EntityReference<Cart> Cart; }
class HotelCartItem : CartItem { EntityReference<Hotel> Hotel; }
class TransferCartItem : CartItem { }
class Hotel { }

As expressed "graphically":

CartItem
|<- HotelCartItem
|   |-> Hotel
|
|<- TransferCartItem

Now I want to load all CartItems and include data from the Hotel class if the type of CartItem is a HotelCartItem.

This is how I'm trying to do it, but it fails with a "does not declare a navigation property with the name 'Hotel'."

var q = from cartitems in context.CartItems
            .Include("Hotel")
        where cartitems.CART_ID == CartID
        select cartitems;

If I leave out the .Include("Hotel") the Hotel property of CartItems of type Hotel is null.

My question:
Is there a way to get around this?

Was it helpful?

Solution 2

I ended up splitting the query into several parts:

  1. Load the parent item, a "Cart".
  2. For each of the different types I got (HotelCartItem and TransferCartItem) I queried the db for a set of only that type:
private IQueryable<T> GetCartItemQuery<T>(Guid CartID) where T : CartItem
{
    if (typeof(T) == typeof(HotelCartItem))
    {
        var q = from ci in db.CartItems.OfType<T>()
                    .Include("Hotel")
                where ci.CART_ID == CartID
                select ci;
        return q;
    }
    else
    {
        var q = from ci in db.CartItems.OfType<T>()
                where ci.CART_ID == CartID
                select ci;
        return q;
    }
}

Call it with:

var hotels = GetCartItemQuery<HotelCartItem>(CartID);
var transfers = GetCartItemQuery<TransferCartItem>(CartID);

3 . Add the CartItems to the collection of the Cart-object.

OTHER TIPS

Eager loading of navigation properties on sub classes is tricky. I haven't found any other way except loading them separately. The easy way to do that is registering custom ObjectMaterialized handler (only in EF 4.0) on ObjectContext:

context.ObjectMaterialized += RegisterEagerLoadingStrategies;

And the handler method looks like:

private static void RegisterEagerLoadingStrategies(object sender, ObjectMaterializedEventArgs e)
{
  var context = (ObjectContext)sender;

  var cartItem = e.Entity as HotelCartItem;
  if (cartItem != null)
  {
    context.LoadProperty(cartItem, o => o.Hotel); 
  }
}

This solution has N + 1 problem. N + 1 means that if your main query returns N HotelCartItems you will execute N + 1 queries in database (each LoadProperty calls additional query). Also this method is called for every loaded entity (not only for HotelCartItem). So this solution is really bad for loading large number of entities.

Another approach of loading navigation properties from related entities is dividing your query into two queries. First query will load CartItems and second query will load Hotels for cart items loaded in first query (same conditions). References to hotels in cart items should be automatically set if your entities are still attached to context.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top