2016-01-21 40 views
6

我在查詢中包含導航屬性Include,以便稍後不會延遲加載。但是,當我使用Select投影創建匿名包裝對象時,它不起作用。EF:使用「選擇」投影創建包裝對象時,「包含」導航屬性

讓我看看簡單的例子。 實體

public class UserEntity { 
    public string Name {get;set;} 
    public virtual ICollection<UserEntity> Friends { get; set; } 
} 

查詢

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    // Select here is simplified, but it shows the wrapping 
    .Select(user => new { 
     User = user 
    }) 
    .First(); 

// Here we have additional lazy loaded DB call 
var friends = entry.User.Friends.Select(x => x.Name).ToList(); 

而且我也看到生成的SQL,不包括該導航屬性:

SELECT 
    [Limit1].[Name] AS [Name], 
    FROM (SELECT TOP (1) 
     [Extent1].[Name] AS [Name] 
     FROM [dbo].[Users] AS [Extent1] 
    ) AS [Limit1] 

是否有可能到Include導航屬性Friends in這種情況下,以便User將獲得沒有延遲加載的數據?

,我也期待這個工作:

var entry = _dbCtx 
    .Users 
    .Select(user => new { 
     User = user 
    }) 
    .Include(x => x.User.Friends) 
    .First(); 

但是,讓一個例外:

InvalidOperationException異常:在查詢的結果類型既不是的EntityType,也不與實體元素類型CollectionType 。包含路徑只能爲具有這些結果類型之一的查詢指定。

有一些解決方法我來,但他們莫名其妙地棘手:

  1. 除了屬性添加到我們的匿名對象Select

    var entry = _dbCtx 
        .Users 
        .Select(user => new { 
         User = user, 
         UsersFriends = user.Friends 
        }) 
        .First(); 
    
    // manually copy the navigation property 
    entry.User.Friends = user.UsersFriends; 
    
    // Now we don't have any addition queries 
    var friends = entry.User.Friends.Select(x => x.Name).ToList(); 
    
  2. 地圖還用戶一個匿名對象,然後將屬性映射到C#中的UserEntity

    var entry = _dbCtx 
        .Users 
        .Select(user => new { 
         User = new { 
          Name = user.Name, 
          Friends = user.Friends 
         } 
        }) 
        .Take(1) 
        // Fetch the DB 
        .ToList() 
        .Select(x => new { 
         User = new UserEntity { 
          Name = x.Name, 
          Friends = x.Friends 
         } 
        }) 
        .First(); 
    
    // Now we don't have any addition queries 
    var friends = entry.User.Friends.Select(x => x.Name).ToList(); 
    

所以,現在,有一個Friends一個LEFT OUTER JOIN,但都解決方法是不太好:

1)附加屬性和副本不是一個乾淨的方式。

2)我的UserEntity有更多的其他屬性。另外,每次我們添加新的屬性,我們都應該修改這裏的選擇器。

是否有某種方式來實現導航屬性,包括從第一個樣本?

感謝您的閱讀,我希望有人對此有所瞭解。

編輯:

我將擴展實體和查詢,以顯示真正的用例。

實體

public class UserEntity { 
    public string Name {get;set;} 
    public int Score {get;set;} 
    public virtual ICollection<UserEntity> Friends { get; set; } 
} 

查詢

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    .Select(user => new { 
     User = user, 
     Position = _dbCtx.Users.Count(y => y.Score > user.Score) 
    }) 
    .First(); 

回答

0

不是一個答案,但_why_更好通緝代碼格式...

實際上,我驚訝它的作品。也許英孚正在檢測出你沒有直接在投影中使用Friends屬性,因此忽略了它。如果你封裝的對象(S)外的EF查詢

var entry = _dbCtx 
    .Users 
    .Include(x => x.Friends) 
    .Take(1); // replicate "First" inside the EF query to reduce traffic 
    .AsEnumerable() // shift to linq-to-objects 
    // Select here is simplified, but it shows the wrapping 
    .Select(user => new { 
     User = user 
    }) 
    .First() 
+0

你的樣品將作爲我們創建的'C#'側(查詢後)的包裝對象,而不是用'sql'。但我在'db'級別需要它。原因:我用'Select'執行額外的子查詢。有趣的是,查看我的第一個解決方法,雖然我們在查詢中使用了'user.Friends',但是我們必須在實現後將Property複製回實體,否則我們仍然會進行延遲加載。 – tenbits

+0

爲什麼你需要投影到數據庫查詢中的匿名類型?據我所知,你最終會得到同樣的結果。或者你在投射你希望投影到SQL的_additional_方法('Where','OrderBy'等)? –

+0

看到我的文章編輯,我爲用戶執行子查詢,並且它應該在數據庫中完成,因爲它遍歷每個用戶。所以由於性能的原因,我不能用'c#'來做到這一點。 – tenbits