LINQ en ASP.NET MVC: Operaciones de Conjunto con Union, Intersect y Except

En el desarrollo de aplicaciones web con ASP.NET MVC y Entity Framework, las consultas LINQ ofrecen potentes funcionalidades para manipular y consultar datos. Las operaciones de conjunto, como Union, Intersect y Except, permiten combinar y comparar colecciones de datos de manera eficiente. En este artículo, exploraremos cómo utilizar estas operaciones de conjunto en consultas LINQ para realizar tareas comunes de manipulación de datos en nuestras aplicaciones ASP.NET MVC.

Creación del Proyecto en Visual Studio

  • Crear Nuevo Proyecto:

    • Abre Visual Studio y selecciona File > New > Project.
    • Escoge ASP.NET Web Application (.NET Framework) como el tipo de proyecto.
    • Asigna un nombre al proyecto, como LinqSetOperationsExample.
    • Selecciona la plantilla MVC y haz clic en OK para crear el proyecto.
  • Instalación de Entity Framework:

    • Abre Package Manager Console desde Tools > NuGet Package Manager > Package Manager Console.
    • Ejecuta el siguiente comando para instalar Entity Framework:
Install-Package EntityFramework

 

 

Definición de Modelos y Configuración de DbContext

En la carpeta Models de tu proyecto, define los modelos de datos que utilizarás y configura el contexto de datos (DbContext) para interactuar con la base de datos.

Customer.cs (en la carpeta Models):

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Order> Orders { get; set; }
}

Order.cs (en la carpeta Models):

public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    public int CustomerId { get; set; }
    public Customer Customer { get; set; }
    public ICollection<OrderDetail> OrderDetails { get; set; }
}

OrderDetail.cs (en la carpeta Models):

public class OrderDetail
{
    public int Id { get; set; }
    public int OrderId { get; set; }
    public int ProductId { get; set; }
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public Order Order { get; set; }
    public Product Product { get; set; }
}

Product.cs (en la carpeta Models):

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

 

Configuración del Contexto de Datos

Crea una clase ApplicationDbContext (si no existe ya) que extienda DbContext para configurar la conexión y definir las relaciones entre los Modelos de datos y las tablas de la base de datos utilizando Entity Framework.

ApplicationDbContext.cs:

using System.Data.Entity;

public class ApplicationDbContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<OrderDetail> OrderDetails { get; set; }
    public DbSet<Product> Products { get; set; }

    // Constructor que configura la cadena de conexión.
    public ApplicationDbContext() : base("name=DefaultConnection")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configuraciones adicionales del modelo
        base.OnModelCreating(modelBuilder);
    }
}

 

Configuración de la Cadena de Conexión

Para que Entity Framework pueda conectarse a la base de datos, es necesario definir la cadena de conexión en el archivo Web.config:

<configuration>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=LinqSetOperationsExample;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <!-- Otras configuraciones -->
</configuration>

Nota: Esta cadena de conexión se configura para usar una base de datos local con el nombre LinqSetOperationsExample. Puedes ajustar esta configuración según tus necesidades específicas de entorno.

 

 

Cargar datos de prueba en la Base de Datos

Para simular datos de prueba en la base de datos, utilizaremos el método Seed dentro de la clase de configuración de migraciones de Entity Framework. A continuación, se muestra cómo agregar datos de ejemplo para clientes, órdenes y detalles de órdenes.

Configuration.cs (en la carpeta Migrations):

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using LinqSetOperationsExample.Models;

