6

I'm trying to apply the advice in this post: Tip 22 - How to make Include really Include

It suggests a workaround for ensure eager loading works in the Entity Framework (4.2). That workaround involves casting the IQueryable to an ObjectQuery.

However, when I attempt this, as shown in the post, the query returns a null.

My query is (ctx is a DbContext):

IEnumerable<Coupon> coupons =
    from x in ctx.Coupons
    where x.LanguageCode.Equals("EN", StringComparison.InvariantCultureIgnoreCase) && x.CategoryId == MainCategoryId && x.Date >= fromDate && x.Date <= toDate
    select x;

That works as expected.

However, when I use,

IEnumerable<Coupon> coupons =
    (from x in ctx.Coupons
     where x.LanguageCode.Equals("EN", StringComparison.InvariantCultureIgnoreCase) && x.CategoryId == MainCategoryId && x.Date >= fromDate && x.Date <= toDate
     select x) as ObjectQuery<Coupon>;

it assigns a null to "coupons".

Any idea what I'm doing wrong?

dommer
  • 19,610
  • 14
  • 75
  • 137
  • What if you use `var` instead of `IEnumerable`? `IQueryable` and `IEnumerable` are not the same thing. – GalacticCowboy Dec 16 '11 at 18:35
  • 1
    Is your ctx a DbContext or an ObjectContext? ObjectQuerys are only used with ObjectContexts. You can also inspect coupons.GetType() in the debugger, to see what type the query really is. –  Dec 16 '11 at 18:42
  • Thanks. I've update the post to say I'm using a DBContext. – dommer Dec 17 '11 at 12:27
  • 1
    In that case, the only answer can be "You cannot convert an `IQueryable` to an `ObjectQuery` unless that `IQueryable` really is an `ObjectQuery`." I'm wondering why you really need that, though. In your query, you wouldn't need the workaround in the first place, but if you did, does `(from x in ctx.Coupons ... select x).Include(...)` not do the trick, assuming you're `using System.Data.Entity;`? –  Dec 17 '11 at 12:38
  • @Hvd. Thanks. I didn't have System.Data.Entity in the using declarations and was relying on Intellisense to determine what was allowed - so didn't think I could use .Include there! And then got into reading an old blog post that "confirmed" that misconception. Doh! Shouldn't code when tired and frustrated, I guess. If you want to post an answer, I'll then accept it - or I'll self-answer in a few days, referencing your comment. – dommer Dec 17 '11 at 17:55
  • @dommer Thanks, I've posted it as an answer. Glad to read it's working. –  Dec 17 '11 at 18:08

3 Answers3

5

From the comments to an answer:

Casting to an ObjectQuery<T> only works when the query really is an ObjectQuery<T>, it won't work on any other IQueryable<T>. Since you're using a DbContext instead of an ObjectContext, the queries are of a different type. However, you do not need to cast to the correct type, the DbExtensions.Include extension methods in the System.Data.Entity namespace accept any IQueryable<T> type, and call the appropriate underlying Include method. You can use this, avoid the cast, and thereby avoid explicitly specifying the query's type in your code.

4

What you are doing is equivalent to this

IEnumerable<Coupon> coupons =
    from x in ctx.Coupons
    where x.LanguageCode.Equals("EN", StringComparison.InvariantCultureIgnoreCase) && x.CategoryId == MainCategoryId && x.Date >= fromDate && x.Date <= toDate
    select x;

ObjectQuery<Coupon> converted = coupons as ObjectQuery<Coupon>;

, with coupons not null and converted null.

Which means the cast is failing.
This does not solve your problem, but at least identifies it.

Perhaps you are using a different version of Entity Framework?

ANeves
  • 6,219
  • 3
  • 39
  • 63
  • Thanks. It could indeed be that this workaround no longer applies, as the post was in 2009. – dommer Dec 17 '11 at 12:28
0

Getting the SQL Query From an Entity Framework IQueryable helped me solve this issue to satisfy a slightly different requirement, I need to access the TraceString AND the underlying parameters so I could inject a few IQueryable<> expressions into a .SqlQuery<>()

When you inspect an IQueryable during a debug session you can see that internally it uses ObjectQuery, but it does not Inherit from ObjectQuery so you cannot cast it directly. We can however use Reflection to access the internal query object.

Here's Steve Fenton's code wrapped into an Extension Method:

    /// <summary>
    /// Extract the Internal Object Query from an IQueryable, you might do this to access the internal parameters for logging or injection purposes
    /// </summary>
    /// <remarks>Sourced from https://www.stevefenton.co.uk/2015/07/getting-the-sql-query-from-an-entity-framework-iqueryable/ </remarks>
    /// <typeparam name="T">Entity Type that is the target of the query</typeparam>
    /// <param name="query">The query to inspect</param>
    /// <returns>Object Query that represents the same IQueryable Expression</returns>
    public static System.Data.Entity.Core.Objects.ObjectQuery<T> ToObjectQuery<T>(this IQueryable<T> query)
    {
        // force the query to be cached, otherwise parameters collection will be empty!
        string queryText = query.ToString();
        queryText = queryText.ToLower(); // stop compiler from optimising this code away because we don't do anything with queryText parameter!

        var internalQueryField = query.GetType().GetProperties(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("InternalQuery")).FirstOrDefault();
        var internalQuery = internalQueryField.GetValue(query);
        var objectQueryField = internalQuery.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("_objectQuery")).FirstOrDefault();
        return objectQueryField.GetValue(internalQuery) as System.Data.Entity.Core.Objects.ObjectQuery<T>;
    }

Now your code can look like this:

IEnumerable<Coupon> coupons =
    (from x in ctx.Coupons
     where x.LanguageCode.Equals("EN", StringComparison.InvariantCultureIgnoreCase) && x.CategoryId == MainCategoryId && x.Date >= fromDate && x.Date <= toDate
     select x).ToObjectQuery();
Chris Schaller
  • 13,704
  • 3
  • 43
  • 81