不确定这是否是标题中的正确用语,但对下面描述的某种行为有疑问。
假设:
public class FooBar
{
// etc
public virtual ICollection<Foo> Foos { get; set; }
public virtual ICollection<Bar> Bars { get; set; }
}
public class Foo
{
}
public class Bar
{
}
public class FooBarRepo
{
private readonly EntitiesContext _context;
public FooBarRepo()
{
this._context = new EntitiesContext();
}
public IQueryable<FooBar> GetIncludeFoo()
{
return this
._context
.FooBars
.Include(i => i.Foos);
}
public IQueryable<FooBar> GetIncludeBar()
{
return this
._context
.FooBars
.Include(i => i.Bars);
}
}
我没有测试床来确认这种行为,所以我想确保我正确地解释/记住 - 但是如果我要按照这样的定义抛出一个额外的功能:
public IQueryable<FooBar> GetIncludeBarChainedWithGetIncludeFoo()
{
return this
.GetIncludeFoo()
.Include(i => i.Bars);
}
我似乎记得在致电GetIncludeBarChainedWithGetIncludeFoo()
时,我只收到我的Bars
,而不是我希望调用Foos
时的额外GetIncludeFoo()
通话的一部分。
可以确认/解释这种行为吗?
答案 0 :(得分:0)
免责声明:自从我使用EF以来,EF可能已发生变化。我是在EF 4.x的水平上写作的。以后的版本可能添加了一些更好的支持传播包括向上查询,我实际上从来没有检查过。但我有点怀疑。所以,请不要把所有这些都用于绝对的事实,请自己尝试。例如,我不记得GroupBy是否真的打破了Inclusions,但我确定Select会导致对匿名类型的投射 - 确实如此。
不,在你的例子中,它会起作用。
在您的示例中,当调用GetIncludeBarChainedWithGetIncludeFoo
时,您的代码实际调用/构建以下序列/查询:
// from GetIncludeFoo
return this
._context
.FooBars
.Include(i => i.Foos)
// from GetIncludeBarChainedWithGetIncludeFoo
.Include(i => i.Bars);
这将100% - 按照您的想法行事:它将返回FooBars,同时预装了Foos和Bars。简单的链接绝对可以,就像你也可以做的那样:
// from GetIncludeFoo
return this
._context
.FooBars
.Include("Foos")
.Include("Bars");
然而,当你将这两个包括你的例子中的几个方法分开时,就会出现一种微妙的陷阱。我们来看看:
return this
.GetIncludeFoo() // let's say they all are extensions just for clarity
.DoSomeMyOtherThings()
.GetIncludeBar()
如果&#34; DoSomeMyOtherThings&#34;使任何会导致查询改变其形状并松散其与特定表格的紧密绑定(如投影),然后即使看起来没问题,IncludeBar也会失败。所有包含(定义要拉动的新源)都应该在任何其他操作之前。
return this
.GetIncludeFoo() // let's say they all are extensions just for clarity
.GetIncludeBar()
.DoSomeMyOtherThings()
这看起来很明显,但是当你开始将包含/ wheres / choices / groupbys包装成漂亮的方法时,然后当你开始混合方法时,它很容易忘记:
return this
.GetFooThatAre("Green")
.GetBestBarForEachFoo()
.GetBuzzFromBar()
第一种方法将能够包括&#34; Foos&#34;。第二种方法可能会在Includeing&#34; Bars&#34;中成功。但是最后一个可能无法包含&#34; Buzz&#34;因为在第二种方法中隐藏了groupby和/或投影。
即使使用漂亮的包装器,查询仍然必须是:
return this
.GetIncludeFoo()
.GetIncludeBar()
.GetIncludeBuzz()
.GetFooThatAre("Green")
.GetBestBarForEachFoo()
.GetBuzzFromBar()
或者像那样。
答案 1 :(得分:0)
我必须有一个稍微不同的场景,在那里的某个地方有一个ToList()或类似的东西,因为我无法重现我遇到问题的场景:
public class FooBarRepo : IFooBarRepo
{
private readonly Entities _context;
public FooBarRepo()
{
this._context = new Entities();
}
public IQueryable<FooBar> Get()
{
return this._context.FooBar;
}
public IQueryable<FooBar> GetIncludeFoo()
{
return this.Get().Include(i => i.Foo);
}
public IQueryable<FooBar> GetIncludeBar()
{
return this.Get().Include(i => i.Bar);
}
public IQueryable<FooBar> GetIncludeFooAndBar()
{
return this
.Get()
.Include(i => i.Foo)
.Include(i => i.Bar);
}
public IQueryable<FooBar> GetIncludeFooAndChainBar()
{
return this.GetIncludeBar().Include(i => i.Foo);
}
public void Dispose()
{
this._context.Dispose();
}
}
class Program
{
static void Main(string[] args)
{
IEnumerable<FooBar> get;
IEnumerable<FooBar> getIncludeFoo;
IEnumerable<FooBar> getIncludeBar;
IEnumerable<FooBar> getIncludeFooAndBar;
IEnumerable<FooBar> getIncludeFooAndChainBar;
using (var context = new EntityFrameworkTesting.TestIncludeChaining.Repository.FooBarRepo())
{
get = context.Get().ToList();
getIncludeFoo = context.GetIncludeFoo().ToList();
getIncludeBar = context.GetIncludeBar().ToList();
getIncludeFooAndBar = context.GetIncludeFooAndBar().ToList();
getIncludeFooAndChainBar = context.GetIncludeFooAndChainBar().ToList();
}
}
}
生成SQL:
-- get
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name]
FROM [dbo].[_FooBar] AS [Extent1]
-- getIncludeFoo
SELECT
[Project1].[Id] AS [Id],
[Project1].[Name] AS [Name],
[Project1].[C1] AS [C1],
[Project1].[Id1] AS [Id1],
[Project1].[FooBarId] AS [FooBarId]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[FooBarId] AS [FooBarId],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[_FooBar] AS [Extent1]
LEFT OUTER JOIN [dbo].[_Foo] AS [Extent2] ON [Extent1].[Id] = [Extent2].[FooBarId]
) AS [Project1]
ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
-- getIncludeBar
SELECT
[Project1].[Id] AS [Id],
[Project1].[Name] AS [Name],
[Project1].[C1] AS [C1],
[Project1].[Id1] AS [Id1],
[Project1].[FooBarId] AS [FooBarId]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id1],
[Extent2].[FooBarId] AS [FooBarId],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
FROM [dbo].[_FooBar] AS [Extent1]
LEFT OUTER JOIN [dbo].[_Bar] AS [Extent2] ON [Extent1].[Id] = [Extent2].[FooBarId]
) AS [Project1]
ORDER BY [Project1].[Id] ASC, [Project1].[C1] ASC
-- getIncludeFooAndBar
SELECT
[UnionAll1].[Id] AS [C1],
[UnionAll1].[Id1] AS [C2],
[UnionAll1].[Name] AS [C3],
[UnionAll1].[C1] AS [C4],
[UnionAll1].[Id2] AS [C5],
[UnionAll1].[FooBarId] AS [C6],
[UnionAll1].[C2] AS [C7],
[UnionAll1].[C3] AS [C8]
FROM (SELECT
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Id] AS [Id1],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id2],
[Extent2].[FooBarId] AS [FooBarId],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3]
FROM [dbo].[_FooBar] AS [Extent1]
LEFT OUTER JOIN [dbo].[_Foo] AS [Extent2] ON [Extent1].[Id] = [Extent2].[FooBarId]
UNION ALL
SELECT
2 AS [C1],
[Extent3].[Id] AS [Id],
[Extent3].[Id] AS [Id1],
[Extent3].[Name] AS [Name],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
[Extent4].[Id] AS [Id2],
[Extent4].[FooBarId] AS [FooBarId]
FROM [dbo].[_FooBar] AS [Extent3]
INNER JOIN [dbo].[_Bar] AS [Extent4] ON [Extent3].[Id] = [Extent4].[FooBarId]) AS [UnionAll1]
ORDER BY [UnionAll1].[Id1] ASC, [UnionAll1].[C1] ASC
-- getIncludeFooAndChainBar
SELECT
[UnionAll1].[Id] AS [C1],
[UnionAll1].[Id1] AS [C2],
[UnionAll1].[Name] AS [C3],
[UnionAll1].[C1] AS [C4],
[UnionAll1].[Id2] AS [C5],
[UnionAll1].[FooBarId] AS [C6],
[UnionAll1].[C2] AS [C7],
[UnionAll1].[C3] AS [C8]
FROM (SELECT
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Id] AS [Id1],
[Extent1].[Name] AS [Name],
[Extent2].[Id] AS [Id2],
[Extent2].[FooBarId] AS [FooBarId],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3]
FROM [dbo].[_FooBar] AS [Extent1]
LEFT OUTER JOIN [dbo].[_Bar] AS [Extent2] ON [Extent1].[Id] = [Extent2].[FooBarId]
UNION ALL
SELECT
2 AS [C1],
[Extent3].[Id] AS [Id],
[Extent3].[Id] AS [Id1],
[Extent3].[Name] AS [Name],
CAST(NULL AS int) AS [C2],
CAST(NULL AS int) AS [C3],
[Extent4].[Id] AS [Id2],
[Extent4].[FooBarId] AS [FooBarId]
FROM [dbo].[_FooBar] AS [Extent3]
INNER JOIN [dbo].[_Foo] AS [Extent4] ON [Extent3].[Id] = [Extent4].[FooBarId]) AS [UnionAll1]
ORDER BY [UnionAll1].[Id1] ASC, [UnionAll1].[C1] ASC
如果我再次遇到我无法找到的实际情况,我会更新/提出一个新问题。但在此期间,感谢理智检查。