LINQ OrderBy的异常

bom*_*sen 2 c# linq linq-to-objects linq-query-syntax

我还是LINQ的新手,我创建了一个我觉得有点笨拙的查询.我想知道是否有任何方法可以简化它?

我的查询如下:

var agreementsMatching = _ctx.LeasingAgreements
    .Where(x => x.Dealer.Id == dealer.dealerId)
    .ToList();
var ag = agreementsMatching
    .OrderBy(o => o.Model.Specification)
    .OrderBy(o => o.Model.ModelName)
    .OrderBy(o => o.Model.ModelBrand)
    .OrderBy(c => c.LeasingAgreementClicks)
    .GroupBy(sg => sg.Model.Specification)
    .Select(sg => new { GroupId = sg.Key, Agreements = sg });
Run Code Online (Sandbox Code Playgroud)

我认为它可能不是最好的查询的原因还在于它给了我一个例外:

至少有一个对象必须实现IComparable.

我知道它发生是因为一个或多个对象没有实现IComparable接口.我只是不确定如何实际处理它.

任何帮助/提示都非常感谢这一个!

编辑:事实证明我不需要所有这些OrderBy电话.我可以这样做:

var agreementsMatching = _ctx.LeasingAgreements
    .Where(x => x.Dealer.Id == dealer.dealerId)
    .ToList();
var ag = agreementsMatching
    .GroupBy(sg => sg.Model.Specification)
    .Select(sg => new { GroupId = sg.Key, Agreements = sg });
Run Code Online (Sandbox Code Playgroud)

虽然问题现在解决了,但我还是想学习如何避免上述错误:)

Jon*_*nna 8

首先要注意的是

someList.OrderBy(item => item.SomeProp).OrderBy(item => item.SomeOtherProp);
Run Code Online (Sandbox Code Playgroud)

或多或少相当于:

someList.OrderBy(item => item.SomeOtherProp);
Run Code Online (Sandbox Code Playgroud)

因为第二次OrderBy撤销了第一次的工作.一般你想要:

someList.OrderBy(item => item.SomeProp).ThenBy(item => item.SomeOtherProp);
Run Code Online (Sandbox Code Playgroud)

注意等效:

from item in someList orderby item.SomeProp, item.SomeOtherProp select item
Run Code Online (Sandbox Code Playgroud)

使用ThenBy如上所述.

现在,使用任何语法,使用Linq到对象(但不是数据库和其他linq查询提供程序)OrderBy通过调用(IComparable<T>如果可用),IComparable否则(除非我们稍后会出现异常).由于agreementsMatching是内存中的列表,因此这是使用的形式.这就是如何OrderBy知道"order by"对于给定类型意味着什么.

字符串和所有内置的数字类型(int,double等),实现IComparable<T>所有它们都可以正常使用,而无需您的任何操作.

据推测,您上面订购的至少一个属性不是这些类型中的一种,而是您自己的类型.我不知道你的代码中有哪些,所以我将编写以下内容:

我将假设该Specification属性返回一个Spec对象,并且该Spec对象应该Name根据不变文化以不区分大小写的方式按其属性排序.所以我开始:

class Spec
{
  public property Name
  {
     get { /* code I don't care about here*/ }
     set { /* code I don't care about here*/ }
  }
  /* more code I don't care about here*/
}
Run Code Online (Sandbox Code Playgroud)

我添加了一个实现IComparable<Spec>.IComparable在这种情况下实现这是一个好主意,为了向后兼容,尽管可以跳过它.两者都定义了一个方法,用于将实例与另一个对象进行比较,如果实例为"较小"(按顺序排在第一位),则返回数字<0;如果它们在排序中等效则返回0,如果实例为"更大"则返回> 0:

class Spec : IComparable<Spec>, IComparable
{
  public property Name
  {
     get { /* code I don't care about here*/ }
     set { /* code I don't care about here*/ }
  }
  /* more code I don't care about here*/
  public int CompareTo(Spec other)
  {
     if(other == null)
       return 1;
     //Often we make use of an already-existing comparison, though not always
     return string.Compare(Name, other.Name, StringComparison.InvariantCultureIgnoreCase)
  }
  //For backwards compatibility:
  public int CompareTo(object other)
  {
    if(other == null)
      return 1;
    Spec os = other as Spec;
    if(os == null)
      throw new ArgumentException("Comparison between Spec and " + other.GetType().FullName + " is not allowed");
    return CompareTo(os);
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,OrderBy可以处理Spec对象的比较,它将通过名称进行比较(此外,我们可以使用List<Spec>.Sort()和其他一些东西.

最后一个事情,就是如果我们需要通过一些其他的规则进行排序,或者如果我们需要排序类型,其中我们没有源代码,它没有实现会发生什么IComparable<T>IComparable

在这里,我们可以创建一个实现的类,IComparer<T>这将绕过使用IComparable<T>.这是一个演示这个的例子:

public class OddBeforeEven : IComparer<int>
{
  public int Compare(int x, int y)
  {
    int compareOddEven = y % 2 - x % 2;
    if(compareOddEven != 0)
      return compareOddEven;
    //if both odd or both even, use default ordering:
    return x.CompareTo(y);
  }
}
/* ... */
var oddBeforeEven0To20 = Enumerable.Range(0, 21).OrderBy(x => x, new OddBeforeEven());
/*Enumerating oddBeforeEven0To20 will produce 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20*/
Run Code Online (Sandbox Code Playgroud)

最后一点.

你确定需要ToList()在你的问题中吗?

Linq的新手通常会ToList()在很大程度上将部分内容放在一个可以更好地描绘的结构中,部分原因是很多教程示例都会大量使用它.

当然,有时候ToList()才能做出明智的事情,而且有必要将内存中非内存形式的内容(例如针对数据库或XMLDocument)带到内存中.然而:

如果数据库中有东西出现,那么大部分时间最好尽可能长时间保存.这有很多例外,但通常希望将数据保存在数据库中并通过将其作为优化少数异常的内容进行优化,而不是习惯于快速将内容放入内存然后优化内存.将它保存在数据库中的时间百分比更快!

如果你需要切换到linq-to-objects,ToEnumerable()就可以在不急于执行查询的情况下执行此操作,因此它比ToList()大多数时候都要好.

有时候ToList()效率最高(特别是如果你必须两次点击相同的列表),但是当你可以看到它的明确需要时,你应该调用它,而不是默认情况下.