'I can't get left join result to view with pagination
i can't pass the result to view, my Index view has pagination.
Index View:
@model rInventarioTI.Clases.PaginationList<rInventarioTI.Models.Productos>
@{
ViewData["Title"] = "Lista Productos";
}
<h1 class="text-center">Listado Productos</h1>
<br />
<br />
<br />
<form asp-action="Index" method="get">
<div class="form-group d-flex justify-content-center">
<p>
Tipo/Serie/Marca/Modelo
<input type="text" name="searchString" value="@ViewData["FiltroActual"]" />
<input type="submit" value="Filtrar" class="btn btn-primary" />
<a asp-action="Index">Borrar filtro</a>
</p>
</div>
</form>
<p class="float-end">
<a asp-action="Create" class="btn btn-success">Crear nuevo</a>
</p>
<table class="table">
<thead class="bg-dark" style="color: white;">
<tr>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["TypeProductNameSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">Tipo de producto <i class="fas fa-sort-amount-down-alt"></i> </a>
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["COGSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">COG <i class="fas fa-sort-amount-down-alt"></i></a>
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["SerieSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">Numero de Serie <i class="fas fa-sort-amount-down-alt"></i></a>
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["MarcaSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">Marca <i class="fas fa-sort-amount-down-alt"></i></a>
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["ModeloSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">Modelo Equipo<i class="fas fa-sort-amount-down-alt"></i></a>
</th>
<th>
@*@Html.DisplayNameFor(model => model.TiposMovimientosNav)*@
Movimiento
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]"
asp-route-filtroActual="@ViewData["FiltroActual"]">Fecha Registro<i class="fas fa-sort-amount-down-alt"></i></a>
</th>
<th>Operaciones</th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.TiposProductosNav.Nombre)
</td>
<td>
@Html.DisplayFor(modelItem => item.COG)
</td>
<td>
@Html.DisplayFor(modelItem => item.NumeroSerie)
</td>
<td>
@Html.DisplayFor(modelItem => item.MarcasNav.Nombre)
</td>
<td>
@Html.DisplayFor(modelItem => item.Modelo)
</td>
<td>
@Html.DisplayFor(modelItem => item.MovimientosProductosNav)
</td>
<td>
@Html.DisplayFor(modelItem => item.FechaAlta)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.Id"><i class="fas fa-pencil-alt"></i></a> |
<a asp-action="Details" asp-route-id="@item.Id"><i class="fas fa-eye"></i></a> |
<a asp-action="Delete" asp-route-id="@item.Id"><i class="fas fa-trash-alt"></i></a> |
<a asp-action="Asignar" asp-route-id="@item.Id"><i class="fas fa-arrow-right"> Asignar</i></a> |
<a asp-action="Desasignar" asp-route-id="@item.Id"><i class="fas fa-arrow-left"> Desasignar</i></a> |
<a asp-action="Transferir" asp-route-id="@item.Id"><i class="fas fa-exchange-alt"> Transferir</i></a>
</td>
</tr>
}
</tbody>
</table>
@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}
<a asp-action="Index"
asp-route-sortOrder="@ViewData["OrdenamientoActual"]"
asp-route-pageNumber="@(Model.PageIndex + 1)"
asp-route-filtroActual="@ViewData["FiltroActual"]"
class="btn btn-primary @nextDisabled float-end">Adelante</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["OrdenamientoActual"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-filtroActual="@ViewData["FiltroActual"]"
class="btn btn-primary @prevDisabled float-end">Atrás</a>
PaginationList
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace rInventarioTI.Clases
{
public class PaginationList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginationList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage
{
get
{
return (PageIndex > 1);
}
}
public bool HasNextPage
{
get
{
return (PageIndex < TotalPages);
}
}
public static async Task<PaginationList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginationList<T>(items, count, pageIndex, pageSize);
}
}
}
Productos Model:
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace rInventarioTI.Models
{
//[Index(nameof(COG), IsUnique = true)]
public partial class Productos
{
public Productos()
{
MovimientosProductosNav = new HashSet<MovimientosProductos>();
}
public int Id { get; set; }
[Display(Name = "Tipo de producto")]
//[Required(ErrorMessage = "Seleccione un tipo de producto")]
public int TiposProductosID { get; set; }
[Display(Name = "Marca")]
public int MarcasID { get; set; }
// Algunos productos pueden no tener numero de serie.
[Display(Name = "Numero Serie")]
public string NumeroSerie { get; set; }
[Display(Name = "Descripcion")]
[Required(ErrorMessage = "Ingrese una descripción")]
[StringLength(1500, MinimumLength = 6, ErrorMessage = " Caracteres permitidos, mas de 6 y menos de 1500")]
public string Descripcion { get; set; }
[Display(Name = "Modelo del producto")]
[Required(ErrorMessage = "Ingrese un modelo para este producto")]
public string Modelo { get; set; }
[Display(Name = "Color de producto")]
[Required(ErrorMessage = "Ingrese un color para este producto")]
public string Color { get; set; }
[Display(Name = "Codigo del fabricante")]
public string CodigoFabricante { get; set; }
// Clave unica e irrepetible. TODOS los productos deben tener
// un COD definido y asignado por el sistema.
[Display(Name = "COG")]
[Required(ErrorMessage = "Ingrese un COG")]
[StringLength(25, MinimumLength = 3, ErrorMessage = "Ingrese un COG de entre 3 y 25 caracteres")]
public string COG { get; set; }
[Display(Name = "Datos QR del producto")]
public string CodigoQr { get; set; }
[Display(Name = "Fecha de Registro")]
public DateTime FechaAlta { get; set; }
[Display(Name = "Tipo de producto")]
public virtual TiposProductos TiposProductosNav { get; set; }
[Display(Name = "Marcas")]
public virtual Marcas MarcasNav { get; set; }
public virtual ICollection<MovimientosProductos> MovimientosProductosNav { get; set; }
}
}
MovimientosProductos Model
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
namespace rInventarioTI.Models
{
public partial class MovimientosProductos
{
public MovimientosProductos() {
}
public int id { get; set; }
[Required]
public int ProductosID { get; set; }
[Display(Name = "Tipo Movimiento")]
[Required]
public int TiposMovimientosID { get; set; }
[Display(Name = "Usuario Genera Movimiento")]
public int UsuariosID { get; set; }
[Display(Name = "Oficina Destino")]
public int OficinasID { get; set; }
[Display(Name = "Estado físico del producto")]
public int EstadosProductosID { get; set; }
[Display(Name = "Observaciones del movimiento y/o producto")]
[Required(ErrorMessage = "Observaciones del movimiento y/o producto")]
[StringLength(1500, MinimumLength = 6, ErrorMessage = "Minimo 6 caracteres, maximo 1500 caracteres.")]
public string Observaciones { get; set; }
[Display(Name = "Fotos del producto")]
public string PathFotos { get; set; }
[Display(Name = "Fecha de Registro")]
public DateTime FechaAlta { get; set; }
[Display(Name = "Produco")]
public virtual Productos ProductosNav { get; set; }
[Display(Name = "Tipo Movimiento")]
public virtual TiposMovimientos TiposMovimientosNav { get; set; }
[Display(Name = "Usuario generó movimiento")]
public virtual Usuarios UsuariosNav { get; set; }
[Display(Name = "Oficina asignada")]
public virtual Oficinas OficinaNav { get; set; }
[Display(Name = "Estado del producto")]
public virtual EstadosProductos EstadosProductosNav { get; set; }
}
}
Productos Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using rInventarioTI.Models;
using rInventarioTI.Clases;
using System.Collections.Generic;
//using System.Data.Entity;
namespace rInventarioTI.Controllers
{
public class ProductosController : Controller
{
private readonly rInventarioContext _context;
public ProductosController(rInventarioContext context)
{
_context = context;
}
// GET: Productos
public async Task<IActionResult> Index(
string sortOrder,
string filtroActual,
string searchString,
int? pageNumber)
{
ViewData["OrdenamientoActual"] = sortOrder;
ViewData["TypeProductNameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "Name_desc" : "";
ViewData["COGSortParm"] = sortOrder == "COG" ? "COG_desc" : "COG";
ViewData["SerieSortParm"] = sortOrder == "Serie" ? "Serie_desc" : "Serie";
ViewData["MarcaSortParm"] = sortOrder == "Marca" ? "Marca_desc" : "Marca";
ViewData["ModeloSortParm"] = sortOrder == "Modelo" ? "Modelo_desc" : "Modelo";
ViewData["DatesortParm"] = sortOrder == "Date" ? "Date_desc" : "Date";
// Valido si hay cadena de búsqueda ingresada
if (searchString != null)
{
pageNumber = 1;
} else
{
searchString = filtroActual;
}
ViewData["FiltroActual"] = searchString;
//var productos = (from prod in _context.Productos
// select prod)
// .Include(m => m.MarcasNav).AsQueryable()
// .Include(t => t.TiposProductosNav).AsQueryable()
// .Include(ep => ep.MovimientosProductosNav).AsQueryable();
/*
Recupera listado de productos, donde el numero de movimientos sea de 2 o mas.
*/
var productos = (from prod in _context.Productos.ToList()
join mov in _context.MovimientosProductos on prod.Id equals mov.ProductosID into ProductosMovimientosGroup
from mov in ProductosMovimientosGroup.DefaultIfEmpty()
where (ProductosMovimientosGroup.Count() == 0)
select prod);
if (!String.IsNullOrEmpty(searchString))
{
productos = productos.Where(
prod => prod.TiposProductosNav.Nombre.Contains(searchString) ||
//prod.SerialesProductosID.
prod.MarcasNav.Nombre.Contains(searchString) ||
prod.Modelo.Contains(searchString) ||
prod.COG.Contains(searchString));
}
switch (sortOrder)
{
case "Name_desc":
productos = productos.OrderByDescending(prod => prod.TiposProductosNav.Nombre);
break;
case "COG":
productos = productos.OrderBy(prod => prod.COG);
break;
case "COG_desc":
productos = productos.OrderByDescending(prod => prod.COG);
break;
case "Serie":
productos = productos.OrderBy(prod => prod.NumeroSerie);
break;
case "Serie_desc":
productos = productos.OrderByDescending(prod => prod.NumeroSerie);
break;
case "Marca":
productos = productos.OrderBy(prod => prod.MarcasNav.Nombre);
break;
case "Marca_desc":
productos = productos.OrderByDescending(prod => prod.MarcasNav.Nombre);
break;
case "Modelo":
productos = productos.OrderByDescending(prod => prod.Modelo);
break;
case "Modelo_desc":
productos = productos.OrderBy(prod => prod.Modelo);
break;
case "Date":
productos = productos.OrderBy(prod => prod.FechaAlta);
break;
case "Date_desc":
productos = productos.OrderByDescending(prod => prod.FechaAlta);
break;
default:
productos = productos.OrderBy(prod => prod.TiposProductosNav.Nombre);
break;
}
int pageSize = 10;
return View(await PaginationList<Productos>.CreateAsync(
productos.AsNoTracking(),
pageNumber ?? 1,
pageSize));
//return View(productos);
}
}
}
Source: Tutorial: Add sorting, filtering, and paging - ASP.NET MVC with EF Core
My query in SQL works fine
SELECT Productos.COG, Productos.Id
from Productos
left join MovimientosProductos on MovimientosProductos.ProductosID = Productos.Id
Group by Productos.Id, Productos.COG
having COUNT(MovimientosProductos.ProductosID) = 0
SELECT Productos.COG, Productos.Id, ProductosID
from MovimientosProductos
RIGHT join Productos on Productos.Id = MovimientosProductos.ProductosID
Group by Productos.COG, Productos.Id, ProductosID
having ProductosID IS NULL
My query in Linq is:
var productos = (from prod in _context.Productos.ToList()
join mov in _context.MovimientosProductos on prod.Id equals mov.ProductosID into ProductosMovimientosGroup
from mov in ProductosMovimientosGroup.DefaultIfEmpty()
where ProductosMovimientosGroup.Count() == 0
select prod);
and in debug mode productos has 3 records. Works fine.
But when the app runs, i get this error:
Severity Code Description Project File Line Suppression State Error CS1061 'IEnumerable' does not contain a definition for 'AsNoTracking' and no accessible extension method 'AsNoTracking' accepting a first argument of type 'IEnumerable' could be found (are you missing a using directive or an assembly reference?)
I understand that i need to get IQueryable result instead of IEnumerable, but how can i convert the result?
Then, i have removed ToList() from the query and AsNoTracking() from the return View for test, but now the error is:
An unhandled exception occurred while processing the request. InvalidOperationException: The LINQ expression 'DbSet() .LeftJoin( inner: DbSet(), outerKeySelector: p => p.Id, innerKeySelector: m => m.ProductosID, resultSelector: (p, m) => new TransparentIdentifier<Productos, MovimientosProductos>( Outer = p, Inner = m )) .Where(ti => ProductosMovimientosGroup .Count() == 0)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
But i don't know how to translate the query...
Thank you
Solution 1:[1]
Thank you all for your comments
Gert Arnold mentioned the navigation properties and then changed the query for this:
var productos = (from p in _context.Productos
.Include(mp => mp.MovimientosProductosNav)
.Include(m => m.MarcasNav)
.Include(tp => tp.TiposProductosNav).ToList()
select p);
I get the properties by "navigation properties" declared in my models and the view get model without any problem.
Now it works.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
