在LINQ to Entities查询中将某些子实体标记为“从不加载”

时间:2016-01-12 14:50:15

标签: c# entity-framework linq-to-entities

TL; DR

我想在LINQ to Entities中编写一个查询,并告诉它我永远不会加载实体的子实体。我怎么做而不预测

例如,

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();

我希望名为Asset的实体在我正在定义的匿名类型上被拉入a,当我打电话给ToList()时,我不想要资产的孩子,状态和类型,延迟加载。

编辑:经过一些随机的Visual Studio自动完成调查后,可以通过关闭DbContext中的延迟加载来完成大部分工作:

this.Db.Configuration.LazyLoadingEnabled = false;

不幸的是,如果您使用查询结果的工作确实有一些子表,即使关闭了LazyLoadingEnabled,对于它们的某些子集,事情仍然可能“起作用” iff 数据因为这些孩子早已在DbContext中加载 - 也就是说,如果这些孩子已经缓存了他们的上下文 - 这可能会产生一些令人惊讶且暂时混淆的结果。

也就是说,我想在查询时显式加载一些子项,并完全切断与其他子实体的任何关系。

最佳方式是主动加载某些实体并忽略其余实体。也就是说,我可以调用ToList(),而不必担心丢弃大量的数据库连接。

上下文

我有一个案例,我正在使用名为Asset的实体的LINQ to Entities查询结果为视图模型提供保湿。 Asset表有几个子表,Type和Status。 “类型”和“状态”都有“描述”字段,我的视图模型中包含两个描述。让我们假装这个查询变得复杂。

所以我想在一个数据库查询中从连接到Type和Status的Asset表中提取所有内容,在此期间我会提取Type和Status描述。换句话说,我不想懒得加载那些信息。

WET(Woeful Entity reTranscription?)

我们现在正在做的事情,从连接的角度来看,这正是我想要的,通常.Select进入视图模型,与繁琐的字段匹配。

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();

这很好,因为从不访问Asset的Status和Type子实体,而没有延迟加载。 SQL是针对所有资产的一个数据库命中的一个连接。完美。

担忧是// *** LOTS of fields from Asset removed ***中所有重复的jive。目前,我们在每个freakin查询中都有这个投影,显然不是DRY。这意味着当资产表发生变化时,新字段很少包含在每个投影中(因为人类),这很糟糕。

我没有看到查询的快速方法,顺便说一句。如果我想在一个查询中执行它,我必须有连接。我可以用不同的方法添加它,但我不确定我每次都会跳过投影。或者我可以在级联方法中为查询添加连接,但是我的投影仍然是“存储库绑定”,如果我在其他地方使用这些类型的查询,这不是最好的情况。但我打赌我在这里蠢蠢。

当我尝试从资产中添加一个强制转换为我的视图模型并更改为类似的东西时,从代码的角度来看这很漂亮,虽然我被延迟加载所困扰 - 两个额外的数据库命中每个资产,一个用于状态,一个用于类型。

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

正如我们所期望的那样,因为我正在使用像...这样的行。

AssetTypeDesc = a.AssetType.AssetTypeDesc,

...在投射代码内部。所以那是愚蠢的。简洁,可重复使用,但愚蠢。这就是为什么我们讨厌在不检查SQL的情况下使用ORM的人。 ; ^)

过于聪明,排序

但后来我试着变得过于聪明,为视图模型提供了一个新的构造函数,它接管了资产实体&两个描述值为string s,最终导致相同的延迟加载问题(因为,在选择匿名对象之前,第一个ToList()意味着我们不知道资产将如何进行被使用,我们被迫退回一切安全(我假设))。

//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();
  

如果只有某种方式可以说,“将这些匿名对象强制转换为List,但在你做这件事的时候不要懒得加载资产的孩子。” <<<那是我的问题,自然。

我读过一些关于DataLoadOptions.LoadWith()which probably provides an okay solution的内容,我最终可能会这样做,但这并不是我所要求的。我认为这是一个全局式的设置(我认为只是数据上下文的生命周期,应该是单控制器交互),我可能不一定要设置它。我可能也想要ObjectTrackingEnabled = false,但我还没想过。

我也不想使用自动播放器。

1 个答案:

答案 0 :(得分:1)

痛苦地,经过一些随机的Visual Studio自动完成调查后,这可能就像关闭DbContext中的延迟加载一样简单:

this.Db.Configuration.LazyLoadingEnabled = false;

古怪的是,如果您使用查询结果的工作确实有一些子表,即使关闭了LazyLoadingEnabled,事情仍然可以“工作”#34;对于它们的某些子集 iff 这些子节点的数据已经在DbContext的早期加载了 - 也就是说,如果这些子节点已经缓存了它们的上下文 - 这可以用于一些令人惊讶且暂时令人困惑的结果。

  

最好是能够樱桃挑选孩子是什么"延迟加载符合条件的"。

我可能需要更新问题,以便涵盖原始问题的这种变体。