Solo se admiten inicializadores, miembros de entidad y propiedades de navegación de entidad

102

Recibo esta excepción:

El miembro de tipo especificado 'Pagado' no es compatible con LINQ to Entities. Solo se admiten inicializadores, miembros de entidad y propiedades de navegación de entidad.

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .Where(o => o.Paid == false)
            .OrderByDescending(o => o.DateCreated);

        return View(debts);
    }

Mi clase de modelo

public partial class Order
{
    public bool Paid {
        get {
            return TotalPaid >= Total;
        }
    }

    public decimal TotalPaid {
        get {
            return Payments.Sum(p => p.Amount);
        }
    }

Pagos es una tabla relacionada que contiene el monto del campo. La consulta funciona si elimino la cláusula Where que muestra la información correcta sobre los pagos, ¿alguna pista de lo que está mal con el código?

Resuelto como la respuesta sugerida con:

    public ActionResult Index()
    {
        var debts = storeDB.Orders
            .OrderByDescending(o => o.DateCreated)
            .ToList()
            .Where(o => o.Paid == false);

        return View(debts);
    }
Bagazo
fuente
15
Respuesta simple: ¡no puede usar propiedades no asignadas en consultas de linq a entidades! Solo las propiedades asignadas se traducen a SQL.
Ladislav Mrnka

Respuestas:

114

La entidad está intentando convertir su propiedad Paid a SQL y no puede porque no forma parte del esquema de la tabla.

Lo que puede hacer es dejar que la entidad consulte la tabla sin filtro de pago y luego filtrar los que no son de pago.

public ActionResult Index()
{
    var debts = storeDB.Orders
        //.Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

    debts = debts.Where(o => o.Paid == false);

    return View(debts);
}

Eso, por supuesto, significaría que traerá todos los datos al servidor web y los filtrará. Si desea filtrar en el servidor de base de datos, puede crear una columna calculada en la tabla o utilizar un procedimiento almacenado.

Eugene S.
fuente
25

Solo tenía que resolver un problema similar. Las soluciones anteriores requieren procesamiento en memoria, lo cual es una mala práctica (carga diferida).

Mi solución fue escribir un ayudante que devolviera un predicado:

public static class Extensions
{
    public static Expression<Func<Order, bool>> IsPaid()
    {
        return order => order.Payments.Sum(p => p.Amount) >= order.Total;
    }
}

Puede reescribir su declaración de linq como:

var debts = storeDB.Orders
                    .Where(Extensions.IsPaid())
                    .OrderByDescending(o => o.DateCreated);

Esto es útil cuando desea reutilizar la lógica de cálculo (DRY). La desventaja es que la lógica no está en su modelo de dominio.

Koen Luyten
fuente
1
Hay una serie de bibliotecas que intentan hacer que este enfoque esté más "integrado", consulte: stackoverflow.com/a/27383641/470183 . Linq-to-entity se limita a expresiones que utilizan las "funciones canónicas", que se pueden convertir en SQL. C # 6 introdujo "Funciones con cuerpo de expresión" pero estas no son lambdas verdaderas (ver: stackoverflow.com/a/28411444/470183 ). Aún así, sería bueno tener esto en el marco, de ahí los datos de WIBNI.uservoice.com/forums/…
James Close
1
Gracias por este sencillo y conciso ejemplo de Expression<Func<xx,yy>>. Lo había entendido antes, pero ahora parece obvio.
AlexB
17

Este problema también puede provenir de una [NotMapped]propiedad que tiene el mismo nombre en su modelo de base de datos y modelo de vista.

AutoMapper intenta seleccionarlo del DB durante una proyección; y la propiedad NotMapped obviamente no existe en la base de datos.

La solución es Ignorela propiedad en la configuración de AutoMapper al mapear desde el modelo de base de datos al modelo de vista.

  1. Busque una [NotMapped]propiedad con nombre Fooen su modelo de base de datos.
  2. Busque una propiedad con el mismo nombre Foo, en su Modelo de vista.
  3. Si ese es el caso, cambie su configuración de AutoMapper. Añadir.ForMember(a => a.Foo, b => b.Ignore());
Encadenar
fuente
Dang AutoMapper Projection también me atrapó, ¡gracias por la respuesta!
Chase Florell
15

Linq convierte las declaraciones en declaraciones SQL y las ejecuta en una base de datos.

Ahora, esta conversión solo ocurre para miembros de entidades, inicializadores y propiedades de navegación de entidades. Entonces, para lograr la función u obtener la comparación de propiedades, primero debemos convertirlos en un listado en memoria y luego aplicar la función para recuperar datos.

Por lo tanto, en totalidad,

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);
T Gupta
fuente
21
Sugeriría que pedirle a alguien que haga un toList () en los pedidos es peligroso, ya que significaría recuperar la lista completa
elgrego
Esto es bueno para mí porque mi propiedad problemática está en una función Sum Linq no en la cláusula Where. Por lo tanto, no obtengo datos innecesarios y, en los datos recuperados, estoy haciendo la función Linq Sum que está trabajando en la Lista. ¡Gracias! Lo que puede verse mal al principio puede ser muy útil en determinadas situaciones.
Dov Miller
11

La otra razón probable es que está utilizando IEnumerablepara su propiedad, en lugar deICollection

Entonces en lugar de:

public class This
{
    public long Id { get; set; }
    //...
    public virtual IEnumerable<That> Thats { get; set; }
}

Hacer esto:

public class This
{
    public long Id { get; set; }
    //...
    public virtual ICollection<That> Thats { get; set; }
}

Y estás buenísimo ... es una estupidez perder 2 horas.

Serj Sagan
fuente
0

Me enfrenté a este problema porque tenía una variable miembro con solo get without setpropiedad

eso significa su auto calculatedy not storedcomo una columna enthe table

por lo tanto está not existen eltable schema

de modo make sureque cualquier variable miembro not auto calculateda haveuna gettery setterpropiedades

Basheer AL-MOMANI
fuente
-1

su edmx y el modelo de contexto tienen alguna propiedad diferente que se agregó recientemente a db.

Actualice su EDMX, actualícelo correctamente. Edite su proyecto y vuelva a ejecutarlo.

Resolverá tu problema.

Saludos, Ganesh Nikam

Ganesh Nikam
fuente