【问题标题】:jqGrid LINQ and anonymous typesjqGrid LINQ 和匿名类型
【发布时间】:2011-11-07 02:34:58
【问题描述】:

jqGrid 采用以下 JSON 格式:

{ 
  "total": "5", 
  "page": "2", 
  "records": "55",
  "rows" : [
    {"id" :"21", "cell" :["cell11", "cell12", "cell13"]},
    {"id" :"22", "cell" :["cell21", "cell22", "cell23"]},
      ...
    {"id" :"30", "cell" :["cell31", "cell32", "cell33"]},
  ]
}

我正在尝试使方法尽可能可重用,以通过 AJAX 将数据传回 jqGrid。

var result = new
{
    total = (int) Math.Ceiling((double) totalCount/PageSize),
    page = PageIndex,
    records = totalCount,
    rows = data.Select((d, id) => new {id, cell = d.SerializeGridParameters()}).ToArray()
};

如您所见,目前我设法轻松添加了索引,但我在处理字段数据时遇到了问题。

到目前为止,我设法通过使用接口来处理它:

public interface IGridParameterListable
{
    List<string> SerializeGridParameters();
}

对于我的数据(这是一个IEnumerable&lt;T&gt; where T : IGridParameterListable)。
事情是我宁愿有一个通用方法,只是盲目地将对象属性值转换为List&lt;string&gt;..

听起来不太可爱,我知道,我对其他想法持开放态度。
我想尽可能避免在客户端和服务器端重复网格的数据结构。

【问题讨论】:

  • 也许最好的方法是在 AJAX 请求中传递字段名称,并使用反射在每一行中返回这些字段?

标签: c# asp.net ajax jqgrid webforms


【解决方案1】:

Nico,这是我见过的带有 asp.net webforms 的 jqgrid 的最佳实现。通过向 SerializeQuery 方法添加第三个参数,我做了一个小而重要的改进:实际行 id 的列表,以便我们可以将这些添加到结果中。如果没有该列表,发送到客户端的行 ID 实际上只是列表中包含行数据的项目的索引,而不是数据库行中的实际 ID。这在需要在 jqgrid 中启用编辑和删除的场景中不可用。

public string SerializeQuery<T>(IQueryable<T> query, Func<T, List<string>> select, Func<T, List<int>> ids)
    {
        //filtering
        if (IsSearch && Where.rules != null)
        {
            if (Where.groupOp == "AND") // TODO: INSENSITIVE EQUALS, Y un enum GridGroupOperation.And.Name()
            {
                foreach (var rule in Where.rules)
                    query = query.Where(rule.field, rule.data, rule.op);
            }
            else if (Where.groupOp == "OR") // TODO: INSENSITIVE EQUALS, Y un enum GridGroupOperation.Or.Name()
            {
                var temp = (new List<T>()).AsQueryable();

                foreach (var rule in Where.rules)
                {
                    var t = query.Where(rule.field, rule.data, rule.op);
                    temp = temp.Concat(t);
                }

                //remove repeat records
                query = temp.Distinct();
            }
        }

        //sorting
        query = query.OrderBy(SortColumn, SortOrder);

        //count
        var totalCount = query.Count();

        //paging
        var data = query.Skip((PageIndex - 1) * PageSize).Take(PageSize);

        //convert to grid format
        var result = new
        {
            total = (int)Math.Ceiling((double)totalCount / PageSize),
            page = PageIndex,
            records = totalCount,
            rows = data.Select((d) => new { 
                id = ids(d),
                cell = select(d) 
            }).ToArray()
        };

        return JsonConvert.SerializeObject(result);
    }

