2017-08-13 96 views
0

讓我先說這是自學,我試圖自學LINQ和實體框架。我花了幾天的時間試圖將這個問題底部的SQL語句轉換爲LINQ,結果很糟糕。我還在底部包含了SQL圖。LINQ - 搜索一對多數組的完全匹配

我的目標是選擇所有與傳遞的字符串數組具有相同字符的故事。我不想要故事返回有額外的字符或缺少字符。這是我的軟弱LINQ技能也想出迄今:

var characters = new string[] { "Harry", "Tom" }; 
var cq = _context.TblCharacter.AsNoTracking().Where(c => characters.Contains(c.NameVc)); 
var q = from c in cq 
     join sc in _context.TblStoryCharacter.AsNoTracking() 
     on c.IdI equals sc.CharacterIdI 
     join s in _context.TblStory.AsNoTracking().Include(s => s.TblStoryCharacter).ThenInclude(sc => sc.CharacterIdINavigation) 
     on sc.StoryIdI equals s.IdI 
     where s.TblStoryCharacter.Count() == characters.Length 
     where s.TblStoryCharacter.Where(sc => characters.Contains(sc.CharacterIdINavigation.NameVc)).Count() == characters.Length 
     select s; 

上面的代碼產卵一堆查詢(以下SQL事件探查器圖像),並加載大量的對象到內存中。這種情況下是否有任何LINQ魔術?

enter image description here

LINQ產生了疑問:

SELECT [t0].[StoryId_i] 
FROM [tbl_story_character] AS [t0] 

SELECT [sc1].[StoryId_i] 
FROM [tbl_story_character] AS [sc1] 
INNER JOIN [tbl_character] AS [sc.CharacterIdINavigation0] ON [sc1].[CharacterId_i] = [sc.CharacterIdINavigation0].[Id_i] 
WHERE [sc.CharacterIdINavigation0].[Name_vc] IN ('Harry', 'Tom') 

這是我一開始就試圖轉換爲LINQ的SQL:

select * 
from tbl_story 
where Id_i in (
    select sc.StoryId_i 
    from tbl_story_character sc 
    inner join tbl_character c 
     on c.Id_i = sc.CharacterId_i 
    where c.Name_vc in ('Harry', 'Tom') 
    and  not exists (
     select * 
     from tbl_story_character subsc 
     inner join tbl_character subc 
      on subc.Id_i = subsc.CharacterId_i 
     where subc.Name_vc not in ('Harry', 'Tom') 
     and  subsc.StoryId_i = sc.StoryId_i 
    ) 
    group by sc.StoryId_i 
    having count(*) = 2 
) 

數據庫圖表: Story DB Diagram

編輯: 這些模型由EFCore基於現有數據庫生成,每個模型都包含基於圖中外鍵的導航屬性。

在接受Jon Skeet和Munzer的建議之後的新LINQ。

from s in _context.TblStory.AsNoTracking() 
      .Include(s => s.AuthorIdINavigation) 
      .Include(s => s.TblStoryCharacter) 
        .ThenInclude(sc => sc.CharacterIdINavigation) 
where s.TblStoryCharacter.All(sc => characters.Contains(sc.CharacterIdINavigation.NameVc)) 
where s.TblStoryCharacter.Count == 2 
select s; 

這會導致以下SQL似乎正確。

SELECT [s].[Id_i], [s].[AuthorId_i], [s].[Published_dt] 
FROM [tbl_story] AS [s] 
INNER JOIN [tbl_author] AS [t2] ON [s].[AuthorId_i] = [t2].[Id_i] 
WHERE NOT EXISTS (
    SELECT 1 
    FROM [tbl_story_character] AS [sc] 
    INNER JOIN [tbl_character] AS [sc.CharacterIdINavigation] ON [sc].[CharacterId_i] = [sc.CharacterIdINavigation].[Id_i] 
    WHERE ([s].[Id_i] = [sc].[StoryId_i]) AND [sc.CharacterIdINavigation].[Name_vc] NOT IN ('Harry', 'Tom')) AND ((
    SELECT COUNT(*) 
    FROM [tbl_story_character] AS [t] 
    WHERE [s].[Id_i] = [t].[StoryId_i] 
) = 2) 
ORDER BY [s].[Id_i] 
+1

我想先開始試圖將查詢簡化爲仍然顯示問題的較短示例。你可以用* one * join和一個過濾器來重現問題嗎?基本上,找出它開始炸燬的地方。 –

+0

看看「LINQ派生查詢」,我會說你正在使用EF核心,這當然不是一個很好的(複雜)查詢工具。但是你的LINQ查詢看起來很奇怪,所有這些混合的手動連接,導航屬性,跟蹤一個急切的加載相關結構。您已經顯示了數據庫圖表,但對於EF查詢,擁有相關表格的實體模型(類)更重要,您能否發佈它? –

+0

@IvanStoev我是否應該離開核心直到它更成熟一些?我沒有任何限制,需要我利用核心。 – M3SSYM4RV1N

回答

2

我相信All是你在找什麼在這裏

它應該是這樣的

var q = from c in cq 
     join sc in _context.TblStoryCharacter.AsNoTracking() 
     on c.IdI equals sc.CharacterIdI 
     join s in _context.TblStory.AsNoTracking().Include(s => s.TblStoryCharacter).ThenInclude(sc => sc.CharacterIdINavigation) 
     on sc.StoryIdI equals s.IdI 
     where s.TblStoryCharacter.All(sc => characters.Contains(sc.CharacterIdINavigation.NameVc)) 
     select s; 
+0

我使用了LINQ查詢,但還需要添加它才能使用正確的SQL:where s.TblStoryCharacter.Count == 2。 – M3SSYM4RV1N

0

嘗試這樣:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 


namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      string[] names = {"Harry", "Tom"}; 

      var results = (from sc in Story_Character.story_character 
          join chr in Character.character on sc.Id_i equals chr.Id_i 
          join st in Story.story on sc.Id_i equals st.Id_i 
          join auth in Author.author on sc.Id_i equals auth.Id_i 
          where names.Contains(chr.Name_vc) 
          select new { sc = sc, chr = chr, st = st, auth = auth }) 
          .GroupBy(x => x.sc.StoryId_i).Where(x => x.Count() >= 2).ToList(); 

     } 
    } 
    public class Story_Character 
    { 
     public static List<Story_Character> story_character = new List<Story_Character>(); 
     public int Id_i { get; set; } 
     public int StoryId_i { get; set; } 
     public int CharacterId_i { get; set; } 
    } 

    public class Character 
    { 
     public static List<Character> character = new List<Character>(); 
     public int Id_i { get; set; } 
     public string Name_vc { get; set; } 
    } 
    public class Story 
    { 
     public static List<Story> story = new List<Story>(); 
     public int Id_i { get; set; } 
     public DateTime Published_dt { get; set; } 
     public string Title_vc { get; set; } 
     public string AuthorId_i { get; set; } 
    } 
    public class Author 
    { 
     public static List<Author> author = new List<Author>(); 
     public int Id_i { get; set; } 
     public string Name_vc { get; set; } 
     public string Url_vc { get; set; } 
    } 

} 
+0

這將正確地識別具有哈利和湯姆作爲人物的故事,但我認爲其中還會包含具有哈利,湯姆和其他隨機人物的故事。正確? – M3SSYM4RV1N

+0

由於Id_i是主鍵,每個Id_i只有一個字符名稱。所以不可能有任何其他的字符名稱。現在有可能兩個id_i具有相同的字符名稱。我不認爲有必要測試x.Count()> = 2。 – jdweng