I need to retrieve a set of Widgets from my data access layer, grouped by widget.Manufacturer, to display in a set of nested ASP.NET ListViews.
我需要从我的数据访问层检索一组Widgets,按widget.Manufacturer分组,以显示在一组嵌套的ASP.NET ListViews中。
The problem is that (as far as I can tell) the nested ListView approach requires me to shape the data before using it, and I can't figure out the best approach to take. The best I've been able to come up with so far is to put a LINQ query in my data access layer like so:
问题是(据我所知)嵌套的ListView方法要求我在使用之前对数据进行整形,我无法找出最佳方法。到目前为止,我能够提出的最好的方法是在我的数据访问层中放置LINQ查询,如下所示:
var result = from widget in GetAllWidgets(int widgetTypeID)
group widget by widget.Manufacturer into groupedWidgets
let widgets = from widgetGroup in groupedWidgets
select widgetGroup
select new { Manufacturer = groupedWidgets.Key, Widgets = widgets };
Of course, anonymous types can't be passed around, so that doesn't work. Defining a custom class to enclose data seems like the wrong way to go. Is there some way I can perform the grouping on the ASP.NET side of things? I'm using ObjectDataSources to access the DAL.
当然,匿名类型不能传递,所以这不起作用。定义一个自定义类来封装数据似乎是错误的方法。有没有什么方法可以在ASP.NET方面执行分组?我正在使用ObjectDataSources来访问DAL。
Updated: OK, I'm not creating an anonymous type anymore, and instead my DAL passes an IEnumerable<IGrouping<Manufacturer, Widget>>
to the ASP.NET page, but how can I use this in my ListViews? I need to render the following HTML (or something pretty much like it)
更新:好的,我不再创建匿名类型,而是我的DAL将IEnumerable
<ul>
<li>Foo Corp.
<ol>
<li>Baz</li>
<li>Quux</li>
</ol>
</li>
<li>Bar Corp.
<ol>
<li>Thinger</li>
<li>Whatsit</li>
</ol>
</li>
</ul>
Originally, I had a ListView within a ListView like so:
最初,我在ListView中有一个ListView,如下所示:
<asp:ListView ID="ManufacturerListView">
<LayoutTemplate>
<ul>
<asp:Placeholder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Manufacturer.Name") %>' />
<li>
<asp:ListView ID="WidgetsListView" runat="server" DataSource='<%# Eval("Widgets") %>'>
<LayoutTemplate>
<ol>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</ol>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Name") %>'></li>
</ItemTemplate>
</asp:ListView>
</li>
</ItemTemplate>
</asp:ListView>
Note how the DataSource
property of WidgetsListView is itself databound. How can I duplicate this functionality without reshaping the data?
请注意WidgetsListView的DataSource属性本身是如何数据绑定的。如何在不重塑数据的情况下复制此功能?
This is getting kind of complicated, sorry if I should have just made a separate question instead.
这有点复杂,对不起,如果我应该刚刚提出一个单独的问题。
3 个解决方案
#1
12
Ok, I'm going to contradict my prior statement. Since eval wants some kind of property name in the nested control, we should probably shape that data.
好吧,我要反驳我先前的陈述。由于eval在嵌套控件中需要某种属性名称,因此我们应该对该数据进行整形。
public class CustomGroup<TKey, TValue>
{
public TKey Key {get;set;}
public IEnumerable<TValue> Values {get;set;}
}
// and use it thusly...
//并因此使用它......
IEnumerable<CustomGroup<Manufacturer, Widget>> result =
GetAllWidgets(widgetTypeId)
.GroupBy(w => w.Manufacturer)
.Select(g => new CustomGroup<Manufacturer, Widget>(){Key = g.Key, Values = g};
/// and even later...
///甚至更晚......
<asp:ListView ID="ManufacturerListView">
<LayoutTemplate>
<ul>
<asp:Placeholder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Key.Name") %>' />
<li>
<asp:ListView ID="WidgetsListView" runat="server" DataSource='<%# Eval("Values") %>'>
<LayoutTemplate>
<ol>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</ol>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Name") %>'></li>
</ItemTemplate>
</asp:ListView>
</li>
</ItemTemplate>
</asp:ListView>
#2
5
I've just spent quite a while on this. Eventually found the solution and it's so simple.
我刚刚在这上花了很长时间。最终找到了解决方案并且它非常简单。
var enumerableData = myData.Tables[0].AsEnumerable();
var groupedData = enumerableData.GroupBy(x => x["GroupingColumn"]);
myParentRepeater.DataSource = groupedData;
myParentRepeater.DataBind();
<asp:Repeater ID="myParentRepeater" runat="server">
<ItemTemplate>
<h3><%#Eval("Key") %></h3>
<asp:Repeater ID="myChildRepeater" runat="server" DataSource='<%# Container.DataItem %>'>
<ItemTemplate>
<%#((DataRow)Container.DataItem)["ChildDataColumn1"] %>
<%#((DataRow)Container.DataItem)["ChildDataColumn2"] %>
</ItemTemplate>
<SeparatorTemplate>
<br />
</SeparatorTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Eval("Key") returns the grouped value. When retrieving child info, Container.DataItem is of type IGrouping but you simply cast it to the correct type.
Eval(“Key”)返回分组值。检索子信息时,Container.DataItem的类型为IGrouping,但您只需将其强制转换为正确的类型。
Hope this helps someone else.
希望这有助于其他人。
#3
3
When you're using Linq to group, you can get a strongly typed object without that shaping:
当您使用Linq进行分组时,您可以获得一个强类型对象而不进行整形:
List<int> myInts = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<IGrouping<int, int>> myGroups = myInts.GroupBy(i => i % 2);
foreach (IGrouping<int, int> g in myGroups)
{
Console.WriteLine(g.Key);
foreach (int i in g)
{
Console.WriteLine(" {0}", i);
}
}
Console.ReadLine();
In your case, you'd have:
在你的情况下,你有:
IEnumerable<IGrouping<Manufacturer, Widget>> result =
GetAllWidgets(widgetTypeId).GroupBy(w => w.Manufacturer);
This will let you return the result from the method.
这将允许您返回方法的结果。
#1
12
Ok, I'm going to contradict my prior statement. Since eval wants some kind of property name in the nested control, we should probably shape that data.
好吧,我要反驳我先前的陈述。由于eval在嵌套控件中需要某种属性名称,因此我们应该对该数据进行整形。
public class CustomGroup<TKey, TValue>
{
public TKey Key {get;set;}
public IEnumerable<TValue> Values {get;set;}
}
// and use it thusly...
//并因此使用它......
IEnumerable<CustomGroup<Manufacturer, Widget>> result =
GetAllWidgets(widgetTypeId)
.GroupBy(w => w.Manufacturer)
.Select(g => new CustomGroup<Manufacturer, Widget>(){Key = g.Key, Values = g};
/// and even later...
///甚至更晚......
<asp:ListView ID="ManufacturerListView">
<LayoutTemplate>
<ul>
<asp:Placeholder ID="itemPlaceholder" runat="server" />
</ul>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Key.Name") %>' />
<li>
<asp:ListView ID="WidgetsListView" runat="server" DataSource='<%# Eval("Values") %>'>
<LayoutTemplate>
<ol>
<asp:PlaceHolder runat="server" ID="itemPlaceholder" />
</ol>
</LayoutTemplate>
<ItemTemplate>
<li><asp:Label Text='<%# Eval("Name") %>'></li>
</ItemTemplate>
</asp:ListView>
</li>
</ItemTemplate>
</asp:ListView>
#2
5
I've just spent quite a while on this. Eventually found the solution and it's so simple.
我刚刚在这上花了很长时间。最终找到了解决方案并且它非常简单。
var enumerableData = myData.Tables[0].AsEnumerable();
var groupedData = enumerableData.GroupBy(x => x["GroupingColumn"]);
myParentRepeater.DataSource = groupedData;
myParentRepeater.DataBind();
<asp:Repeater ID="myParentRepeater" runat="server">
<ItemTemplate>
<h3><%#Eval("Key") %></h3>
<asp:Repeater ID="myChildRepeater" runat="server" DataSource='<%# Container.DataItem %>'>
<ItemTemplate>
<%#((DataRow)Container.DataItem)["ChildDataColumn1"] %>
<%#((DataRow)Container.DataItem)["ChildDataColumn2"] %>
</ItemTemplate>
<SeparatorTemplate>
<br />
</SeparatorTemplate>
</asp:Repeater>
</ItemTemplate>
</asp:Repeater>
Eval("Key") returns the grouped value. When retrieving child info, Container.DataItem is of type IGrouping but you simply cast it to the correct type.
Eval(“Key”)返回分组值。检索子信息时,Container.DataItem的类型为IGrouping,但您只需将其强制转换为正确的类型。
Hope this helps someone else.
希望这有助于其他人。
#3
3
When you're using Linq to group, you can get a strongly typed object without that shaping:
当您使用Linq进行分组时,您可以获得一个强类型对象而不进行整形:
List<int> myInts = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<IGrouping<int, int>> myGroups = myInts.GroupBy(i => i % 2);
foreach (IGrouping<int, int> g in myGroups)
{
Console.WriteLine(g.Key);
foreach (int i in g)
{
Console.WriteLine(" {0}", i);
}
}
Console.ReadLine();
In your case, you'd have:
在你的情况下,你有:
IEnumerable<IGrouping<Manufacturer, Widget>> result =
GetAllWidgets(widgetTypeId).GroupBy(w => w.Manufacturer);
This will let you return the result from the method.
这将允许您返回方法的结果。