domingo, 3 de noviembre de 2013

[ASP.NET MVC] WebGrid Filtrado por DropDownList

 

Introducción


En el articulo aprenderemos :

  • como capturar la selección de un DropDownList
  • filtrar una query usando Entity Framework
  • desplegar los datos en un WebGrid

La idea es listas en un dropdownlist los clientes (customers) y al seleccionar alguno, listar las ordenes de compra realizadas.

image

 

Modelo de Dominio


Empezaremos analizando el dominio de entidades que usaremos en el ejemplo.

image

La entidad Orden tiene relación con el cliente (customer) y un empleado (employee), usaremos el primero para filtrar mientras que el segundo será información adicional que se mostrara en una columna del grid

 

Definición de la persistencia


El acceso a los datos lo realizaremos con al ayuda de Entity Framework, por lo que deberemos definir el DbContext y las clases de mapping para cada entidad

 

public class NorthWindContext : DbContext
{

    public NorthWindContext()
        : base("NorthwindDb")
    {
        Database.SetInitializer<NorthWindContext>(null);

        this.Configuration.LazyLoadingEnabled = false;
        this.Configuration.ProxyCreationEnabled = false;
    }

    public DbSet<Customer> Employees { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new CustomerMap());
        modelBuilder.Configurations.Add(new EmployeeMap());
        modelBuilder.Configurations.Add(new OrderMap());

        modelBuilder.Configurations.Add(new LocationComplexMap());

        base.OnModelCreating(modelBuilder);
    }

}


public class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        HasKey(x => x.CustomerID);

        Property(x => x.CustomerID)
                .HasColumnType("nchar")
                .HasMaxLength(5);


        Property(x => x.CompanyName)
                .HasMaxLength(40)
                .IsRequired();

        Property(x => x.ContactName)
                .HasMaxLength(30);

        Property(x => x.ContactTitle)
                .HasMaxLength(30);

    }
}

public class EmployeeMap : EntityTypeConfiguration<Employee>
{
    public EmployeeMap()
    {
        HasKey(x => x.EmployeeID);

        Property(x => x.LastName)
                .HasMaxLength(20)
                .IsRequired(); ;

        Property(x => x.FirstName)
                .HasMaxLength(10)
                .IsRequired();

        Ignore(x => x.FullName);

    }
}

public class OrderMap : EntityTypeConfiguration<Order>
{
    public OrderMap()
    {
        HasKey(x => x.OrderID);

        Property(x => x.CustomerID)
            .HasColumnType("nchar")
            .HasMaxLength(5)
            .IsOptional();

        HasOptional(x => x.Customer)
                .WithMany()
                .HasForeignKey(x => x.CustomerID);

        HasOptional(x => x.Employee)
                .WithMany()
                .HasForeignKey(x => x.EmployeeID);

        Property(x => x.ShipName)
                .HasMaxLength(40);

        Property(x => x.ShipAddress)
                .HasMaxLength(60);

    }
}


public class LocationComplexMap : EntityTypeConfiguration<Localization>
{
    public LocationComplexMap()
    {
        Property(x => x.Address).HasColumnName("Address").HasMaxLength(60);
        Property(x => x.City).HasColumnName("City").HasMaxLength(15);
        Property(x => x.Region).HasColumnName("Region").HasMaxLength(15);
        Property(x => x.PostalCode).HasColumnName("PostalCode").HasMaxLength(10);
        Property(x => x.Country).HasColumnName("Country").HasMaxLength(15);
    }
}

 

En este caso estamos utilizando una db existente por lo tanto importante definir la línea:

Database.SetInitializer<NorthWindContext>(null);

de esta forma indicamos que no se debe crear la db cuando ejecutemos la aplicacion

 

Definir datos del dropdownlist


Para la vista definiremos un modelo que nos permita especificar los datos que mostraremos al usuario.

public class CustomerOrdersModel
{

    public SelectList CustomerList { get; set; }

    public List<Order> OrderList { get; set; }

}

 

Es por eso que encontraremos dos listas, una para cargar los ítems del combo y la segunda para las ordenes del grid.

Al ingresar al action Index(), el cual responde a los pedidos GET, del controlador debemos cargar la lista del combo, nos ayudaremos con el repositorio para esta tarea.

public ActionResult Index()
{

    CustomerOrdersModel model = new CustomerOrdersModel()
    {
        CustomerList = new SelectList(repository.GetAllWithoutSelection(), "CustomerID", "CompanyName")
    };

    return View(model);
}

 

la vista hará uso de las propiedades del modelo para cargar el dropdownlist

SNAGHTML978badb8

 

