C# 排序列表<;T>;在相关列表中的属性上<;T>;

C# 排序列表<;T>;在相关列表中的属性上<;T>;,c#,linq-to-objects,C#,Linq To Objects,假设我们有两个列表。第一个是销售总额列表: class SalesTotals { public Guid EmpID { get; set; } public string EmpName { get; set; } public decimal? TotalSales { get; set; } } 然后,我们有另一份按年度列出的销售清单: class YearlySales { public Guid EmpID { get; set; } pub

假设我们有两个
列表
。第一个是销售总额列表:

class SalesTotals
{
    public Guid EmpID { get; set; }
    public string EmpName { get; set; }
    public decimal? TotalSales { get; set; }
}
然后,我们有另一份按年度列出的销售清单:

class YearlySales
{
    public Guid EmpID { get; set; }
    public short SalesYear { get; set; }
    public decimal? YearlyTotals { get; set; }
}
它们一起用于创建一个“交叉表”报告,列出每个员工的总销售额,然后是一列每年的相关年度销售额。它看起来像这样:

| Name | Total | 2018 | 2017 | 2016 |
+------+-------+------+------+------+
|  Joe |    70 |   20 |      |   50 |
|  Sam |    60 |   30 |   20 |   10 |
| Fred |    50 |   30 |      |   20 |
|  Bob |    40 |   10 |   15 |   15 |
SELECT ST.EmpID, ST.EmpName, ST.TotalSales
FROM SalesTotals AS ST
LEFT JOIN YearlySales AS YS ON ST.EmpID=YS.EmpID
WHERE YS.SalesYear=@SortBySalesYear OR YS.SalesYear IS NULL
ORDER BY YS.YearlySales DESC, ST.TotalSales DESC
默认情况下,报告按TotalSales排序(没有问题)。但如果我们想按一年来进行分类,事情就变得更加棘手了。按2017年排序(然后按总数排序):

我假设我们想(左)
加入
这两个
列表
EmpID
其中saleyear=
然后
OrderBy
YearlyTotals,TotalSales
(因为YearlyTotals在给定年份可能不存在,在这种情况下我们仍然需要某种类型的订单)。因此,我们也必须考虑到今年可能不会有一个记录加入(所以它需要是一个左联)。 如果我正在编写SQL,它将如下所示:

| Name | Total | 2018 | 2017 | 2016 |
+------+-------+------+------+------+
|  Joe |    70 |   20 |      |   50 |
|  Sam |    60 |   30 |   20 |   10 |
| Fred |    50 |   30 |      |   20 |
|  Bob |    40 |   10 |   15 |   15 |
SELECT ST.EmpID, ST.EmpName, ST.TotalSales
FROM SalesTotals AS ST
LEFT JOIN YearlySales AS YS ON ST.EmpID=YS.EmpID
WHERE YS.SalesYear=@SortBySalesYear OR YS.SalesYear IS NULL
ORDER BY YS.YearlySales DESC, ST.TotalSales DESC
我对林克还不够好,还不能弄明白这一点。事实上,我几乎找不到任何地方(可能一次做的太多了,可能我需要把它分解成单独的步骤,而不是搜索一行)

那么,有没有办法让Linq做到这一点?还是我应该尝试其他类型的方法

注意:这里我只需要一个“就地”排序。我不需要/想要在这里返回不同类型的
列表
,只需要一个排序的
列表

编辑:我更喜欢Linq“查询语法”,因为它对我来说更直观(强大的SQL背景)。所以我更喜欢使用查询语法而不是方法语法的答案

编辑:以下是测试用例设置:

