DataTables Totalmente no Server Side

Já abordei anteriormente o uso de DataTables com ASP.NET MVC aqui, mas agora vamos mostrar como utilizar o componente com todas as requisições sendo tratadas no servidor – paginação, busca e ordenação!

Parte fácil: Estruturar o HTML:

<table class="table table-hover" id="tblPessoas">
    <thead>
        <tr>
            <th>Nome Completo</th>
            <th>Data de Aniversário</th>
            <th>Emprego</th>
            <th>Salário</th>
        </tr>
    </thead>
    <tbody>

    </tbody>
</table>

Depois temos que preparar o Javascript:

$(function () {

    $("#tblPessoas").dataTable({
        "bServerSide": true,
        "sAjaxSource": "Home/BuscaAssincrona",
        "bProcessing": true,
        "aoColumns":
            [
                {
                    "sName": "Nome",
                    "mData": "Nome"
                },
                {
                    "sName": "DtAniversario",
                    "mData": "DtAniversario",
                    "mRender" : function(val, type)
                    {
                        var bDay = new Date(parseInt(val.substr(6)));
                        return Globalize.format(bDay, "dd/MM/yyyy");
                    }
                },
                {
                    "sName": "Emprego.Nome",
                    "mData": "EmpregoNome"
                },
                {
                    "sName": "Emprego.Salario",
                    "mData": "EmpregoSalario",
                    "mRender": function(val)
                    {
                        return Globalize.format(val, "C");
                    }
                }
            ]
    });

})

Agora entra a parte ferrenha: o server side.

Como primeiro passo, precisamos de uma ViewModel que contemple os campos que o datatables envia ao servidor:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Dona.DataTablesServerSide.ViewModel.DataTables
{
    /// <summary>
    /// Class that encapsulates most common parameters sent by DataTables plugin
    /// </summary>
    public class JQueryDataTablesParamViewModel
    {
        /// <summary>
        /// Request sequence number sent by DataTable,
        /// same value must be returned in response
        /// </summary>
        public string sEcho { get; set; }

        /// <summary>
        /// Text used for filtering
        /// </summary>
        public string sSearch { get; set; }

        /// <summary>
        /// Number of records that should be shown in table
        /// </summary>
        public int iDisplayLength { get; set; }

        /// <summary>
        /// First record that should be shown(used for paging)
        /// </summary>
        public int iDisplayStart { get; set; }

        /// <summary>
        /// Number of columns in table
        /// </summary>
        public int iColumns { get; set; }

        /// <summary>
        /// Number of columns that are used in sorting
        /// </summary>
        public int iSortingCols { get; set; }

        /// <summary>
        /// Comma separated list of column names
        /// </summary>
        public string sColumns { get; set; }

        /// <summary>
        /// Which column is ordering the result
        /// </summary>
        public int iSortCol_0 { get; set; }

        /// <summary>
        /// Is the sort ASC or DESC?
        /// </summary>
        public string sSortDir_0 { get; set; }
    }
}

Basta colocar isso num controller e repassar ao seu mecanismo de serviço (ou repositório, ou qualquer outro pattern que faça a busca com o ORM):

public JsonResult BuscaAssincrona(JQueryDataTablesParamViewModel Params)
        {
            //para informar ao datatables quantos registros existem AO TOTAL
            int TotalPessoas = 0;

            //para informar ao datatables quantos registros existem com o filtro aplicado
            int TotalPessoasFiltradas = 0;

            List<ListarPessoaViewModel> Pessoas = _servico.PegarPessoas(Params, out TotalPessoas, out TotalPessoasFiltradas);

            var Resultado = new
            {
                sEcho = Params.sEcho,
                iTotalRecords = TotalPessoas,
                iTotalDisplayRecords = TotalPessoasFiltradas,
                aaData = Pessoas
            };

            return Json(Resultado, JsonRequestBehavior.AllowGet);
        }

É importante repassar sempre essas informações do resultado, para que o datatables, do lado cliente, consiga informar o usuário quantos registros existem ao total (iTotalRecords), quantos registrem dentro do filtro aplicado (iTotalDisplayRecords) e o JSON com as informações em si (no caso, “Pessoas”).

Para fazer a filtragem, eu utilizei o EntityFramework junto com a extensão de Query Dinâmica, para facilitar a ordenação (É o “System.Linq.Dynamic” e você pode ver mais sobre ele aqui e aqui).

De resto, eu optei por aplicar o filtro manualmente em todas as colunas exibidas:

        public List<ListarPessoaViewModel> PegarPessoas(JQueryDataTablesParamViewModel Params, out int TotalPessoas, out int TotalPessoasFiltradas)
        {
            //apenas pegando a referência aos dados que vamos lidar (não executa query nenhuma ainda)
            IQueryable<Pessoa> Pessoas = _db.Pessoas.AsQueryable();

            //se houver um filtro, vamos aplicá-lo (MAS AINDA SEM EXECUTAR A CONSULTA NO BANCO!)
            if(!string.IsNullOrWhiteSpace(Params.sSearch))
            {
                string search = Params.sSearch;

                bool isDate = false;
                DateTime dateValue = DateTime.Now;

                bool isDecimal = false;
                decimal decimalValue = 0;

                //é uma data?
                isDate = DateTime.TryParse(search, out dateValue);

                //é valor decimal?
                isDecimal = decimal.TryParse(search, out decimalValue);

                //aplique o filtro em todas as colunas visíveis da tabela
                //eu optei por filtrar as colunas decimais e de data apenas quando o usuário inserir um valor compatível
                Pessoas = Pessoas.Where(x =>
                    x.Nome.Contains(search)
                    ||
                    x.Emprego.Nome.Contains(search)
                    ||
                    (isDate && x.DtAniversario.Equals(dateValue))
                    ||
                    (isDecimal && x.Emprego.Salario.Equals(decimalValue))
                );
            }

            //fazemos um acesso ao banco para dizer quantas pessoas existem no DB
            TotalPessoas = _db.Pessoas.Count();

            //mais um acesso para dizer quantas pessoas existem com os filtros aplicados
            TotalPessoasFiltradas = Pessoas.Count();

            //fazendo a ordenação pelo que foi informado na interface
            string ColunaOrdenada = Params.sColumns.Split(',')[Params.iSortCol_0];
            Pessoas = Pessoas.OrderBy(ColunaOrdenada + " " + Params.sSortDir_0);

            //pegando apenas os resultados relativos a página atual
            Pessoas = Pessoas.Skip(Params.iDisplayStart).Take(Params.iDisplayLength);

            //fazendo a query e retornando a viewmodel...
            return Pessoas.Select(x => new ListarPessoaViewModel()
            {
                Id = x.Id,
                Nome = x.Nome,
                DtAniversario = x.DtAniversario,
                EmpregoId = x.EmpregoId,
                EmpregoNome = x.Emprego.Nome,
                EmpregoSalario = x.Emprego.Salario,
            }).ToList();
        }

Basicamente é isso pessoal. É algo extritamente técnico, mas que se você já tem alguma afinidade com o datatables, ASP.NET MVC e Entity Framework, você consegue se adequar e talvez fazer soluções ainda melhores que essa.

Deixo o link do projeto no GitHub para que vocês possam ver o código fonte completo.

Qualquer problema, dúvidas ou sugestões, deixem um comentário!

Até a próxima!

Anúncios

Um pensamento sobre “DataTables Totalmente no Server Side

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s