LINQ - 搜索一对多以查找数组

时间:2017-08-13 06:07:10

标签: c# .net entity-framework linq .net-core

让我先说这是自学,我正在尝试自学LINQ和实体框架。我花了几天时间试图将这个问题底部的SQL语句转换为LINQ,结果很糟糕。我还在底部包含了SQL图表。

我的目标是选择与传入的字符串数组具有相同字符的所有故事。我不想要返回有额外字符或缺少字符的故事。到目前为止,这是我虚弱的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 profiler图片),并将一堆对象加载到内存中。这种情况有没有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]

2 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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; }
    }

}