class SalesTotals
{
    public int EmpID { get; set; }
    public string EmpName { get; set; }
    public decimal? TotalSales { get; set; }
}
class YearlySales
{
    public int EmpID { get; set; }
    public short SalesYear { get; set; }
    public decimal? YearlyTotals { get; set; }
}
class TestSort
{
    public TestSort()
    {
        var st = new List<SalesTotals>
        {
            new SalesTotals() { EmpID = 1, EmpName = "Joe", TotalSales = 70 },
            new SalesTotals() { EmpID = 2, EmpName = "Sam", TotalSales = 60 },
            new SalesTotals() { EmpID = 3, EmpName = "Fred", TotalSales = 50 },
            new SalesTotals() { EmpID = 4, EmpName = "Bob", TotalSales = 40 }
        };

        var ys = new List<YearlySales>
        {
            new YearlySales() { EmpID = 1, SalesYear = 2018, YearlyTotals = 20 },
            new YearlySales() { EmpID = 2, SalesYear = 2018, YearlyTotals = 30 },
            new YearlySales() { EmpID = 3, SalesYear = 2018, YearlyTotals = 30 },
            new YearlySales() { EmpID = 4, SalesYear = 2018, YearlyTotals = 10 },
            new YearlySales() { EmpID = 2, SalesYear = 2017, YearlyTotals = 20 },
            new YearlySales() { EmpID = 4, SalesYear = 2017, YearlyTotals = 15 },
            new YearlySales() { EmpID = 1, SalesYear = 2016, YearlyTotals = 10 },
            new YearlySales() { EmpID = 2, SalesYear = 2016, YearlyTotals = 15 },
            new YearlySales() { EmpID = 3, SalesYear = 2016, YearlyTotals = 50 },
            new YearlySales() { EmpID = 4, SalesYear = 2016, YearlyTotals = 20 }
        };

        st = SortByYear(st, ys, 2017);
    }
    private List<SalesTotals> SortByYear(List<SalesTotals> salesTotals, List<YearlySales> yearlySales, short sortYear)
    {
        // return sorted salesTotals by sortYear using both salesTotals and yearlySales joined on EmpID
    }
}         
class SalesTotals
{
公共int EmpID{get;set;}
公共字符串EmpName{get;set;}
公共十进制?TotalSales{get;set;}
}
年度销售类
{
公共int EmpID{get;set;}
公共短期销售年{get;set;}
公共十进制?年度总计{get;set;}
}
类TestSort
{
公共TestSort()
{
var st=新列表
{
new SalesTotals(){EmpID=1,EmpName=“Joe”,TotalSales=70},
new SalesTotals(){EmpID=2,EmpName=“Sam”,TotalSales=60},
new SalesTotals(){EmpID=3,EmpName=“Fred”,TotalSales=50},
new SalesTotals(){EmpID=4,EmpName=“Bob”,TotalSales=40}
};
var ys=新列表
{
new YearlySales(){EmpID=1,SalesYear=2018,YearlyTotals=20},
new YearlySales(){EmpID=2,SalesYear=2018,YearlyTotals=30},
new YearlySales(){EmpID=3,SalesYear=2018,YearlyTotals=30},
new YearlySales(){EmpID=4,SalesYear=2018,YearlyTotals=10},
new YearlySales(){EmpID=2,SalesYear=2017,YearlyTotals=20},
new YearlySales(){EmpID=4,SalesYear=2017,YearlyTotals=15},
new YearlySales(){EmpID=1,SalesYear=2016,YearlyTotals=10},
new YearlySales(){EmpID=2,SalesYear=2016,YearlyTotals=15},
new YearlySales(){EmpID=3,SalesYear=2016,YearlyTotals=50},
new YearlySales(){EmpID=4,SalesYear=2016,YearlyTotals=20}
};
st=SortByYear(st,ys,2017年);
}
私人列表SortByEAR(列出总销售额、列出年度销售额、短sortYear)
{
//使用EmpID上加入的salesTotals和yearlySales返回按sortYear排序的salesTotals
}
}         

您可以像在SQL中一样编写它

        var results = from t in totals
            join y in years on t.EmpID equals y.EmpID into groupedTable
                      from p in groupedTable.DefaultIfEmpty()
                      where y == null || y.SalesYear == year
                      orderby y.SalesYear, t.TotalSales descending
                      select t;

快速说明:LINQ中的联接默认为内部联接。如果需要外部联接,则必须使用DefaultIfEmpty()调用。

您可以用与SQL中几乎相同的方式编写它

        var results = from t in totals
            join y in years on t.EmpID equals y.EmpID into groupedTable
                      from p in groupedTable.DefaultIfEmpty()
                      where y == null || y.SalesYear == year
                      orderby y.SalesYear, t.TotalSales descending
                      select t;

