2014-12-29 164 views
2

不確定這是標題中的正確術語,但對於下面描述的特定行爲有疑問。「包含」實體框架中的鏈接

考慮:

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

當你調用'Include'兩次?例如:'this.Include(x => x.Bars).Include(x => x.Foos);'。 – haim770

+1

看起來它應該對我有用。看看你是否可以將SQL發送到數據庫(通過分析器,日誌輸出等) – JasonCoder

+0

@haim770,它可以像你期望的那樣工作,兩者都將包括在內 - 這是一個希望理解原因的簡單示例,但是(包括兩個,包括相同的功能)是我最終選擇的解決方案。 – Kritner

回答

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 

如果我再次遇到了無法找到的實際場景,我會更新/詢問一個新問題。但在此期間,感謝您的理智檢查。

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%-ly的行爲,你想:這都將返回FooBars因預裝Foos和酒吧。簡單的鏈接是絕對OK,就像你也可以這樣做:

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

如果「DoSomeMyOtherThings」做任何將導致查詢來改變它的形狀和失去其緊密結合到特定的表,如投影,那麼IncludeBar會失敗,即使它似乎好。所有包括(定義新的來源將被拉),應該在任何其他操作之前。

return this 
     .GetIncludeFoo()  // let's say they all are extensions just for clarity 
     .GetIncludeBar() 
     .DoSomeMyOtherThings() 

它可能看起來很明顯,但是當你開始包裝包括/哪來/選擇/ groupbys變成了很好的方法,當你再啓動混合方法一起使用,它真的很容易關於忘記:

return this 
     .GetFooThatAre("Green") 
     .GetBestBarForEachFoo() 
     .GetBuzzFromBar() 

第一種方法將能夠包括「Foos」。第二種方法可能會成功包含「酒吧」。然而,由於groupby和/或投影隱藏在第二種方法中,最後一個可能無法包含「Buzz」。

即使使用相當包裝時,查詢仍然是:

return this 
     .GetIncludeFoo() 
     .GetIncludeBar() 
     .GetIncludeBuzz() 
     .GetFooThatAre("Green") 
     .GetBestBarForEachFoo() 
     .GetBuzzFromBar() 

或成才這樣。