Cómo desarrollar un filtro de búsqueda por texto en ASP.NET MVC

Toda aplicación Web basada en la recuperación de información desde una base de datos, requiere de al menos un 'sistema de búsqueda' que permita al usuario acceder a los contenidos en los que está interesado. Como complemento al Post Cómo desarrollar un sistema de paginación en ASP.NET MVC, en este caso veremos cómo desarrollar un 'filtro de búsqueda' por texto y cómo integrarlo a un formulario de consulta de registros con paginación en ASP.NET MVC.

filtro-de-busqueda

Para desarrollar este ejemplo, utilizaremos la estructura ya creada en el Post Cómo desarrollar un sistema de paginación en ASP.NET MVC, e iremos añadiendo al código las nuevas funcionalidades para integrar el 'filtro de búsqueda' por texto.

El modelo de datos

El modelo de datos sigue siendo el mismo (la tabla 'Customers' de la base de datos de pruebas Northwind). También utilizaremos Entity Framework como medio de acceso a la base de datos.

La Clase de paginación

Añadiremos a la 'clase de paginación' PaginadorGenerico<T>, una nueva propiedad public string BusquedaActual { get; set; } para poder mantener el estado de la búsqueda mientras nos movemos por las páginas de resultados.

    public class PaginadorGenerico<T> where T:class
    {
        public int PaginaActual { get; set; }
        public int RegistrosPorPagina { get; set; }
        public int TotalRegistros { get; set; }
        public int TotalPaginas { get; set; }
        public string BusquedaActual { get; set; }
        public IEnumerable<T> Resultado { get; set; }
    }

El Controlador

En la Acción por defecto Index del Controlador, añadiremos un nuevo parámetro de entrada string buscar donde recibiremos el 'texto de búsqueda' para filtrar los resultados Index(string buscar, int pagina = 1). También implementaremos el nuevo código para el 'filtro de búsqueda':

    public class CustomersController : Controller
    {
        private readonly int _RegistrosPorPagina = 10;

        private AppDbContext _DbContext;        
        private List<Customer> _Customers;
        private PaginadorGenerico<Customer> _PaginadorCustomers;

        // GET: Customers
        public ActionResult Index(string buscar, int pagina = 1)
        {
            int _TotalRegistros = 0;
            int _TotalPaginas = 0;

            // FILTRO DE BÚSQUEDA
            using (_DbContext = new AppDbContext())
            {
                // Recuperamos el 'DbSet' completo
                _Customers = _DbContext.Customers.ToList();

                // Filtramos el resultado por el 'texto de búqueda'
                if (!string.IsNullOrEmpty(buscar))
                {                    
                    foreach (var item in buscar.Split(new char[] { ' ' },
                             StringSplitOptions.RemoveEmptyEntries))
                    {
                        _Customers = _Customers.Where(x => x.ContactName.Contains(item) ||
                                                      x.CompanyName.Contains(item) ||
                                                      x.Email.Contains(item))
                                                      .ToList();
                    }
                }                
            }
            
            // SISTEMA DE PAGINACIÓN
            using (_DbContext = new AppDbContext())
            {
                // Número total de registros de la tabla Customers
                _TotalRegistros = _Customers.Count();
                // Obtenemos la 'página de registros' de la tabla Customers
                _Customers = _Customers.OrderBy(x => x.ContactName)
                                                 .Skip((pagina - 1) * _RegistrosPorPagina)
                                                 .Take(_RegistrosPorPagina)
                                                 .ToList();
                // Número total de páginas de la tabla Customers
                _TotalPaginas = (int)Math.Ceiling((double)_TotalRegistros / _RegistrosPorPagina);

                // Instanciamos la 'Clase de paginación' y asignamos los nuevos valores
                _PaginadorCustomers = new PaginadorGenerico<Customer>()
                {
                    RegistrosPorPagina = _RegistrosPorPagina,
                    TotalRegistros = _TotalRegistros,
                    TotalPaginas = _TotalPaginas,
                    PaginaActual = pagina,
                    BusquedaActual = buscar,
                    Resultado = _Customers
                };
            }
            
            // Enviamos a la Vista la 'Clase de paginación'
            return View(_PaginadorCustomers);
        }
     }

La Vista

En la Vista Index.cshtml añadiremos el código para el 'filtro de búsqueda' (caja de texto y botón), y actualizamos el 'paginador de registros' para que envíe al a Controlador el 'texto de búsqueda' como parámetro buscar = Model.BusquedaActual.

