1

TL;DR

I want to write a query in LINQ to Entities and tell it that I'll never load the child entities of an entity. How do I do that without projecting?

Eg,

return (from a in this.Db.Assets
        join at in this.Db.AssetTypes on a.AssetTypeId equals at.AssetTypeId
        join ast in this.Db.AssetStatuses on a.AssetStatusId equals ast.AssetStatusId
        select new {
            a = a,
            typeDesc = at.AssetTypeDesc,
            statusDesc = ast.AssetStatusDesc
        }).ToList().Select(anon => new AssetViewModel(anon.a, anon.typeDesc, anon.statusDesc)).ToList();

I want the entity called Asset pulled into a on the anonymous type I'm defining, and when I call ToList(), I don't want the Assets' children, Status and Type, to lazy load.

EDIT: After some random Visual Studio autcomplete investigation, much of this can be accomplished by turning off lazy loading in the DbContext:

this.Db.Configuration.LazyLoadingEnabled = false;

Unfortunately, if your work with the query results does have a few child tables, even with LazyLoadingEnabled turned off, things may still "work" for some subset of them iff the data for those children has already been loaded earlier in this DbContext -- that is, if those children have already had their context cached -- which can make for some surprising and temporarily confusing results.

That is to say, I want to explicitly load some children at query time and completely sever any relationship to other child entities.

Best would be some way to actively load some entities and to ignore the rest. That is, I could call ToList() and not have to worry about throwing off lots of db connections.


Context

I have a case where I'm hydrating a view model with the results of a LINQ to Entities query from an entity called Asset. The Asset table has a couple of child tables, Type and Status. Both Type and Status have Description fields, and my view model contains both descriptions in it. Let's pretend that's as complicated as this query gets.

So I'd like to pull everything from the Asset table joined to Type and Status in one database query, during which I pull the Type and Status descriptions. In other words, I don't want to lazy load that info.

WET (Woeful Entity reTranscription?)

What we're doing now, which does exactly what I want from a connection standpoint, is the usual .Select into the view model, with a tedious field matchup.

return (from a in this.Db.Assets
    join at in this.Db.AssetTypes on a.AssetTypeId equals at.AssetTypeId
    join ast in this.Db.AssetStatuses on a.AssetStatusId equals ast.AssetStatusId
    select new AssetViewModel
    {
        AssetId = a.AssetId,
// *** LOTS of fields from Asset removed ***
        AssetStatusDesc = ast.AssetStatusDesc,
        AssetTypeDesc = at.AssetTypeDesc
    }).ToList();

That's good in that the Status and Type child entities of Asset are never accessed, and there's no lazy load. The SQL is one join in one database hit for all the assets. Perfect.

The worry is all the repeated jive in // *** LOTS of fields from Asset removed ***. Currently, we've got that projection in every freakin query, which obviously isn't DRY. And it means that when the Asset table changes, it's rare that the new field is included in every projection (because humans), which stinks.

I don't see a quick way around the query, btw. If I want to do it in a single query, I have to have the joins. I could add wheres to it in separate methods, but I'm not sure how I'd skip the projection each time. Or I could add joins to the query in cascading methods, but then my projection is still "repository bound", which isn't best case if I'm using these sorts of queries elsewhere. But I'm betting I'm stupiding something here.

Dumb

When I tried adding a cast to my view model from asset and changing to something like this, which is beautiful from a code standpoint, though I get bitten by lazy loading -- two extra database hits per Asset, one for Status and one for Type.

return (from a in this.Db.Assets
        select a).ToList().Select(asset => (AssetViewModel)asset).ToList();

Just as we would expect, since I'm using lines like...

AssetTypeDesc = a.AssetType.AssetTypeDesc,

... inside of the casting code. So that was dumb. Concise, reusable, but dumb. This is why we hate folks who use ORMs without checking the SQL. ;^)

Overly clever, sorta

But then I tried getting too clever, with a new constructor for the view model that took the asset entity & the two description values as strings, which ended up with the same lazy load issue (because, duh, the first ToList() before selecting the anonymous objects means we don't know how the Assets are going to be used, and we're stuck pulling back everything to be safe (I assume)).

//Use anon type to skirt "Only parameterless constructors 
//and initializers are supported in LINQ to Entities,"
//issue.
return (from a in this.Db.Assets
        join at in this.Db.AssetTypes on a.AssetTypeId equals at.AssetTypeId
        join ast in this.Db.AssetStatuses on a.AssetStatusId equals ast.AssetStatusId
        select new {
            a = a,
            typeDesc = at.AssetTypeDesc,
            statusDesc = ast.AssetStatusDesc
        }).ToList().Select(anon => new AssetViewModel(anon.a, anon.typeDesc, anon.statusDesc)).ToList();

If only there was some way to say, "cast these anonymous objects to a List, but don't lazy load the Asset's children while you're doing it." <<< That's my question, natch.


I've read some about DataLoadOptions.LoadWith(), which probably provides an okay solution, and I might end up just doing that, but that's not precisely what I'm asking. I think that's a global-esque setting (? I think just for the life of the data context, which should be the single controller interaction), which I might not necessarily want to set. I may also want ObjectTrackingEnabled = false, but I'm not grokking yet.

I also don't want to use an automapper.

Community
  • 1
  • 1
ruffin
  • 13,513
  • 8
  • 72
  • 118
  • This is probably a duplicate of http://stackoverflow.com/questions/24022957/entity-framework-how-to-disable-lazy-loading-for-specific-query. – mcse3010 May 02 '16 at 19:14
  • @mcse3010 I'll edit to make it more clearly ask the question I mention below. There seems to be an issue with info already cached popping up even when `LazyLoadingEnabled` is `false`. That is, entities can still be loaded even when lazy loaded is off in some edgy, but still common enough, cases. I want them to be draconianly "never-loaded". – ruffin May 03 '16 at 18:20

1 Answers1

1

Painfully, after some random Visual Studio autcomplete investigation, this might be as easy as turning off lazy loading in your DbContext:

this.Db.Configuration.LazyLoadingEnabled = false;

The wacky thing is that if your work with the query results does have a few child tables, even with LazyLoadingEnabled turned off, things may still "work" for some subset of them iff the data for those children has already been loaded earlier in this DbContext -- that is, if those children have already had their context cached -- which can make for some surprising and temporarily confusing results.

Better would be to be able to cherry pick what children are "lazy-loading eligible".

I may need to update the question to make it cover this variation of the original question.

ruffin
  • 13,513
  • 8
  • 72
  • 118