internal sealed class Configuration : DbMigrationsConfiguration<ApplicationDbContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(ApplicationDbContext context)
    {
        var customer1 = new Customer { Name = "Cliente A" };
        var customer2 = new Customer { Name = "Cliente B" };
        var customer3 = new Customer { Name = "Cliente C" };

        var product1 = new Product { Name = "Producto 1", Price = 100 };
        var product2 = new Product { Name = "Producto 2", Price = 150 };
        var product3 = new Product { Name = "Producto 3", Price = 200 };

        var order1 = new Order
        {
            Customer = customer1,
            OrderDate = DateTime.Now,
            OrderDetails = new List<OrderDetail>
            {
                new OrderDetail { Product = product1, Quantity = 2, UnitPrice = product1.Price },
                new OrderDetail { Product = product2, Quantity = 1, UnitPrice = product2.Price }
            }
        };

        var order2 = new Order
        {
            Customer = customer2,
            OrderDate = DateTime.Now.AddDays(-7),
            OrderDetails = new List<OrderDetail>
            {
                new OrderDetail { Product = product1, Quantity = 3, UnitPrice = product1.Price },
                new OrderDetail { Product = product3, Quantity = 2, UnitPrice = product3.Price }
            }
        };

        var order3 = new Order
        {
            Customer = customer3,
            OrderDate = DateTime.Now.AddDays(-14),
            OrderDetails = new List<OrderDetail>
            {
                new OrderDetail { Product = product2, Quantity = 1, UnitPrice = product2.Price },
                new OrderDetail { Product = product3, Quantity = 1, UnitPrice = product3.Price }
            }
        };

        context.Customers.AddOrUpdate(customer1, customer2, customer3);
        context.Products.AddOrUpdate(product1, product2, product3);
        context.Orders.AddOrUpdate(order1, order2, order3);

        context.SaveChanges();
    }
}

 

 

Creación de las Clases ViewModel

Antes de implementar las consultas LINQ en el controlador, definiremos las clases ViewModel que se utilizarán para proyectar los resultados de las consultas.

CustomerOrderViewModel.cs (en la carpeta Models):

public class CustomerOrderViewModel
{
    public string CustomerName { get; set; }
    public DateTime OrderDate { get; set; }
    public string ProductName { get; set; }
    public int Quantity { get; set; }
    public decimal UnitPrice { get; set; }
}

Nota: Estas clases ViewModel se utilizan para proyectar los resultados de las consultas LINQ en formas específicas que se puedan mostrar en las vistas.

 

 

Consultas LINQ con Operaciones de Conjunto en el Controlador

Para utilizar consultas LINQ con operaciones de conjunto, crearemos un controlador SetOperationsController que maneje las acciones necesarias para mostrar los resultados en las vistas correspondientes.

Crear el Controlador

En la carpeta Controllers, crea un nuevo controlador llamado SetOperationsController. Añade las acciones Union, Intersect y Except que realizarán las consultas LINQ y devolverán los datos necesarios para las vistas correspondientes.

SetOperationsController.cs (en la carpeta Controllers):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using LinqSetOperationsExample.Models;

public class SetOperationsController : Controller
{
    private ApplicationDbContext db = new ApplicationDbContext();

    // GET: SetOperations/Union
    public ActionResult Union()
    {
        var ordersFromCustomer1 = db.Orders
                                    .Where(o => o.CustomerId == 1)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var ordersFromCustomer2 = db.Orders
                                    .Where(o => o.CustomerId == 2)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var unionQuery = ordersFromCustomer1.Union(ordersFromCustomer2).ToList();
        
        return View(unionQuery);
    }

    // GET: SetOperations/Intersect
    public ActionResult Intersect()
    {
        var ordersFromCustomer1 = db.Orders
                                    .Where(o => o.CustomerId == 1)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var ordersFromCustomer3 = db.Orders
                                    .Where(o => o.CustomerId == 3)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var intersectQuery = ordersFromCustomer1.Intersect(ordersFromCustomer3).ToList();
        
        return View(intersectQuery);
    }

    // GET: SetOperations/Except
    public ActionResult Except()
    {
        var ordersFromCustomer1 = db.Orders
                                    .Where(o => o.CustomerId == 1)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var ordersFromCustomer2 = db.Orders
                                    .Where(o => o.CustomerId == 2)
                                    .Select(o => new CustomerOrderViewModel
                                    {
                                        CustomerName = o.Customer.Name,
                                        OrderDate = o.OrderDate,
                                        ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                        Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                        UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                                    });

        var exceptQuery = ordersFromCustomer1.Except(ordersFromCustomer2).ToList();
        
        return View(exceptQuery);
    }
}

 

 

