2017-06-02 56 views
0

我試圖從間接相關的實體查詢到單一用途視圖模型的東西。這裏是我的實體的攝製:防止使用連接的實體框架查詢中的SELECT N + 1問題

public class Team { 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public List<Member> Members { get; set; } 
} 

public class Member { 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class Pet { 
    [Key] 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Member Member { get; set; } 
} 

每個類都在我的數據庫上下文DbSet<T>

這是我想從一個查詢,以構建視圖模型:

public class PetViewModel { 
    public string Name { get; set; } 
    public string TeamItIndirectlyBelongsTo { get; set; } 
} 

我與此查詢要這樣做:

public PetViewModel[] QueryPetViewModel_1(string pattern) { 
    using (var context = new MyDbContext(connectionString)) { 
     return context.Pets 
      .Where(p => p.Name.Contains(pattern)) 
      .ToArray() 
      .Select(p => new PetViewModel { 
       Name = p.Name, 
       TeamItIndirectlyBelongsTo = "TODO", 
      }) 
      .ToArray(); 
    } 
} 

但顯然還是有「TODO」在那裏。

疑難雜症:我不能在這一刻改變實體,所以我不能只是包括List<Pet>財產或Member一個Team屬性來助陣。我想在此時修復查詢內容。

這是我目前的解決方案:

public PetViewModel[] QueryPetViewModel_2(string pattern) { 
    using (var context = new MyDbContext(connectionString)) { 
     var petInfos = context.Pets 
      .Where(p => p.Name.Contains(pattern)) 
      .Join(context.Members, 
       p => p.Member.Id, 
       m => m.Id, 
       (p, m) => new { Pet = p, Member = m } 
      ) 
      .ToArray(); 

     var result = new List<PetViewModel>(); 

     foreach (var info in petInfos) { 
      var team = context.Teams 
       .SingleOrDefault(t => t.Members.Any(m => m.Id == info.Member.Id)); 

      result.Add(new PetViewModel { 
       Name = info.Pet.Name, 
       TeamItIndirectlyBelongsTo = team?.Name, 
      }); 
     } 

     return result.ToArray(); 
    } 
} 

但是,這有一個 「SELECT N + 1」 的問題在那裏。

有沒有辦法創建一個一個 EF查詢得到所需的結果,而不改變實體?

PS。如果您更喜歡包含上述內容的「即插即用」再現器,請參閱this gist

+1

只是出於好奇 - 爲什麼你不能改變這些(很奇怪)實體?我的意思是,如果你修復它們,它不需要修改數據庫模型。 – Evk

+0

@Evk好問題。一個原因是(現在)我真的想了解更多關於查詢的工作方式以及他們可以做什麼和不可以做什麼。原始理由不能從最小限度的repro中清楚表明,但歸結爲我想阻止其他人查詢此路線,除非他們真的知道他們在做什麼(並且不包括記錄中的財產是一種快速實現這一目標的方式)。 – Jeroen

+0

我意識到這也意味着我可能需要接受一個答案「*不可能,改變你的實體無論如何你傻傻的*。」,如果事實證明是這樣的話。 – Jeroen

回答

3

由於沒有提供必要的導航屬性,因爲@Evk在註釋中提到的不影響數據庫結構,但是允許EF在寫入類似pet.Member.Team.Name(你在這裏需要什麼)。

與模型的另一個問題是,你沒有既不能從TeamPet也沒有從PetTeam自「加盟」實體Member沒有導航性能的導航路徑。

仍然有可能獲得通過使用現有的導航性能和不尋常的join運營商像這樣的一些不那麼直觀的方式與單個查詢所需要的信息:

var result = (
    from team in context.Teams 
    from member in team.Members 
    join pet in context.Pets on member.Id equals pet.Member.Id 
    where pet.Name.Contains(pattern) 
    select new PetViewModel 
    { 
     Name = pet.Name, 
     TeamItIndirectlyBelongsTo = team.Name 
    }).ToArray(); 
+0

感謝您的意見和建議的解決方案。我將嘗試將其轉換爲基於方法的linq語法,並檢查它是否有效。 – Jeroen

+0

像''Join'和'SelectMany'一樣,方法的語法將會非常難看。我想一些公司的政策要求。 –

+0

我想知道在哪個sql中它將被翻譯,與此「正常」方式(如果存在導航屬性)相比。 – Evk