JQGrid LINQ и анонимные типы - PullRequest
       3

JQGrid LINQ и анонимные типы

2 голосов
/ 30 августа 2011

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<T> where T : IGridParameterListable).
Дело в том, что я предпочел бы иметь универсальный метод, который просто вслепую преобразует значения свойств объектов в List<string> ..

Звучит не слишком мило, я знаю, я открыт для других идей.
Я хочу максимально избежать повторения структуры данных для сетки как на стороне клиента, так и на стороне сервера.

Ответы [ 3 ]

4 голосов
/ 30 августа 2011

Это не ответ, просто некоторый исходный код

В случае, если кому-то понадобится моя реализация jqGrid для ASP.NET WebForms, в свете того, что это довольно сложно реализовать, я опубликую код здесь.

Во-первых, некоторые классы 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; }
}

Код переднего плана, я изменил некоторые из них, так что он позволяет мне публиковать в сервисе только с одним параметром, заполненным информацией Grid. Кроме того, мне не нужны отдельные фильтры, поэтому я просто поддерживаю режим нескольких фильтров (что, на мой взгляд, строго лучше).

<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);
}

Тогда для фактического обслуживания нам понадобятся следующие фрагменты:

[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;
    }
}

Как видите, я просто извлекаю данные, выбираю столбцы, которые я хочу отобразить (которые должны быть такими же и в том же порядке, что и в сетке на стороне клиента), и это почти все. Идея в том, чтобы это можно было использовать как можно больше.

1 голос
/ 11 января 2012

Нико, это лучшая реализация jqgrid с веб-формами asp.net, которую я видел.Я сделал небольшое, но важное улучшение, добавив третий параметр в метод SerializeQuery: список реальных идентификаторов строк, чтобы мы могли добавить их к результату.Без этого списка идентификатор строки, который отправляется клиенту, на самом деле является просто индексом элемента в списке с данными строки, а не фактическим идентификатором из строки базы данных.Это не применимо в сценарии, где вам нужно включить редактирование и удаление в 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);
    }
0 голосов
/ 30 августа 2011

Это может быть лучшим вариантом.

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;
    }

Чуть лучше, я думаю. Любые другие идеи, чтобы далее расширить это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...