@model PaginadorGenerico<Customer>
@{
    ViewBag.Title = "Customers";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@*CÓDIGO PARA EL FILTRO DE BÚSQUEDA*@
<div class="text-right form-inline">
    <form method="get" action=@Url.Action("Index", "Customers" )>
        <div class="form-group">
            @Html.TextBox("buscar", null, new { placeholder = "texto de búsqueda",
                                    @class = "form-control" })
        </div>
        <button class="btn btn-default" type="submit">Buscar</button>
    </form>
</div>

<br />
@*CÓDIGO PARA LA TABLA DE DATOS*@
<table class="table table-bordered">
    <thead>
        <tr>
            <th>
                Contact Name
            </th>
            <th>
                Company Name
            </th>
            <th>
                Email
            </th>
        </tr>
    </thead>
    @foreach (var item in Model.Resultado)
    {
        <tbody>
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.ContactName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.CompanyName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Email)
                </td>
            </tr>
        </tbody>
    }
</table>

@*CÓDIGO PARA EL PAGINADOR DE REGISTROS*@
@if (Model.Resultado.Count() > 0)
{
    <span>
        <strong>@Model.TotalRegistros</strong> registros encontrados
    </span>
    <span>&nbsp;|&nbsp;</span>
    <span>
        Página <strong>@(Model.PaginaActual)</strong> de
        <strong>@Model.TotalPaginas</strong>
    </span>
    <span>&nbsp;|&nbsp;</span>
}
else
{
    <span>No hay resultados para esta búsqueda</span>
    <span>&nbsp;|&nbsp;</span>
}

@if (Model.PaginaActual > 1)
{
    @Html.ActionLink("<<", "Index", new { pagina = 1, buscar = Model.BusquedaActual },
                        new { @class = "btn btn-sm btn-default" })
    <span></span>
    @Html.ActionLink("Anterior", "Index", new { pagina = Model.PaginaActual - 1,
                                                buscar = Model.BusquedaActual },
                                          new { @class = "btn btn-sm btn-default" })
}
else
{
    @Html.ActionLink("<<", "Index", new { pagina = 1,
                                          buscar = Model.BusquedaActual },
                                    new { @class = "btn btn-sm btn-default disabled" })
    <span></span>
    @Html.ActionLink("Anterior", "Index", new { pagina = 1,
                                                buscar = Model.BusquedaActual },
                                          new { @class = "btn btn-sm btn-default disabled" })
}

<span></span>

@if (Model.PaginaActual < Model.TotalPaginas)
{
    @Html.ActionLink("Siguiente", "Index", new { pagina = Model.PaginaActual + 1,
                                                 buscar = Model.BusquedaActual },
                                           new { @class = "btn btn-sm btn-default" })
    <span></span>
    @Html.ActionLink(">>", "Index", new { pagina = Model.TotalPaginas,
                                          buscar = Model.BusquedaActual },
                                    new { @class = "btn btn-sm btn-default" })
}
else
{
    @Html.ActionLink("Siguiente", "Index", new { pagina = Model.TotalPaginas - 1,
                                                 buscar = Model.BusquedaActual },
                                           new { @class = "btn btn-sm btn-default disabled" })
    <span></span>
    @Html.ActionLink(">>", "Index", new { pagina = Model.TotalPaginas,
                                          buscar = Model.BusquedaActual },
                                    new { @class = "btn btn-sm btn-default disabled" })
}
   EtiquetasASP.NET .NET MVC C# Entity Framework

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

Enviando ...

  Comentarios

jhonnatan zevallos jhonnatan zevallos

Buenas, primero quiero felicitarte por tu blog, me ha ayudado mucho. Luego quiero hacerte una consulta respecto de los filtros de busqueda. Como podria hacer en el caso que quisiera distinguir el parametro "buscar" que utilizas para que distinga entre mayusculas y mnusculas. Ya que en el proyecto de practica que estoy trabajando, la busqueda solo funciona en el caso que escriba tal cual figure el parametro en la base de datos. Espero ser claro en mi consulta.

Muchas gracias.
Rafael Acosta Administrador Rafael Acosta

