根据实体框架导航属性中的项数对绑定的ASP.NET GridView进行排序



我有一个ASP.NET页面,其中GridView控件绑定到EntityDataSource(请参阅下面的简化代码(。该网格显示Parent项的列表,并包含一列以显示此父项的Children的.Count。我可以让网格正确地显示计数,但我不知道如何使用asp:TemplateField-SortExpression值来将排序设置为子项的计数。

以下是我的代码(为了清晰起见,进行了简化(。。。

<asp:EntityDataSource ID="edsParentList" runat="server" 
     ConnectionString="name=FooEntities" 
     DefaultContainerName="FooEntities" 
     EnableFlattening="False" 
     EntitySetName="Parents" 
     EntityTypeFilter="Parent"
     Include="Children"
     OrderBy="it.Name"
     Where="(it.Name LIKE '%' + @ParentNameLike + '%')
     >
     <WhereParameters>
         <asp:Parameter Name="ParentNameLike" Type="String" DefaultValue="_" />
     </WhereParameters>
 </asp:EntityDataSource>
 <asp:GridView ID="grdParents" runat="server" 
     AllowPaging="True" 
     AllowSorting="True" 
     AutoGenerateColumns="False" 
     DataSourceID="edsParentList"
     PageSize="20" 
     onpageindexchanged="grdParents_PageIndexChanged" onsorted="grdParents_Sorted" >
     <Columns>
         <asp:TemplateField HeaderText="Name" SortExpression="Name">
             <ItemTemplate>
                 <a href="Parent.aspx?id=<%# Eval("ParentID") %>"><%# Eval("Name") %></a>
             </ItemTemplate>
         </asp:TemplateField>
         <asp:BoundField DataField="BirthDate" HeaderText="Birth Date" 
              DataFormatString="{0:yyyy-MM-dd HH:mm}"
              SortExpression = "BirthDate" />
         <asp:TemplateField HeaderText="Children" SortExpression="Children.Count">
             <ItemTemplate>
                 <asp:Label ID="lblChildCount" runat="server" 
                  Text='<%# Eval("Children.Count") %>'></asp:Label>
             </ItemTemplate>
         </asp:TemplateField>
     </Columns>
 </asp:GridView>

这将精细显示网格。然而,当我点击Children列的标题时,会抛出以下错误:

"Count"不是的成员"Transient.collection[FooEntities.Child(Nullable=True,DefaultValue=(]"。若要提取集合元素的属性,请使用子查询对集合进行迭代。

我的问题是:如何对由子对象集合组成的导航属性的.Count((启用排序

有没有一种方法可以用SortExpression指定这一点,或者我必须分解并手动进行所有的分页和排序?(我显然更喜欢避免!(

在您的位置上,由于实体类被声明为分部类,我会尝试为Parent实体创建一个补充的分部类代码文件,并添加一个ChildCount只读属性。此属性将引用Children导航属性。然后我会对此进行分类。

在这里,我假设EntityDataSource可以处理实体上的派生属性。我还没有对此进行测试。

我已经重现了错误(在一个更简单的例子中(,我认为不可能找到任何对儿童计数执行排序的SortExpression

我看到了两个重要的附加信息:

  • 单击列标题时引发的异常是EntitySqlException
  • 堆栈跟踪中最后抛出异常的方法是EntityDataSourceView.ExecuteSelect(DataSourceSelectArguments arguments)

ExecuteSelect是DataSourceViewabstract方法,由各种特定的数据源控件覆盖,以执行从数据存储加载数据的实际工作。在EntityDataSource的情况下,对应的视图是EntityDataSourceView,从抛出的异常(EntitySqlException-我会得出结论,ExecuteSelect使用实体SQL构建查询。

参数DataSourceSelectArguments包含在EntityDateSource中定义的参数,还有一个SortExpression,它很可能只是您在TemplateField上指定的排序表达式。这些用于组成实体SQL中的最终查询。

我假设SortExpression只是作为实体SQL语句中的ORDER BY子句传递的。这看起来像:

ORDER BY Children.Count

但这是无效的实体SQL。您可以使用虚线路径作为导航引用,但不能在Entity SQL中使用导航集合的任何"类似LINQ"的方法或属性(如Count(。

可以编写一个有效的实体SQL,按照子级计数进行排序。根据这个例子(在文件中搜索"按相关实体排序"(,正确的实体SQL语句应该是:

"SELECT VALUE p2.p 
 FROM (SELECT p, ANYELEMENT(SELECT VALUE Count(c.ChildId) FROM p.Children AS c)
                 AS childCount
       FROM Parents AS p)
 AS p2
 ORDER BY p2.childCount"

(这太难阅读了,我甚至不知道如何在语义上正确地缩进代码。(

我认为这个ANYELEMENT(SELECT...构造是异常正在讨论的"子查询",并且希望拥有它来计算子集合的元素。

显然,如果没有定义p2的整个子查询,就无法将p2.childCount传递到SortExpression中。

我的结论是:没有希望为儿童计数找到一个有效的SortExpression

也许有一种方法不使用SortExpression——例如,在标头上捕获一个点击事件,然后在事件处理程序中手动构建完整的查询,但我真的不知道这是否可能以及如何可能。

为什么EntityDataSourceGridView的消费者必须自己解决这个问题?您是否看到以下文档:">当数据源的类型为EntityDataSource时,GridViewTemplateFieldSortExpression必须是有效的实体SQLORDER BY子句。"我没有。就像:"SortExpression是一个排序表达式。">

不幸的是,由于没有明确说明SortExpression会发生什么,正确的语法是什么,支持或不支持什么类型的表达式,所以这个答案与其说是答案,不如说是猜测。

最新更新