C# 嵌套LINQ查询问题

C# 嵌套LINQ查询问题,c#,asp.net-mvc,linq,grouping,C#,Asp.net Mvc,Linq,Grouping,今天我遇到了一个问题,为了得到我正在寻找的结果,我已经被难住了一段时间 我目前有一个类似于以下内容的类: public class InstanceInformation { public string PatientID {get; set;} public string StudyID {get; set;} public string SeriesID {get; set;} public string InstanceID {get; set;}

今天我遇到了一个问题,为了得到我正在寻找的结果,我已经被难住了一段时间

我目前有一个类似于以下内容的类:

public class InstanceInformation
{
     public string PatientID {get; set;}
     public string StudyID {get; set;}
     public string SeriesID {get; set;}
     public string InstanceID {get; set;}
}
PatientID/StudyID/SeriesID/InstanceID
我有一个
列表
,我正试图使用LINQ(或任何其他方法)基于此列表生成文件目录的路径(),类似于以下内容:

public class InstanceInformation
{
     public string PatientID {get; set;}
     public string StudyID {get; set;}
     public string SeriesID {get; set;}
     public string InstanceID {get; set;}
}
PatientID/StudyID/SeriesID/InstanceID
我的问题是数据目前是非结构化的,因为它是以前面提到的形式(列表)出现的,我需要一种方法来使用以下约束对所有数据进行分组:

  • 按序列ID分组实例ID
  • 按StudyID分组的系列ID
  • 由PatientID进行的小组研究
我现在有一些类似的东西:

var groups = from instance in instances
             group instance by instance.PatientID into patientGroups
             from studyGroups in
                 (from instance in patientGroups
                   group instance by instance.StudyID)
                   from seriesGroup in
                       (from instance in studyGroups
                        group instance by instance.SeriesID)
                            from instanceGroup in
                                 (from instance in seriesGroup
                                  group instance by instance.InstanceID)
             group instanceGroup by patientGroups.Key;
它只是将我所有的InstanceID按PatientID进行分组,在进行大规模分组之后,很难对所有数据进行筛选,以确定中间的区域(StudyID/SeriesID)是否丢失。任何其他解决此问题的方法都是非常受欢迎的


这主要是为了对对象进行分组——因为我需要迭代它们(使用foreach)

我认为这将产生您想要的结果:

public class InstanceInformation {
    public string PatientID { get; set; }
    public string StudyID { get; set; }
    public string SeriesID { get; set; }
    public string InstanceID { get; set; }

    public override string ToString() {
        return String.Format("Series = {0} Study = {1} Patient = {2}", SeriesID, StudyID, PatientID);
    }
}