@jhonnatan zevallos:
En principio creo entender por lo que comentas, que el texto de búsqueda que contiene la variable 'buscar=' no sea sensible a mayúsculas y minúsculas, o sea, 'buscar=ASP.NET MVC' y 'buscar=Asp.Net mvc' devuelvan los mismos resultados.
Este comportamiento a la hora de realizar búsquedas en una base de datos, no depende de Entity Framework y Linq, sino que es la propia base de datos la que gestiona y devuelve los resultados "case sensitive" (distingue mayúsculas y minúsculas) en función de su configuración.
Por ejemplo, en SQL Server deberías definir las columnas de búsqueda de tus tablas de la siguiente manera:
campo1 varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS, donde 'CS' indica 'case sensitive'.
Te recomiendo busques más información en Internet acerca de esta funcionalidad en función de la BD que utilices.

Waldo Waldo

Hola, muchas gracias por tu post, me ha ayudado bastante, sin embargo quiero hacer mas filtros de busquedas pero no entiendo como se le podria hacer amigo :o si puedes ayudarme te lo agradeceria :D
Rafael Acosta Administrador Rafael Acosta

@Waldo: Gracias por tu comentario.


Creo entender por tu pregunta, que lo que quieres es hacer es integrar más de un filtro de búsqueda en los resultados devueltos por la Acción Index(), pero si te das cuenta, esto ya se está haciendo en el ejemplo. El texto de búsqueda que introduces, se está comprobando en los tres campos del Modelo Customers (ContactName, CompanyName y Email):


_Customers = _Customers.Where(x => x.ContactName.Contains(item) ||
                              x.CompanyName.Contains(item) ||
                              x.Email.Contains(item)).ToList();


Esto quiere decir que se está buscando en todos los campos de la tabla. Aun así, si lo que quieres es tener un filtro de búsqueda independiente por cada campo de la tabla, solo debes añadir tantas propiedades de búsqueda más a la clase PaginadorGenerico<T> (ahora solo está public string BusquedaActual { get; set; }), y tantos parámetros más a la Acción Index (ahora solo está string buscar). Por supuesto también debes incluir estos nuevos filtros en el paginador de la Vista, al al realizar las peticiones al Controlador.


empresa apps valencia empresa apps valencia

Me gusta disfrutar y visitar blogs, aprecio mucho el contenido, el trabajo y el tiempo que ponéis en vuestros artículos. Buscando en en la red he encontrado tu web. Ya he disfrutado de varios artículos, pero este es muy interesante, es unos de mis temas predilectos, y por su calidad he disfrutado mucho. He puesto tu web en mis favoritos pues creo que todos tus publicaciones son interesantes y seguro que voy a pasar muy buenos ratos leyendolos.
[url=https://www.granota.eu/desarrollo-aplicaciones-moviles-valencia-madrid-tarragona.html]empresa apps valencia[/url]
Hector Hector

Hola Excelente post. Por favor si el campo de busque es una fecha()datetime o un numero(inte) u otro tipo como se procede. Gracias por su respuesta.

Rafael Acosta Administrador Rafael Acosta

@Hector:


Pues exactamente Igual que en el ejemplo.


Ten en cuenta que todo lo que va desde la Vista al Controlador es texto, y en este caso va en el querystring ya que se hace una petición Http GET.


Lo único que debes tener en cuenta es hacer la transformación al tipo correspondiente (DateTime o Int) del parámetro string buscar, una vez llegue al Controlador.


Alberto Alberto

Buenos días:

Lo primero, felicitarte por tu blog, completo y fácil de leer. Una pregunta, en Core 3.1 las llamadas a las vistas son asíncronas:
return View(await _context.Tabla.ToListAsync());

Pero en el ejemplo no utiliza esta forma de trabajar. ¿sería fácil adaptarlo o no vale la pena?
// Enviamos a la Vista la 'Clase de paginación'
return View(_PaginadorCustomers);

Gracias de antemano.
Jeanpierre Jeanpierre

Hola Rafael estoy realizando una busqueda por keypress cual seria la mejor opción para no ir a cada momento a la BD, cual es la buena práctica.

Como es un punto de venta el usuario solicita que ni bien vaya escribiendo se pueda ir filtrando los productos.

Perfil para Rafael Acosta en Stack Overflow en español, preguntas y respuestas para programadores y profesionales de la informática.

  Etiquetas

.NET Core .NET Framework .NET MVC .NET Standard AJAX ASP.NET ASP.NET Core ASP.NET MVC Bootstrap Buenas prácticas C# Cookies Entity Framework Gráficos JavaScript jQuery JSON JWT PDF Pruebas Unitarias Seguridad SEO SOAP Sql Server SqLite Swagger Validación Web API Web Forms Web Services WYSIWYG

  Nuevos


  Populares















Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto