2

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)
Community
  • 1
  • 1
gmeister_99
  • 221
  • 1
  • 3
  • 8
  • 2
    Imo it should really work. Can you test to use the strongly typed version of `Include`: `query = query.Include(m => m.RelatedMedias);` and `query = query.Include(m => m.Tracks);`. It should make no difference at runtime but perhaps we see a bit more if it behaves different. – Slauma Dec 20 '11 at 20:56
  • Yeah already tried that I'm afraid, produces the same result. – gmeister_99 Dec 21 '11 at 10:15
  • I've tested such an example and it worked for me. "*Specified cast in not valid*" seems to make no sense to me with your example code. Can you show the full exception message and the stacktrace? – Slauma Dec 21 '11 at 10:50
  • I totally agree, that's why this has been driving me nuts. See stack trace amended above. – gmeister_99 Dec 21 '11 at 20:35
  • Your stack trace seems to be pointing at a long to int32 problem. – keni Dec 21 '11 at 20:44
  • Somehow your stacktrace doesn't really match your code (`Media_Get` for example has another signature and you have `Single` in your code, but `SingleOrDefault` in the stack trace, as already mentioned in olivehour's answer). I think the devil is in some detail you have omitted in your code. I'm pretty sure that your two versions of `Media_Get` do the same and that the second would work *if* this is *exactly* your code. – Slauma Dec 21 '11 at 22:22

1 Answers1

1

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)
);
Community
  • 1
  • 1
danludwig
  • 46,965
  • 25
  • 159
  • 237
  • Thanks for your response. My stack trace doesn't quite match as I had amended that later on during which time I was trying different things, ie... .Single as well as .SingleOrDefault. They both however produce the same result. With regards to the MediaID property, this is an Int. With regards to your proposed solution, I will take closer look at this when I'm back in the office after the xmas/new year break, however wouldn't the .EagerLoad method just be doing a .Include on the query object which would potentially produce the same result for me? Thanks again. – gmeister_99 Dec 22 '11 at 22:58
  • @gmeister_99, yes, you would have to fix this exception first. In the database, is MediaID stored as int or bigint? As keni mentioned, you have this at the top of the stack: System.Data.SqlClient.SqlBuffer.get_Int64() <-- this is 64 bit long, not 32 bit int. – danludwig Dec 23 '11 at 12:52
  • Thanks again for your responses. I've broken the entity relationships down and found the offending property which was not actually on the `Media` object but on the related `Track` object. The property was indeed a _long_ when it should have been an _int_. My original solution works fine now :) – gmeister_99 Jan 09 '12 at 01:37