class Program {
    static void Main(string[] args) {
        List<InstanceInformation> infos = new List<InstanceInformation>() {
            new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P1" },
            new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P1" },
            new InstanceInformation(){ SeriesID = "A", StudyID = "A1", PatientID = "P2" },
            new InstanceInformation(){ SeriesID = "A", StudyID = "A2", PatientID = "P1" },
            new InstanceInformation(){ SeriesID = "B", StudyID = "B1", PatientID = "P1"},
            new InstanceInformation(){ SeriesID = "B", StudyID = "B1", PatientID = "P1"},
        };

        IEnumerable<IGrouping<string, InstanceInformation>> bySeries = infos.GroupBy(g => g.SeriesID);
        IEnumerable<IGrouping<string, InstanceInformation>> byStudy = bySeries.SelectMany(g => g.GroupBy(g_inner => g_inner.StudyID));
        IEnumerable<IGrouping<string, InstanceInformation>> byPatient = byStudy.SelectMany(g => g.GroupBy(g_inner => g_inner.PatientID));

        foreach (IGrouping<string, InstanceInformation> group in byPatient) {
            Console.WriteLine(group.Key);
            foreach(InstanceInformation II in group)
                Console.WriteLine("  " + II.ToString());
        }
}
公共类实例信息{
公共字符串PatientID{get;set;}
公共字符串StudyID{get;set;}
公共字符串SeriesID{get;set;}
公共字符串InstanceID{get;set;}
公共重写字符串ToString(){
返回String.Format(“Series={0}Study={1}Patient={2}”,SeriesID,StudyID,PatientID);
}
}
班级计划{
静态void Main(字符串[]参数){
列表信息=新列表(){
新实例信息(){SeriesID=“A”,StudyID=“A1”,PatientID=“P1”},
新实例信息(){SeriesID=“A”,StudyID=“A1”,PatientID=“P1”},
新实例信息(){SeriesID=“A”,StudyID=“A1”,PatientID=“P2”},
新实例信息(){SeriesID=“A”,StudyID=“A2”,PatientID=“P1”},
新实例信息(){SeriesID=“B”,StudyID=“B1”,PatientID=“P1”},
新实例信息(){SeriesID=“B”,StudyID=“B1”,PatientID=“P1”},
};
IEnumerable bySeries=infos.GroupBy(g=>g.SeriesID);
IEnumerable byStudy=bySeries.SelectMany(g=>g.GroupBy(g_inner=>g_inner.StudyID));
IEnumerable by patient=by study.SelectMany(g=>g.GroupBy(g_inner=>g_inner.PatientID));
foreach(按患者分组){
控制台写入线(组键);
foreach(组中的InstanceInformation II)
Console.WriteLine(“+II.ToString());
}
}

在类中重写tostring方法;如下所示

    public class InstanceInformation
    {
        public string PatientID { get; set; } public string StudyID { get; set; } public string SeriesID { get; set; } public string InstanceID { get; set; }
        public override string ToString()
        {
            var r = string.Format("{0}/{1}/{2}/{3}", PatientID, StudyID, SeriesID, InstanceID);
            return r;
        }
    } 

var listofstring = list.ConvertAll<string>(x => x.ToString()).ToList();
var listofstringdistinct = listofstring.Distinct().ToList();
公共类实例信息
{
公共字符串PatientID{get;set;}公共字符串StudyID{get;set;}公共字符串序列ID{get;set;}公共字符串实例ID{get;set;}
公共重写字符串ToString()
{
var r=string.Format(“{0}/{1}/{2}/{3}”,PatientID,StudyID,SeriesID,InstanceID);
返回r;
}
} 
var listofstring=list.ConvertAll(x=>x.ToString()).ToList();
var listofstringdistinct=listofstring.Distinct().ToList();

这更容易阅读和理解。

不确切地知道您需要什么,但这(很长的代码)将返回一个按您所说分组的词典(词典…)(即
PatientID/StudyID/SeriesID/InstanceID
):


我不知道你提出的查询是否是你真正想要或需要的查询,但是假设它是,让我们考虑是否有更好的方法来写它。 您想查看的地方是C#4规范的第7.16.2.1节,为了方便起见,我在此引用其中的一部分:


具有连续性的查询表达式

翻译成


这是清楚的吗?让我们来看一看我用星号标记的查询片段:

var groups = from instance in instances
             group instance by instance.PatientID into patientGroups
             from studyGroups in
                 **** (from instance in patientGroups
                   group instance by instance.StudyID) ****
                   from seriesGroup in
                       (from instance in studyGroups
                        group instance by instance.SeriesID)
                            from instanceGroup in
                                 (from instance in seriesGroup
                                  group instance by instance.InstanceID)
             group instanceGroup by patientGroups.Key;
我们有

from studyGroups in ( from ... ) ...
说明书上说这相当于

from ... into studyGroups ...
因此,我们可以将您的查询重写为

var groups = from instance in instances
             group instance by instance.PatientID into patientGroups
             from instance in patientGroups
             group instance by instance.StudyID into studyGroups
             from seriesGroup in
             **** (from instance in studyGroups
                  group instance by instance.SeriesID) ****
                      from instanceGroup in
                           (from instance in seriesGroup
                            group instance by instance.InstanceID)
             group instanceGroup by patientGroups.Key;
再做一次,现在我们有了

from seriesGroup in (from ... ) ...
说明书上说这和

from ... into seriesGroup ...
那么就这样重写它:

var groups = from instance in instances 
             group instance by instance.PatientID into patientGroups
             from instance in patientGroups 
             group instance by instance.StudyID into studyGroups
             from instance in studyGroups
             group instance by instance.SeriesID into seriesGroup
             from instanceGroup in
              ****     (from instance in seriesGroup
                   group instance by instance.InstanceID) ****
             group instanceGroup by patientGroups.Key;
再一次

var groups = from instance in instances 
             group instance by instance.PatientID into patientGroups
             from instance in patientGroups 
             group instance by instance.StudyID into studyGroups
             from instance in studyGroups
             group instance by instance.SeriesID into seriesGroup
             from instance in seriesGroup
             group instance by instance.InstanceID into instanceGroup
             group instanceGroup by patientGroups.Key;
我希望你同意这一点,它更容易阅读。我会通过改变“实例”被用了半打次来表示不同的东西的事实来提高它的可读性:

var groups = from instance in instances 
             group instance by instance.PatientID into patientGroups
             from patientGroup in patientGroups 
             group patientGroup by instance.StudyID into studyGroups
             from studyGroup in studyGroups
             group studyGroup by studyGroup.SeriesID into seriesGroups
             from seriesGroup in seriesGroups
             group seriesGroup by seriesGroup.InstanceID into instanceGroup
             group instanceGroup by patientGroups.Key;
我不知道这是否真的是解决问题所需的查询,但至少这是一个你可以推理的查询,而不必自暴自弃地尝试跟踪所有嵌套


这种技术被称为“查询延续”。基本上,其思想是延续目前为止在查询上引入了一个新的范围变量。

以下查询语法中的Linq语句应该可以解决您的问题

 var groups = from instance in instances
                        group instance by instance.PatientGuid into patientGroups
                        select new
                        {
                            patientGroups.Key,
                            StudyGroups = from instance in patientGroups
                                          group instance by instance.StudyGuid into studyGroups
                                          select new 
                                          { 
                                          studyGroups.Key,
                                          SeriesGroups = from c in studyGroups
                                                         group c by c.SeriesGuid into seriesGroups
                                                         select seriesGroups
                                          }

                        };
然后,您可以在组上使用以下一组嵌套的foreach循环迭代组。这将允许您高效地创建目录树,并在每个级别执行任何其他操作

foreach (var patientGroups in groups)
             {
                 Console.WriteLine("Patient Level = {0}", patientGroups.Key);
                 foreach (var studyGroups in patientGroups.StudyGroups)
                 {
                     Console.WriteLine("Study Level = {0}", studyGroups.Key);
                     foreach (var seriesGroups in studyGroups.SeriesGroups)
                     {
                         Console.WriteLine("Series Level = {0}", seriesGroups.Key);
                         foreach (var instance in seriesGroups)
                         {
                             Console.WriteLine("Instance Level = {0}", instance.InstanceGuid);
                         }
                     }
                 }

             }

这是一个概念证明,但初步测试表明它工作正常。任何评论都将不胜感激。

Eric Lippert完美地解释了如何避免可怕的嵌套,并使用“查询继续”(关键字
转换为
中)只编写一个简单的查询

我认为您可以再多做一步,直接使用
GroupBy
方法编写。有时,直接使用LINQ方法可以获得更清晰的代码,我认为这就是一个这样的示例:

var groups = instances.
    GroupBy(instance => instance.PatientID).
    GroupBy(patientGroup => patientGroup.StudyID).
    GroupBy(studyGroup => studyGroup.SeriesID).
    GroupBy(seriesGroup => seriesGroup.InstanceID).
    GroupBy(instanceGroup => patientGroups.Key);
(我真的不知道这是否是你想要的——我只是对Eric写的东西做了一个“句法转换”——我相信我没有改变Eric提问的意思)

编辑最后一个
分组依据
可能有一些诡计,因为它不是完全规则的。

这会有帮助吗?var结果
foreach (var patientGroups in groups)
             {
                 Console.WriteLine("Patient Level = {0}", patientGroups.Key);
                 foreach (var studyGroups in patientGroups.StudyGroups)
                 {
                     Console.WriteLine("Study Level = {0}", studyGroups.Key);
                     foreach (var seriesGroups in studyGroups.SeriesGroups)
                     {
                         Console.WriteLine("Series Level = {0}", seriesGroups.Key);
                         foreach (var instance in seriesGroups)
                         {
                             Console.WriteLine("Instance Level = {0}", instance.InstanceGuid);
                         }
                     }
                 }

             }
var groups = instances.
    GroupBy(instance => instance.PatientID).
    GroupBy(patientGroup => patientGroup.StudyID).
    GroupBy(studyGroup => studyGroup.SeriesID).
    GroupBy(seriesGroup => seriesGroup.InstanceID).
    GroupBy(instanceGroup => patientGroups.Key);