【讨论】:

    【解决方案2】:

    这不是答案,只是一些源代码

    如果有人想要我为 ASP.NET WebForms 实现 jqGrid,鉴于它很难实现,我将在此处发布代码。

    首先,一些 JSON 类:

    public class GridFilter
    {
        public string groupOp { get; set; }
        public GridRule[] rules { get; set; }
    }
    
    public class GridRule
    {
        public string field { get; set; }
        public string op { get; set; }
        public string data { get; set; }
    }
    
    public class GridSettings
    {
        public bool IsSearch { get; set; }
        public int PageSize { get; set; }
        public int PageIndex { get; set; }
        public string SortColumn { get; set; }
        public string SortOrder { get; set; }
    
        public GridFilter Where { get; set; }
    }
    

    前端代码,我改变了一些,所以它允许我只用一个参数发布到服务,填充网格信息。另外,我不太在意单个过滤器,所以我只支持多个过滤器模式(无论如何我认为这更好)。

    <table id="UsersGrid"></table>
    <div id="UsersGridPager"></div>
    
    <script type="text/javascript">
        $(document).ready(function () {
            $('#UsersGrid').jqGrid({
                colNames: ['ID', 'Online', 'Computer', 'IP', 'User'],
                colModel: [
                    { name: 'ID', width: 100, index: 'ID', searchoptions: { sopt: ['eq', 'ne']} },
                    { name: 'IsOnline', width: 100, index: 'IsOnline', searchoptions: { sopt: ['eq', 'ne']} },
                    { name: 'Name', index: 'Name', searchoptions: { sopt: ['eq', 'ne', 'cn']} },
                    { name: 'IP', index: 'IP', searchoptions: { sopt: ['eq', 'ne', 'cn']} },
                    { name: 'User', index: 'User', searchoptions: { sopt: ['eq', 'ne', 'cn']} }
                  ],
                jsonReader: {
                    root: function (json) { return JSON.parse(json.d).rows; },
                    page: function (json) { return JSON.parse(json.d).page; },
                    total: function (json) { return JSON.parse(json.d).total; },
                    records: function (json) { return JSON.parse(json.d).records; }
                },
                serializeGridData: jqGridSettings,
                caption: "Usuarios",
                emptyrecords: "No se encontraron usuarios",
                url: "/GridTest/GridTestService.asmx/GetData",
                ajaxGridOptions: { contentType: 'application/json; charset=utf-8' },
                datatype: 'json',
                mtype: 'POST',
                height: 250,
                rowNum: 10,
                rowList: [10, 25, 50],
                rownumbers: true,
                autowidth: true,
                pager: "#UsersGridPager"
            }).navGrid("#UsersGridPager",
                {
                    refresh: true,
                    add: false,
                    edit: false,
                    del: false,
                    search: true
                },
                {},
                {},
                {},
                {
                    sopt: ["eq", "ne", "cn"],
                    multipleSearch: true,
                    showQuery: true
                }
            );
    
            function jqGridSettings(p) {
                var settings = {
                    grid: {
                        PageIndex: p.page,
                        PageSize: p.rows,
                        IsSearch: p._search,
                        SortColumn: p.sidx,
                        SortOrder: p.sord,
                        Where: jqGridFilters(p.filters)
                    }
                };
    
                return JSON.stringify(settings);
            }
    
            function jqGridFilters(json) {
                var filters = {};
                if (!json) {
                    return;
                }
                if (!json.length) {
                    return;
                }
                var parsed = JSON.parse(json);
                if (!!parsed.rules) {
                    filters = parsed;
                }
    
                return filters;
            }
        });
    

    现在,对于实际实现...首先,我们将需要几个 LINQ 扩展方法,用于对数据进行排序和排序。它们如下:

    public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string sortColumn, string direction)
    {
        if (string.IsNullOrEmpty(sortColumn))
            return query;
    
        string methodName = string.Format("OrderBy{0}",
            direction.ToLower() == "asc" ? "" : "descending");
    
        ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
    
        MemberExpression memberAccess = null;
        foreach (var property in sortColumn.Split('.'))
            memberAccess = MemberExpression.Property(memberAccess ?? (parameter as Expression), property);
    
        LambdaExpression orderByLambda = Expression.Lambda(memberAccess, parameter);
    
        MethodCallExpression result = Expression.Call(
                  typeof(Queryable),
                  methodName,
                  new[] { query.ElementType, memberAccess.Type },
                  query.Expression,
                  Expression.Quote(orderByLambda));
    
        return query.Provider.CreateQuery<T>(result);
    }
    
    public static IQueryable<T> Where<T>(this IQueryable<T> query, string column, object value, string operation)
    {
        if (string.IsNullOrEmpty(column))
            return query;
    
        ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
    
        MemberExpression memberAccess = null;
        foreach (var property in column.Split('.'))
            memberAccess = MemberExpression.Property
               (memberAccess ?? (parameter as Expression), property);
    
        //change param value type
        //necessary to getting bool from string
        ConstantExpression filter = Expression.Constant
            (
                Convert.ChangeType(value, memberAccess.Type)
            );
    
        //switch operation
        LambdaExpression lambda = null;
        switch (operation)
        {
            case "eq": // equal
                {
                    lambda = Expression.Lambda(Expression.Equal(memberAccess, filter), parameter);
                    break;
                }
            case "ne": // not equal
                {
                    lambda = Expression.Lambda(Expression.NotEqual(memberAccess, filter), parameter);
                    break;
                }
            case "cn": // contains
                {
                    Expression condition = Expression.Call(memberAccess,
                                                           typeof (string).GetMethod("Contains"),
                                                           Expression.Constant(value.ToString()));
                    lambda = Expression.Lambda(condition, parameter);
                    break;
                }
        }
    
        var result = Expression.Call(
               typeof(Queryable), "Where",
               new[] { query.ElementType },
               query.Expression,
               lambda);
    
        return query.Provider.CreateQuery<T>(result);
    }
    

    搜索的实际实现,作为一个偏好问题,我将它放在我的 GridSettings 类中,作为成员方法,但它也可以是 IQueryable 的扩展方法。

    public string SerializeQuery<T>(IQueryable<T> query, Func<T, List<string>> select)
    {
        //filtering
        if (IsSearch && Where.rules != null)
        {
            if (Where.groupOp == "AND") // TODO: INSENSITIVE EQUALS, Y un enum GridGroupOperation.And.Name()
            {
                foreach (var rule in Where.rules)
                    query = query.Where(rule.field, rule.data, rule.op);
            }
            else if (Where.groupOp == "OR") // TODO: INSENSITIVE EQUALS, Y un enum GridGroupOperation.Or.Name()
            {
                var temp = (new List<T>()).AsQueryable();
    
                foreach (var rule in Where.rules)
                {
                    var t = query.Where(rule.field, rule.data, rule.op);
                    temp = temp.Concat(t);
                }
    
                //remove repeat records
                query = temp.Distinct();
            }
        }
    
        //sorting
        query = query.OrderBy(SortColumn, SortOrder);
    
        //count
        var totalCount = query.Count();
    
        //paging
        var data = query.Skip((PageIndex - 1) * PageSize).Take(PageSize);
    
        //convert to grid format
        var result = new
        {
            total = (int)Math.Ceiling((double)totalCount / PageSize),
            page = PageIndex,
            records = totalCount,
            rows = data.Select((d, id) => new { id, cell = select(d) }).ToArray()
        };
    
        return JsonConvert.SerializeObject(result);
    }
    

    然后,对于实际的服务,我们将需要像这样的 sn-ps:

    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [ToolboxItem(false)]
    [ScriptService]
    public class GridTestService : System.Web.Services.WebService
    {
        [WebMethod]
        [ScriptMethod]
        public string GetData(GridSettings grid)
        {
            var query = new FakeComputersRepository().Computers();
            var response = grid.SerializeQuery(query, d => new List<string>
            {
                d.ID.ToString(),
                d.IsOnline.ToString(),
                d.Name,
                d.IP,
                d.User
            });
    
            return response;
        }
    }
    

    如您所见,我只是获取数据,选择要显示的列(必须与客户端网格中的列相同且顺序相同),仅此而已。我们的想法是尽可能地重复使用。

    【讨论】:

    • 您好,尼科先生。这是一个很好的解决方案,正是我想要的!你能在 asp.net MVC 中为this problem 制定通用解决方案吗?
    【解决方案3】:

    这可能是一个更好的选择。

    public string SerializeQuery<T>(IQueryable<T> query, Func<T, List<string>> select)
    {
        // stuff ...
    
        var result = new
        {
            total = (int)Math.Ceiling((double)totalCount / PageSize),
            page = PageIndex,
            records = totalCount,
            rows = data.Select((d, id) => new { id, cell = select(d) }).ToArray()
        };
    
        // stuff ...
    }
    

    我消除了对接口的需求,并将转换转移到每个特定网格的查询实现中。在这个例子中:

    [WebMethod]
    [ScriptMethod]
        public string GetData(GridSettings grid)
        {
            var query = new FakeComputersRepository().Computers();
            var response = grid.SerializeQuery(query, d => new List<string>
            {
                d.ID.ToString(),
                d.IsOnline.ToString(),
                d.Name,
                d.IP,
                d.User
            });
    
            return response;
        }
    

    我觉得好一点。 还有其他想法,可以进一步扩展吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多