Capturar selección dropdownlist


La captura de la selección puede realizarse de varias formas, pero no debe perderse de vista que es la propiedad “name” de los controles html los que participan en el post que se realiza al servidor, no hay que confundirse con la propiedad “id” esta solo es útil para manipular el control en el cliente usando jquery.

Para capturar la acción del usuario al cambiar la selección en el dropdownlist nos ayudamos de jquery, para poder definir el selector definimos un id en el tag <form>

SNAGHTML97918cef

Es en ese momento cuando forzamos un submit para invocar a un action concreto

image

en el controlador encontraremos

[HttpPost]
public ActionResult Index(string CustomerList)
{
    //
    // usamos el metodo del repositorio el cual nos permite recuperar las odenes de compra del cliente
    // se incluye en esta la informacion del cliente y el empleaod que realizo la venta
    //
    var orders = orderRepository.Filter(x => x.CustomerID == CustomerList,
                                            new List<Expression<Func<Entities.Order,object>>>() { x=>x.Employee, x=>x.Customer });

    CustomerOrdersModel model = new CustomerOrdersModel()
    {
        CustomerList = new SelectList(repository.GetAllWithoutSelection(), "CustomerID", "CompanyName"),
        OrderList = orders
    };


    return View(model);
}

 

El nombre del parámetro debe coincidir con la propiedad “name” del control para poder tomar el dato, en este caso hemos dejado el valor por defecto que crear asp.net mvc, pero podríamos redefinirlo.

SNAGHTML97af81c9

Si en la vista cambiamos la propiedad “name” debemos también hacerlo en el parámetro del action.

SNAGHTML97a1f0d0

 

 

Definición del WebGrid


El post al action devolverá un nuevo modelo con la lista de ordenes cargada

image

En la definición del grid es cuando indicamos que propiedad del modelo define los datos

var grid = new WebGrid(Model.OrderList, canPage: false, canSort: false);

luego se define el lugar donde ira el grid y que columnas se mostrara

 

@if (Model.OrderList != null)
{
    @grid.GetHtml(
                tableStyle: "webgrid-table",
                headerStyle: "webgrid-head",
                alternatingRowStyle: "webgrid-alternating-row",
                selectedRowStyle: "webgrid-selected-row",
                
                columns: grid.Columns(
                    grid.Column(columnName: "Employee.FullName", header: "Empleado", style: "webgrid-column-employee"),
                    grid.Column(columnName: "OrderDate", header: "Fecha Pedido", format: x=>x.OrderDate.ToString("dd/MM/yyyy")),
                    grid.Column(columnName: "RequiredDate", header: "Fecha Requisito", format: x=>x.RequiredDate.ToString("dd/MM/yyyy")),
                    grid.Column(columnName: "ShippedDate", header: "Fecha Envio", format: x=>x.ShippedDate != null ? x.ShippedDate.ToString("dd/MM/yyyy") : ""),
                    grid.Column(columnName: "ShipName", header: "Nombre Envio"),
                    grid.Column(columnName: "ShipAddress", header: "Direccion Envio") 
                
                ))
}
else
{
    <p>No hay datos</p>
}

se podría resaltar como se especifica una columna cuando la entidad esta relacionada con otra

grid.Column(columnName: "Employee.FullName", header: "Empleado", style: "webgrid-column-employee"),

al necesitar el nombre del empleado se realiza por medio de la relación

También se definió formato para las columnas fecha

grid.Column(columnName: "ShippedDate", header: "Fecha Envio", format: x=>x.ShippedDate != null ? x.ShippedDate.ToString("dd/MM/yyyy") : ""),

no solo indicando la representación de la fecha sino también validando cuando esta pueda tener un valor nulo

 

WebGrid referencia en asp.net mvc


Según el tipo de proyecto que crees en el Visual Studio puede que no tengas acceso al WebGrid, si este es el caso deberá agregar la referencia a la librería, para esta tarea NuGet nos dará una mano

Solo es cuestión de buscar: microsoft-web-helper

en la herramienta de nuget y proceder a instalarlo

SNAGHTML988cc7c2

 

Código


Para el ejemplo hemos utilizado Visual Studio 2012, la base de datos se encuentra en la carpeta App_Data del proyecto web