Explicación Detallada del Código LINQ

En este apartado, profundizaremos en las consultas LINQ utilizadas en las acciones del controlador SetOperationsController, específicamente en las operaciones Union, Intersect y Except.

1. Operación Union

La operación Union en LINQ combina dos colecciones de datos y elimina cualquier duplicado en el resultado final. En nuestro caso, queremos mostrar las órdenes de clientes que pertenecen tanto al cliente 1 como al cliente 2.

Código LINQ para Union:

// Obtener órdenes del cliente 1
var ordersFromCustomer1 = db.Orders
                            .Where(o => o.CustomerId == 1)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Obtener órdenes del cliente 2
var ordersFromCustomer2 = db.Orders
                            .Where(o => o.CustomerId == 2)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Aplicar la operación Union
var unionQuery = ordersFromCustomer1.Union(ordersFromCustomer2).ToList();

Explicación:

  • ordersFromCustomer1 y ordersFromCustomer2 son consultas LINQ separadas que seleccionan órdenes de clientes específicos (cliente 1 y cliente 2 respectivamente).
  • Utilizamos Select para proyectar los resultados en instancias de CustomerOrderViewModel, que es una clase ViewModel diseñada para mostrar datos específicos en la vista.
  • La operación Union se aplica en ordersFromCustomer1 y ordersFromCustomer2, combinando ambas colecciones y eliminando duplicados basados en la igualdad de objetos.

 

2. Operación Intersect

La operación Intersect en LINQ devuelve los elementos que son comunes en ambas colecciones. En nuestro caso, queremos mostrar las órdenes que son comunes entre el cliente 1 y el cliente 3.

Código LINQ para Intersect:

// Obtener órdenes del cliente 1
var ordersFromCustomer1 = db.Orders
                            .Where(o => o.CustomerId == 1)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Obtener órdenes del cliente 3
var ordersFromCustomer3 = db.Orders
                            .Where(o => o.CustomerId == 3)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Aplicar la operación Intersect
var intersectQuery = ordersFromCustomer1.Intersect(ordersFromCustomer3).ToList();

Explicación:

  • ordersFromCustomer1 y ordersFromCustomer3 son consultas que seleccionan órdenes de clientes específicos (cliente 1 y cliente 3 respectivamente).
  • Utilizamos Select para proyectar los resultados en instancias de CustomerOrderViewModel.
  • La operación Intersect se aplica en ordersFromCustomer1 y ordersFromCustomer3, devolviendo las órdenes que son comunes entre ambos conjuntos basadas en la igualdad de objetos.

 

3. Operación Except

La operación Except en LINQ devuelve los elementos que están presentes en la primera colección pero no en la segunda. En nuestro caso, queremos mostrar las órdenes del cliente 1 que no están presentes en las órdenes del cliente 2.

Código LINQ para Except:

// Obtener órdenes del cliente 1
var ordersFromCustomer1 = db.Orders
                            .Where(o => o.CustomerId == 1)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Obtener órdenes del cliente 2
var ordersFromCustomer2 = db.Orders
                            .Where(o => o.CustomerId == 2)
                            .Select(o => new CustomerOrderViewModel
                            {
                                CustomerName = o.Customer.Name,
                                OrderDate = o.OrderDate,
                                ProductName = o.OrderDetails.FirstOrDefault().Product.Name,
                                Quantity = o.OrderDetails.FirstOrDefault().Quantity,
                                UnitPrice = o.OrderDetails.FirstOrDefault().UnitPrice
                            });

// Aplicar la operación Except
var exceptQuery = ordersFromCustomer1.Except(ordersFromCustomer2).ToList();

