Linq数据源, GridView排序
2010-11-23 11:49 Jeff Chow 阅读(974) 评论(3) 编辑 收藏 举报最近迷上了Linq,在做ASP.NET的项目时,把原来的DataTab的数据源也换成了以Linq的方式获取的数据源,即IEnumerable<T>的一个实例。将其绑定到GridView的时候,遇到了排序方面的问题。折腾了一大圈才搞明白.NET 3.5中委托的写法,问题得以解决。
先是Google了一下GridView如何实现IEnumerable<T>数据源的排序,得到如下的解决方案:
protected void GridView_Sorting(object sender, GridViewSortEventArgs e) { if (e.SortDirection == SortDirection.Ascending) { switch (e.SortExpression) { case "property1": //IEnumber<T>.OrderBy(t => t.property1); break; case "property2": //IEnumber<T>.OrderBy(t => t.property2); break; default: break; } } else { switch (e.SortExpression) { case "property1": //IEnumber<T>.OrderByDescending(t => t.property1); break; case "property2": //IEnumber<T>.OrderByDescending(t => t.property2); break; default: break; } } }
显然这种方案,类型排序的属性多了的话会写死人的。
然后又参考了《Gridview使用LINQ与ObjectDataSource实现自动分页和排序》这篇文章,自己看文章不细心,一开始没明白,博主回复后才清楚可以通过添加对System.Linq.Dynamic的引用,直接使用IQueryable<T>.OrderBy(string sortExpression)函数。然后生成,运行,抛异常,具体的异常名字忘记了,反正就是无法动态地创建查询。
抛出这个异常的原因是,上面文章中的作者,IQueryable<T>中的T是Linq to Sql直接生成的模型,而我的项目中,这个模型T并不是直接对应数据库的数据表。我的数据库里边有Tab_DeviceInfo(设备信息表)和Tab_DeviceStatus(设备状态表),但是我的程序代码里边只定义了一个Device类来存放设备的信息和设备的状态。
继续搜索,看到这篇文章,由于本人英文比较搓,其实很多的说明没看懂,只看了代码部分。作者通过反射构造Expression的实例作为参数调用IQueryable<T>.OrderBy(Expression sortExpression)函数实现排序。由此,我想到通过反射去构造IEnumerable<T>.OrderBy(Func key)的参数,并调用它。
首先,Func是个delegate。通过参考《从.NET框架中委托写法的演变谈开去(中)》我搞明白了.NET 3.5中委托的写法,最后得出关于排序的解决办法就是:
string sortExpression; SortDirection sortDirection; IEnumerable<Device> source; //set value. if (sortDirection == SortDirection.Ascending) { GridView1.DataSource = source.OrderBy(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); } else { GridView1.DataSource = source.OrderByDescending(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); }
很怪异,不过我试过了可以实现。前面说了一大堆,其实都只是我解决这个问题的过程而已。不过,可以用于实现泛型为Linq to Sql自动生成的类型的排序。下面奉上完整的例子代码。
Device类:
public class Device { private Tab_DeviceInfo _deviceInfo; /// <summary> /// 构造器。 /// </summary> /// <param name="deviceInfo"></param> internal Device(Tab_DeviceInfo deviceInfo) { _deviceInfo = deviceInfo; //其他信息的赋值,如果有的话。 } #region 公开属性。 /// <summary> /// 数据主键。 /// </summary> public int ID { get { return _deviceInfo.DI_ID; } set { _deviceInfo.DI_ID = value; } } /// <summary> /// 设备编号。 /// </summary> public string NO { get { return _deviceInfo.DI_DeviceNO; } set { _deviceInfo.DI_DeviceNO = value; } } #endregion }
.aspx文件:
<form id="form1" runat="server"> <div> <asp:GridView ID="GridViewDevice" runat="server" AllowSorting="True" OnSorting="GridViewDevice_Sorting"> <Columns> <asp:BoundField DataField="ID" HeaderText="数据主键" SortExpression="ID" /> <asp:BoundField DataField="NO" HeaderText="设备编号" SortExpression="NO" /> </Columns> </asp:GridView> </div> </form>
.aspx.cs文件:
public partial class OrderByProperty : System.Web.UI.Page { private GSMDataContext _data; /// <summary> /// 构造器。 /// </summary> public OrderByProperty() { _data = new GSMDataContext(); } private void bindGridViewData() { if (!IsPostBack) { GridViewDevice.DataKeyNames = new string[] { "ID" }; } string sortExpression = GridViewDevice.Attributes["sortExpression"]; SortDirection sortDirection = GridViewDevice.Attributes["sortDirection"] == "ASC" ? SortDirection.Ascending : SortDirection.Descending; IEnumerable<Device> source = _data.Tab_DeviceInfos.Select(info => new Device(info)); if (string.IsNullOrEmpty(sortExpression)) { GridViewDevice.DataSource = source; } else { if (sortDirection == SortDirection.Ascending) { GridViewDevice.DataSource = source.OrderBy(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); } else { GridViewDevice.DataSource = source.OrderByDescending(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); } } GridViewDevice.DataBind(); } protected void Page_Load(object sender, EventArgs e) { bindGridViewData(); } protected void GridViewDevice_Sorting(object sender, GridViewSortEventArgs e) { //保存sortExpression和sortDirection。 string sortExpression = e.SortExpression; string sortDirection = "ASC"; if (sortExpression.Equals(GridViewDevice.Attributes["sortExpression"]) && "ASC".Equals(GridViewDevice.Attributes["sortDirection"])) { sortDirection = "DESC"; } GridViewDevice.Attributes.Add("sortExpression", sortExpression); GridViewDevice.Attributes.Add("sortDirection", sortDirection); bindGridViewData(); } }
补充:
文章是昨天写完的,在IEnumerable<Device> source = _data.Tab_DeviceInfos.Select(info => new Device(info));这行代码上面,昨天用的是var source = _data.Tab_DeviceInfos.Select(info => new Device(info));。发布了以后,点了下运行,抛了异常,不能根据System.Object类型进行排序。不直接声明为IEnumber<T>的话,Select函数返回的是IQueryable<T>的实例,则会抛出上述的异常。解决方法的话,可以像我这样将其声明为IEnumberable<T>,或者先调用source.AsEnumerable(),又或者用反射获取其类型,然后进行强转。代码如下:
工具类SortUtility.cs:
public class SortUtility { public static object Sort(IEnumerable source, string sortExpression, SortDirection sortDirection) { IEnumerator sourceEnumerator = source.GetEnumerator(); if (!sourceEnumerator.MoveNext()) { return null; } Type dataSourceType = source.GetType(); Type dataItemType = typeof(object); if (dataSourceType.HasElementType) { dataItemType = dataSourceType.GetElementType(); } else if (dataSourceType.IsGenericType) { dataItemType = dataSourceType.GetGenericArguments().FirstOrDefault(); } else { if (sourceEnumerator.Current != null) { dataItemType = sourceEnumerator.Current.GetType(); } } // We'll handle things like LINQ to SQL differently by passing the love // on to the provider. PropertyInfo sortProperty = dataItemType.GetProperty(sortExpression); //找不到排序属性,返回原数据。 if (sortProperty == null) { return source; } Type sorterType = typeof(SortUtility<,>).MakeGenericType(dataItemType, sortProperty.PropertyType); object sorterObject = Activator.CreateInstance(sorterType); return sorterType.GetMethod("Sort", new Type[] { dataSourceType, typeof(string), typeof(SortDirection) }) .Invoke(sorterObject, new object[] { source, sortExpression, sortDirection }); } } public class SortUtility<T, ST> { public static IEnumerable<T> Sort(IEnumerable<T> source, string sortExpression, SortDirection sortDirection) { Func<T, ST> sortFunc = item => (ST)typeof(T).GetProperty(sortExpression).GetValue(item, null); if (sortDirection == SortDirection.Ascending) { return source.OrderBy<T, ST>(sortFunc); } else { return source.OrderByDescending<T, ST>(sortFunc); } } }
.aspx.cs中绑定数据部分的代码:
private void bindGridViewData() { if (!IsPostBack) { GridViewDevice.DataKeyNames = new string[] { "ID" }; } string sortExpression = GridViewDevice.Attributes["sortExpression"]; SortDirection sortDirection = GridViewDevice.Attributes["sortDirection"] == "ASC" ? SortDirection.Ascending : SortDirection.Descending; var source = _data.Tab_DeviceInfos.Select(info => new Device(info)); if (string.IsNullOrEmpty(sortExpression)) { GridViewDevice.DataSource = source; } else { /* if (sortDirection == SortDirection.Ascending) { GridViewDevice.DataSource = source.OrderBy(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); } else { GridViewDevice.DataSource = source.OrderByDescending(item => item.GetType().GetProperty(sortExpression).GetValue(item, null)); } */ GridViewDevice.DataSource = SortUtility.Sort(source, sortExpression, sortDirection); } GridViewDevice.DataBind(); }