[C#]
 

22 comentarios:

  1. hola leandro por favor si puede responderme de lo agradecere

    mira tenco un reporte hecho en cristal reporte en una aplicación web de vb.net 2012 el hecho que que cuando lo visualizo sale bien
    lo exporto a pdf bien
    el hecho es a la hora de imprimirlo
    presiono el boton de imprimir y sale e dialogo el hecho es que cuando sale la impresion de cristal de la impresora solo sale el recuadro y las imagenes el contenido de ese report sale en pequeño cuadro donde esta el contenido

    ResponderEliminar
  2. hola wilson

    la verdad es bastante raro lo que comentas, peor me pregunto

    - si exportas a pdf y luego en esa mismo impresora imprimes envias el pdf a imprimir, lo hace de forma correcta ?

    - si creas alguna impresora virtual, de esas que definen una impresora pero en realidad el resultado d ela impresion lo envian a pdf, si envias a este tipo de impresora el reporte genera el pdf de forma correcta ? ojo no apunto a exportar sino a imprimir solo que el resultado de esa impresora virtual generara un pdf

    - intentaste enviar a otra impresora ?

    saludos

    ResponderEliminar
  3. Hola te escribo por acá ya que en el blog no figura ningún mail, quiero empezar a programar en c# y no tengo ni idea por dónde empezar.
    Hay miles de cosas por internet, estoy buscando algún libro lo más actual posible como para arrancar.
    ¿Qué recomendarías para alguien que viene de estructurado sin experiencia en POO?
    ¿Hay material que valga la pena en castellano o todo en inglés?

    Este es mi mail pablorastelli@gmail.com por si me queres contestar al mail.
    Gracias.

    ResponderEliminar
  4. hola Pablo

    podrias empezar con este libro

    http://www.scribd.com/doc/5541279/Anaya-La-biblia-de-C-libroprogramacionespanol

    despues si puedes conseguir los de Ceballos tambien son muy buenos

    http://es.scribd.com/doc/9824856/Ceballos-C-Curso-de-programacion-2-edicion

    saludos

    ResponderEliminar
  5. Buenas tarde ... espero que puedas ayudar ..

    Soy nuevo en MVC4 tengo una vista con 5 dropdownlist que se llenan desde Base de Dato .... y un ActionResult de agregar


    quisiera saber como hago para que cuando le haga click al Action result me guarde en una lista o en un modelo o en una tabla los valores seleccionados en cada dropdownlist


    si tienes algun material que pueda ayudar seria de mucha ayuda

    ResponderEliminar
  6. Hola Leandro

    Si me puede dar una orientacion, le agradeceria mucho.
    Actualmente tenemos una aplicacion que estaba publicada en un server win2k3 con .NET Framework 2. Este fin de semana nos vimos obligados a moverla a un win2k8 R2 con el mismo framework (2.0..). Una vez hecho esto, estuvimos haciendo algunas pruebas y la BD que interactua con esta aplicacion, entre las muchas tablas que tiene, una de ellas tiene un campo decimal(20,2) el cual, cuando le insertamos datos desde la app le agrega un cero a la derecha. Ya validamos el tema de configuracion regional y estan iguales el 2k3 y el 2k8. ¿Me puede dar una luz si depronto se me esta pasando algo?

    Por su valiosa colaboracion, mil gracias.

    ResponderEliminar
  7. hola Mauro

    pero donde es que le agrega el cero a la derecha ? cuando visualizas el dato en la pagina web o en la tabla

    si es en la tabla no hya problema es solo como lo representa, eso no afecta porque despues puedes en la aplicacion aplciar formato y quitar ese cero sino lo quieres visualizar

    si es un tema visual podrias aplciar formato usando

    textbox1.Text = valor.ToString("N0");

    con eso aplicas un formato numerico sin decimales, pero lo defines cuando muestras el valor en la pagina al usuario

    saludos

    ResponderEliminar
  8. Hola leandro buen dia,

    tengo un detalle con un metodo ajax que tengo en mi vista, pero en mi controlador no logro devolver ese metodo he intentado muchos ejemplos y no .. que me aconsejas.

    si es posible publicar el codigo donde lo hago

    ResponderEliminar
  9. hola Erick

    para publicar codigo es mejor en el foro

    asp.net mvc foro

    saludos

    ResponderEliminar
  10. Saludos leandro, tengo una pequeña duda sobre rendimiento, veo que realizas un filtrado de datos, en el servidor de aplicacion, ¿Es esta la forma mas performante de hacerlo? y ¿Cual la diferencia con hacerlo en la BBDD, enviando un parametro y capturar los datos ya filtrados?

    ResponderEliminar
  11. hola IvanKike

    la implementacion puede variar dependiendo del volumen de datos que tenga la entidad con la cual trabajas

    quizas si son pocos registros se podria usar algun tipo de cache para mentener los datos he ir filtrandolos sin necesidad consultar la db

    pero si son muchos miles de registros, o si cambian constantemente ponerlo en un cache no veo que sea conveniente, es por eso que se toma la seleccion y se filtra directo usando una query (o en este caso linq) y se carga el resultado al model para enviarlos a la view

    saludos

    ResponderEliminar
  12. Hola que tal saludos,

    mi pregunta es la siguiente tengo un formulario de inicio de sesion, y quiero que cuando un usuario inicie sesion en otro formulario le aparezca en un dropdownlist solo los clientes para ese usuario...

    Tengo 2 tablas: usuarios y clientes.

    Porfa necesito de urgente que me ayuden con ese codigo paso a paso por que soy nuevo en esto.

    Gracias.

    ResponderEliminar
  13. hola Diego

    pero estas tablas que mencionas se relacionan de alguna forma ?

    quizas la tabla cliente tiene algun campo que referencie al id del usuario
    o quizas tener alguna tabla intermedia si es que se trata de una relacion mucho a muchos

    despues si tienes el usuario podrias simplemente filtrarlo usando un WHERE en la query

    saludos

    ResponderEliminar
  14. Hola Leandro Como puedo hacer para que una fecha se actualice desde un boton ?
    tengo un formulario donde actualizo los datos de mi formulario se cargan desde un grid pero cuando le doy actualizar quiero que en el grid aparezca la fecha y hora en que se actualizo como le puedo hacer ?

    ResponderEliminar
    Respuestas
    1. hola
      Imagino es un desarrollo asp.net mvc, no ? pero no entendi como implementas ese grid
      No deberias actualizar el grid sino los datos que este muestra, no se si usas una lista que mentienes en Session, pero al editar deberias actualizar alguna propeidad de la clase que muestras en el grid
      saludos

      Eliminar
    2. Tengo un Grid en el cual mando datos de otro formulario.aspx en el grid tengo dos CommandName con los cuales actualizo y elimino si selecciono la opcion de actualizar me manda al formulario donde puedo generar un nuevo registro y actualizo lo que quiero hacer es que cuando actualice se guarde la fecha y hora de actualizacion espero y me entiendas saludos!!

      Eliminar
    3. hola
      Pero si actualizas entonces como es que podrias generar otro registro? la opcion de generar un registro deberia estar en un boton fuera del grid
      Podrias en la entidad o en la tabla registrar esta operacion definiendo un campo del tipo datetime entonces cuando ejecutas el UPDATE envias el DateTime.Now para registrar el momento de la operacion
      saldos

      Eliminar
    4. Hola Leandro como estas? oye otra consulta como puedo hacer para que una aplicación aspx en vb en la cual tengo un grid con datos cargados pueda buscar desde un texbox un nombre de usuario y me aparezca nada mas ese nombre o nombres de usuario y con un boton buscar

      Eliminar
    5. hola
      Este filtro sobre el grid quieres aplicarlo desde codigo cliente con javascript, o el filtro aplcia del lado del servidor ?
      Si es desde el servidor podras ubicar un boton que aplique el filtro, en el click deberias realizar una query a la db, pero no has mencionado que db utilizas, o si conoces sobre ado.net
      Basicamente pasarias el textbox por parametro en el command, y la query usaria un LIKE
      saludos

      Eliminar
  15. Hola Leandro, te hago una consulta porque estoy medio perdido con MVC, estoy haciendo un sistema para practicar, y tengo una tabla Provincia con un campo Estado, que me indica si esta activa o no.
    Lo que pretendo hacer es que me traiga desde la base de datos directamente las que estan activas, ya sea para mostrarlas en una grilla o en un dropdownlist, en resumen quiero que en todo momento me traiga las que estan activas, y no queria hacerlo desde el controler, ya que cada vez que quiera invocar a la entidad Provincia tengo que hacer el filtrado.

    Como puedo hacer esto??

    Muchas gracoas

    ResponderEliminar
    Respuestas
    1. hola
      Para que solo te devuelva las activas podrias filtrar en la query que realizas a la db, como ser:
      SELECT * FROM provincias WHERE Estado = 1
      De esta forma solo devolvera las activas
      saludos

      Eliminar
  16. Que tal Leandro, antes que nada agradecer por compartir tu conocimiento. Mira soy nuevo en mvc, espero me puedas ayudar. Lo que pasa es que tengo una view, en esta tengo unos selects que cargo desde una db, tambien una tabla. Lo que quiero realizar es tomar los valores de los select cuando recien carga la view y aplicarlos como filtro para poder recuperar los datos y mostrarlos en la tabla.

    Cualquier ayuda se agradece.
    Saludos.

    ResponderEliminar