【问题标题】:Entity Framework 4 - List<T> Order By based on T's children's property实体框架 4 - List<T> Order By 基于 T 的子属性
【发布时间】:2011-04-20 11:30:37
【问题描述】:

我有以下代码-

 public void LoadAllContacts()
        {
            var db = new ContextDB();
            var contacts = db.LocalContacts.ToList();
            grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name));
            grdItems.DataBind();
        }

我正在尝试根据每个联系人中包含的区域名称对联系人列表进行排序。当我尝试上述方法时,我得到“至少一个对象必须实现 IComparable。”。有没有一种简单的方法来代替编写自定义 IComparer?

谢谢!

【问题讨论】:

  • 所以 Areas 是另一个实体列表,即每个 LocalContact 有多个 Area?您希望如何使用多个子元素进行排序-您能举个例子吗?它很可能归结为从区域中提取一些信息,例如第一个区域的字符串名称,并对其进行排序。
  • 没错 - 区域是另一个实体列表。我希望此列表按名称排序。联系人“XYZ”可能是“亚洲”和“非洲”的一部分。联系人“ABC”可能只是“新西兰”的一部分。我想在网格顶部看到 XYZ,然后是 ABC。有意义吗?
  • 并非如此。如果 XYZ 是非洲和新西兰的一部分,而 ABC 是法国的一部分怎么办?那么XYZ是不是因为非洲法国而排在第一位?或者您需要在列表中重复 XYZ,即 XYZ 代表非洲,ABC 代表法国,XYZ 代表新西兰?还是别的什么?

标签: linq entity-framework entity sql-order-by generic-list


【解决方案1】:

试试这个:

public void LoadAllContacts()
{
    var db = new ContextDB();
    var contacts = db.LocalContacts.ToList();
    grdItems.DataSource = contacts.OrderBy(x => x.Areas.OrderBy(y => y.Name).First().Name);
    grdItems.DataBind();
}

这将在按名称排序区域之后按第一个区域名称对联系人进行排序。

希望这会有所帮助:)

编辑:修复了代码中的错误。 (.First().姓名)

【讨论】:

  • 坏主意。如果您有数以百万计的联系人怎么办?这会将它们全部提取到内存中。
  • 解决方案或相关方法的坏主意?该方法是“应该”加载所有联系人!如果解决方案不好,那么我尝试了查询并查看了生成的 SQL(使用 LinqPad),并且排序是在数据库级别完成的,而不是在内存中(这意味着区域没有加载到内存中)
  • @AbdouMoumen 非常抱歉,我没有正确阅读问题。我的错。只是我对看到没有WhereTakeToList 电话过敏:-)
  • 您关于在数据库中完成排序的声明对我来说没有多大意义......除非您的意思是区域是延迟加载的,在这种情况下您有一个 SELECT N+1 问题...
  • @jeroenh 我在这里举了一个例子tinypaste.com/3e2931我就是这样看的,也许你可以纠正我:)
【解决方案2】:

我正在与@AbdouMoumen 讨论,但最后我想我会提供自己的答案:-)

他的答案有效,但此代码中有两个性能问题(答案都与原始问题相同)。

首先,代码加载数据库中的所有联系人。这可能是也可能不是问题,但总的来说,我建议不要这样做。许多现代控件支持开箱即用的分页/过滤,因此您最好提供尚未评估的IQueryable&lt;T&gt; 而不是List&lt;T&gt;。但是,如果您需要内存中的所有内容,则应将ToList 延迟到最后一刻。

其次,在 AbdouMoumen 的回答中,有一个所谓的“SELECT N+1”问题。 Entity Framework 将默认使用延迟加载来获取其他属性。 IE。在访问之前,不会从数据库中获取 Areas 属性。在这种情况下,这将发生在控件“for循环”中,同时它按名称对结果集进行排序。

打开 SQL Server Profiler 以了解我的意思:您将看到所有联系人的 SELECT 语句,以及为每个联系人获取该联系人的区域的附加 SELECT 语句。

更好的解决方案如下:

public void LoadAllContacts()
{
    using (var db = new ContextDB())
    {

        // note: no ToList() yet, just defining the query 
        var contactsQuery = db.LocalContacts
            .OrderBy(x => x.Areas
                       .OrderBy(y => y.Name)
                       .First().Name);  

        // fetch all the contacts, correctly ordered in the DB
        grdItems.DataSource = contactsQuery.ToList();

        grdItems.DataBind();
    }
}

【讨论】:

  • 是的,所以它只是原始代码中的“ToList”,我之前没有注意到;)我认为您必须在内部 OrderBy 之后添加 .First().Name,即表达式应该返回一个值。
【解决方案3】:

是一对一的关系(Contact-&gt;Area)吗?
如果是,请尝试以下操作:

public partial class Contact
{
    public string AreaName
    {
        get
        {
            if (this.Area != null)
               return this.Area.Name;
            return string.Empty;
        }
     }
 }

然后

grdItems.DataSource = contacts.OrderBy(x => x.AreaName);

【讨论】:

  • 你能在 EF4 中做到这一点吗?是否将其移入单独的属性算作 SQL 表达式引擎无法计算的 Invoke? (还是我在考虑 EF 3.5?)
猜你喜欢
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-26
  • 1970-01-01
  • 2015-07-11
  • 2021-05-25
相关资源
最近更新 更多