Explicación:

  • ordersFromCustomer1 y ordersFromCustomer2 son consultas que seleccionan órdenes de clientes específicos (cliente 1 y cliente 2 respectivamente).
  • Utilizamos Select para proyectar los resultados en instancias de CustomerOrderViewModel.
  • La operación Except se aplica en ordersFromCustomer1 y ordersFromCustomer2, devolviendo las órdenes del cliente 1 que no están presentes en las órdenes del cliente 2.

 

 

Creación de Vistas

Las vistas se utilizan para mostrar los resultados de las consultas en el navegador. A continuación, se muestra cómo crear las vistas para mostrar los resultados de las operaciones de conjunto.

  1. Crear Vista para Union:

    • Haz clic derecho en la acción Union del SetOperationsController y selecciona Add View.
    • Configura la vista como una vista fuertemente tipada con el modelo IEnumerable<CustomerOrderViewModel>.

Union.cshtml:

@model IEnumerable<LinqSetOperationsExample.Models.CustomerOrderViewModel>

<h2>Union de Órdenes de Clientes</h2>

<table class="table">
    <thead>
        <tr>
            <th>Customer Name</th>
            <th>Order Date</th>
            <th>Product Name</th>
            <th>Quantity</th>
            <th>Unit Price</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.CustomerName</td>
                <td>@item.OrderDate.ToShortDateString()</td>
                <td>@item.ProductName</td>
                <td>@item.Quantity</td>
                <td>@item.UnitPrice.ToString("C")</td>
            </tr>
        }
    </tbody>
</table>

 

  1. Crear Vista para Intersect:

    • Haz clic derecho en la acción Intersect del SetOperationsController y selecciona Add View.
    • Configura la vista como una vista fuertemente tipada con el modelo IEnumerable<CustomerOrderViewModel>.

Intersect.cshtml:

@model IEnumerable<LinqSetOperationsExample.Models.CustomerOrderViewModel>

<h2>Órdenes Comunes de Clientes</h2>

<table class="table">
    <thead>
        <tr>
            <th>Customer Name</th>
            <th>Order Date</th>
            <th>Product Name</th>
            <th>Quantity</th>
            <th>Unit Price</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.CustomerName</td>
                <td>@item.OrderDate.ToShortDateString()</td>
                <td>@item.ProductName</td>
                <td>@item.Quantity</td>
                <td>@item.UnitPrice.ToString("C")</td>
            </tr>
        }
    </tbody>
</table>

 

  1. Crear Vista para Except:

    • Haz clic derecho en la acción Except del SetOperationsController y selecciona Add View.
    • Configura la vista como una vista fuertemente tipada con el modelo IEnumerable<CustomerOrderViewModel>.

Except.cshtml:

@model IEnumerable<LinqSetOperationsExample.Models.CustomerOrderViewModel>

<h2>Órdenes Excluyentes de Clientes</h2>

<table class="table">
    <thead>
        <tr>
            <th>Customer Name</th>
            <th>Order Date</th>
            <th>Product Name</th>
            <th>Quantity</th>
            <th>Unit Price</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>@item.CustomerName</td>
                <td>@item.OrderDate.ToShortDateString()</td>
                <td>@item.ProductName</td>
                <td>@item.Quantity</td>
                <td>@item.UnitPrice.ToString("C")</td>
            </tr>
        }
    </tbody>
</table>

 

 

Conclusión 

En este artículo, hemos explorado cómo utilizar las operaciones de conjunto (Union, Intersect, Except) en consultas LINQ dentro de una aplicación ASP.NET MVC. Estas operaciones permiten combinar y comparar colecciones de datos de manera eficiente, proporcionando una poderosa herramienta para la manipulación de datos en tus aplicaciones web.

Con este conocimiento, estás preparado para aplicar operaciones de conjunto en tus propios proyectos ASP.NET MVC y aprovechar al máximo las capacidades de LINQ y Entity Framework para consultas avanzadas.

 

   EtiquetasASP.NET MVC LINQ Entity Framework

  Compartir


  Nuevo comentario

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

  Comentarios

No hay comentarios para este Post.



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