快速说明:LINQ中的联接默认为内部联接。如果需要外部联接,则必须使用DefaultIfEmpty()调用。

某种程度上有效。需要为销售输入空值

List<YearlySale> YearlySales = new List<YearlySale>() { new YearlySale() { EmpID = 1, Sales = 700, Year = 2018 },
                                                        new YearlySale() { EmpID = 1, Sales = 600, Year = 2017 },
                                                        new YearlySale() { EmpID = 1, Sales = 500, Year = 2016 },
                                                        new YearlySale() { EmpID = 2, Sales = 400, Year = 2018 },
                                                        new YearlySale() { EmpID = 2, Sales = null, Year = 2017 },
                                                        new YearlySale() { EmpID = 2, Sales = 300, Year = 2016 }
                                                        };
List<SalesTotal> SalesTotals = new List<SalesTotal>() { new SalesTotal() { EmpID = 1, EmpName = "stan", TotalSales  = 1800 },
                                                        new SalesTotal() { EmpID = 2, EmpName = "sally", TotalSales = 700 }

                                                        };
var q = from s in SalesTotals
        join y18 in YearlySales 
            on s.EmpID equals y18.EmpID
        join y17 in YearlySales
            on s.EmpID equals y17.EmpID
        join y16 in YearlySales
            on s.EmpID equals y16.EmpID
        where y18.Year == 2018
        where y17.Year == 2017
        where y16.Year == 2016
        select new { SalesTotal = s, Year18 = y18 == null ? 0 : y18.Year, YearS18 = y18 == null ? 0 : y18.Sales
                                   , Year17 = y17 == null ? 0 : y17.Year, YearS17 = y17 == null ? 0 : y17.Sales
                                   , Year16 = y16 == null ? 0 : y16.Year, YearS16 = y16 == null ? 0 : y16.Sales
                    };
foreach (var v in q.OrderBy(x => x.SalesTotal.EmpID))
{
    Debug.WriteLine($"{v.SalesTotal.EmpID} {v.SalesTotal.EmpName} {v.SalesTotal.TotalSales} {v.YearS18} as y18 {v.YearS17} as y17  {v.YearS16} as y16" );
}
List YearlySale=new List(){new YearlySale(){EmpID=1,Sales=700,Year=2018},
new YearlySale(){EmpID=1,Sales=600,Year=2017},
new YearlySale(){EmpID=1,Sales=500,Year=2016},
new YearlySale(){EmpID=2,销售额=400,年份=2018},
new YearlySale(){EmpID=2,Sales=null,Year=2017},
new YearlySale(){EmpID=2,销售额=300,年份=2016}
};
List SalesTotals=new List(){new SalesTotals(){EmpID=1,EmpName=“stan”,TotalSales=1800},
new SalesTotal(){EmpID=2,EmpName=“sally”,TotalSales=700}
};
var q=销售总额中的s
加入y18的YearlySales
在s.EmpID上等于y18.EmpID
加入y17的YearlySales
在s.EmpID上等于y17.EmpID
在YearlySales中加入y16
在s.EmpID上等于y16.EmpID
其中y18.年==2018年
其中y17.年==2017年
其中,y16.年==2016年
选择新的{SalesTotal=s,Year18=y18==null?0:y18。Year,Year18=y18==null?0:y18。Sales
,Year17=y17==null?0:y17。Year17=y17==null?0:y17。销售
,Year16=y16==null?0:y16。Year,Year16=y16==null?0:y16。销售
};
foreach(q.OrderBy中的var v(x=>x.SalesTotal.EmpID))
{
Debug.WriteLine($“{v.SalesTotal.EmpID}{v.SalesTotal.EmpName}{v.SalesTotal.TotalSales}{v.YearS18}作为y18{v.YearS17}作为y17{v.YearS16}作为y16”);
}
工单种类
var orderedSalesTotals = (from st in salesTotals
                          join ys in yearlySales on st.EmpID equals ys.EmpID into ysj
                          from ys in ysj.Where(y => y.SalesYear == SortBySalesYear).DefaultIfEmpty()
                          orderby ys?.YearSales descending, st.TotalSales descending
                          select st).ToList();