gmeister_99
gmeister_99

Reputation: 221

Entity Framework 4.1 - Dynamic Eager Loading

I have an Entity Framework model (some properties have been excluded to keep it simple):

public class Media
{
    public int MediaID { get; set; }
    public ICollection<Track> Tracks { get; set; }
    public ICollection<RelatedMedia> RelatedMedias { get; set; }
}

I then have my DbContext:

public class MediaServiceContext : DbContext
{
    public DbSet<Media> Medias { get; set; }
}

I can then retrieve data using the following, and it works great:

public Media Media_Get(int id)
    {
        using (MediaServiceContext mc = new MediaServiceContext())
        {
            return mc.Medias.Include("Tracks").Include("RelatedMedias").Single(m => m.MediaID == id);
        }
    }

My question is, I may not want to load one or both of the related entities in some cases, depending on which part of my application is calling this code; how can I make the Includes dynamic?

I have tried this:

public Media Media_Get(int id, bool includeRelated, bool includeTracks)
    {                  
        using (MediaServiceContext mc = new MediaServiceContext())
        {
            IQueryable<Media> query = mc.Medias;

            if (includeRelated)
                query = query.Include("RelatedMedias");

            if (includeTracks)
                query = query.Include("Tracks");

            return query.Single(m => m.MediaID == id);
        }
    }

...but I get a 'Specified cast in not valid' exception.

I have also tried this proposed solution, but it produces a 'unable to cast DbQuery to ObjectQuery' exception. Changing the extension method in the linked solution from '(ObjectQuery)source' to '(DbQuery)source' then causes the same 'Specified cast in not valid' exception.

I have hunted high and low for a solution on this but with no luck. Any help would be much appreciated.

Amendment - Here's the stack trace:

   at System.Data.SqlClient.SqlBuffer.get_Int64()
   at lambda_method(Closure , Shaper )
   at System.Data.Common.Internal.Materialization.Coordinator.HasNextElement(Shaper shaper)
   at System.Data.Common.Internal.Materialization.Shaper`1.RowNestedResultEnumerator.MoveNext()
   at System.Data.Common.Internal.Materialization.Shaper`1.ObjectQueryNestedEnumerator.TryReadToNextElement()
   at System.Data.Common.Internal.Materialization.Shaper`1.ObjectQueryNestedEnumerator.MoveNext()
   at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source)
   at System.Linq.Queryable.SingleOrDefault[TSource](IQueryable`1 source, Expression`1 predicate)
   at API.Areas.V1.Models.RetailerManager.Media_Get(Int32 id, String retailerKey, Boolean includeLicenses, Boolean includeProperties, Boolean includeRelated, Boolean includeTracks) in C:\Users\garth\Documents\Development\WebApplications\api\Areas\V1\Models\RetailerManager.cs:line 28
   at API.Areas.V1.Controllers.RetailerController.Media(Nullable`1 id, String httpVerb, Boolean includeLicenses, Boolean includeProperties, Boolean includeRelated, Boolean includeTracks) in C:\Users\garth\Documents\Development\WebApplications\api\Areas\V1\Controllers\RetailerController.cs:line 25
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClass15.<InvokeActionMethodWithFilters>b__12()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)

Upvotes: 2

Views: 1091

Answers (1)

danludwig
danludwig

Reputation: 47375

Your stack trace shows that .SingleOrDefault() caused this exception, but I don't see .SingleOrDefault() in your code.

I do see this:

return query.Single(m => m.MediaID == id);

Is it possible that Media.MediaID is a long and not an int?

Update

As another alternative to answer your original question, I answered a question a couple of weeks ago in relation to this. The sample code in my answer has to do with dynamic order by, but we use a very similar pattern for dynamic eager loading (see the first comment after my answer).

Instead of a method signature like this:

public Media Media_Get(int id, bool includeRelated, bool includeTracks)

Your signature would look more like this:

public Media Media_Get(MediaGetter mediaGetter)

...and you would use it like this:

var media = someInstance.Media_Get(
    new MediaGetter { ID = id, }
        .EagerLoad(m => m.Tracks)
        .EagerLoad(m => m.RelatedTracks)
);

Upvotes: 1

Related Questions