【问题标题】:How to resolve multiple join with LinQ?如何解决与 LinQ 的多个联接?
【发布时间】:2020-03-06 16:59:24
【问题描述】:

所以,假设我有一个Contest,它可以有多个ContestCulture。 对于本次比赛,我必须拥有 Documents ,其中有 DocumentTypeCulture(规则、条款和条件、隐私政策)

对于每个ContestCulture,我必须为每个DocumentType 至少有一个Document,以便Contest 有效。

假设我有一个 Contest 和 2 个 ContestCulture (fr-CA, en-CA) 我必须至少有 6 个 Document 用于那个 Contest

  • 文档 1:文化 1、规则
  • 文档 2:文化 1、条款和条件
  • 文档 3:文化 1、隐私政策
  • 文档 4:文化 2、规则
  • 文档 5:文化 2、条款和条件
  • 文档 6:文化 2、隐私政策

实体如下:

public class Document
{
    public DocumentType DocumentType { get; set; }
    public int CultureId { get; set; }
    public Culture Culture { get; set; }
    public int ContestId { get; set; }
    public Contest Contest { get; set; }
    //Some other properties
    //...
}

public class Contest
{
    public List<Document> Documents { get; set; }
    public List<ContestCulture> ContestCultures { get; set; }
    //Some other properties
    //...

}

public class ContestCulture 
{
     public int ContestId { get; set; }
    public Contest Contest { get; set; }

    public int CultureId { get; set; }
    public Culture Culture { get; set; }

    //Some other properties
    //...
}

public class Culture
{
    public string Code { get; set; }
    public string Name { get; set; }
}

现在已经设置了上下文..

我有 3 个列表:

  • DocumentType 列表(enum 值列表)
  • ContestCulture ID 列表(int 列表)
  • DocumentsContest 列表(Document 列表)

如何检查每个DocumentType 是否至少有1 个Document ContestCulture

我对 LinQ 真的很陌生,所以我尝试了这个:

from cultureId in list_usedCultureIdsForContest
join doc in documents on cultureId equals doc.CultureId into temp1
from t in temp1.DefaultIfEmpty()
select new {cultureId, t};

但它只给我我拥有的那些,如下所示:

【问题讨论】:

  • 听起来让我很困惑。所以3个列表之间的关系是:1个Contest可以有很多ContestCultures,1个Contest可以有很多Documents。这是正确的吗?并且 1 竞赛仅在所有文档类型存在时才有效。这和文化有什么关系?
  • @Alex-TinLe 你好!你说得对 Contest、ContestCulture 和 Document 之间的关系。当每个 ContestCulture 都存在所有 DocumentType 时,该竞赛是有效的。在我的示例中,CultureId 代表 ContestCulture.Id
  • 我将编辑 OP,因为它有点令人困惑。我可能忘记了一些事情。
  • @Alex-TinLe 如您所见,该文档与 ContestCulture 无关,而是与一种文化相关(没有设计它并且现在进行架构更改的方式很晚:/)。所以我必须检查ContestCulture中使用了哪个Culture作为Contest,然后以某种方式加入所有这些以检查每个ContestCulture的每个DocumentType是否至少有一个Document...

标签: c# linq join


【解决方案1】:

首先,从enum 获取每种文化所需的文档类型数量:

var numDocumentTypes = Enum.GetNames(typeof(DocumentType)).Length;

然后,计算每个CultureId 拥有的不同文档类型的数量:

var docTypesPerCulture = documents.GroupBy(d => d.CultureId)
                                  .Select(dg => new {
                                        CultureId = dg.Key,
                                        Count = dg.Select(d => d.DocumentType).Distinct().Count()
                                   });

现在,您可以左连接到使用的文化 ID(以处理没有文档的文化 ID)并过滤到缺少文档类型的文化 ID:

var ans = list_usedCultureIdsForContest.GroupJoin(docTypesPerCulture,
                                                  ci => ci,
                                                  dpc => dpc.CultureId,
                                                  (ci, dpcj) => dpcj.Any() ? dpcj.Select(dpc => new { CultureId = ci, Count = dpc.Count }).Single()
                                                                           : new { CultureId = ci, Count = 0 }
                                        )
                                       .Where(cic => cic.Count != numDocumentTypes);

【讨论】:

    【解决方案2】:

    简单:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ConsoleApplication159
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                List<Document> contest = new List<Document>() {
                    new Document() { contestCulture = 1,  documentType = DocumentType.Rules },
                    new Document() { contestCulture = 1,  documentType = DocumentType.TermsAndConditions },
                    new Document() { contestCulture = 1,  documentType = DocumentType.PrivacyPolicy },
                    new Document() { contestCulture = 2,  documentType = DocumentType.Rules },
                    new Document() { contestCulture = 2,  documentType = DocumentType.TermsAndConditions },
                    new Document() { contestCulture = 2,  documentType = DocumentType.PrivacyPolicy  }
                };
    
                var valid = contest.GroupBy(x => x.contestCulture)
                    .Select(x => new { culture = x.Key, valid = Valid(x.ToList()) })
                    .ToList();
    
    
            }
    
            static bool Valid(List<Document> documents)
            {
                return documents.Any(x => x.documentType == DocumentType.Rules) &&
                    documents.Any(x => x.documentType == DocumentType.TermsAndConditions) &&
                    documents.Any(x => x.documentType == DocumentType.PrivacyPolicy);
            }
        }
        public enum DocumentType
        {
            Rules,
            TermsAndConditions,
            PrivacyPolicy
        }
        public class Document
        {
            public int contestCulture { get; set; }
            public DocumentType documentType { get; set; }
        }
    
    }
    

    另一种方式

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    namespace ConsoleApplication159
    {
        class Program
        {
    
            static void Main(string[] args)
            {
                List<Document> contest = new List<Document>() {
                    new Document() { contestCulture = 1,  documentType = DocumentType.Rules },
                    new Document() { contestCulture = 1,  documentType = DocumentType.TermsAndConditions },
                    new Document() { contestCulture = 1,  documentType = DocumentType.PrivacyPolicy },
                    new Document() { contestCulture = 2,  documentType = DocumentType.Rules },
                    new Document() { contestCulture = 2,  documentType = DocumentType.TermsAndConditions },
                    new Document() { contestCulture = 2,  documentType = DocumentType.PrivacyPolicy  }
                };
    
                var valid = contest.GroupBy(x => x.contestCulture)
                    .Select(x => new { culture = x.Key, valid = Valid(x.ToList()) })
                    .ToList();
    
            }
    
            static bool Valid(List<Document> documents)
            {
                int or = 0;
                foreach (Document document in documents)
                {
                    or |= (int)document.documentType;
                }
                return (or == 7) ? true : false;
            }
        }
        public enum DocumentType
        {
            Rules = 1,
            TermsAndConditions = 2,
            PrivacyPolicy = 4
        }
        public class Document
        {
            public int contestCulture { get; set; }
            public DocumentType documentType { get; set; }
        }
    
    }
    

    【讨论】:

    • 非常感谢您的宝贵时间。但是当我更新 Op 时,您会看到 Document 与 ContestCulture 无关,而是与一种文化相关。这意味着如果每个 DocumentType 和每个 ContestCulture 都存在 Document,我必须返回 The ContestCulture 并检查文档。但为此,我必须以某种方式加入 ContestCulture 的文化……有点搞砸了……
    • 文化不需要检查文档是否有效(3种类型)。如果您需要名称,您可以进行第二次查询以将名称与文化 ID 结合起来。
    猜你喜欢
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-30
    • 2020-10-02
    • 2018-11-03
    • 2018-03-30
    相关资源
    最近更新 更多