102 votes

Seuls les initialisateurs, les membres de l'entité et les propriétés de navigation de l'entité sont pris en charge.

J'obtiens cette exception :

Le type de membre spécifié 'Paid' n'est pas supporté par LINQ to Entities. Seuls les initialisateurs, les membres de l'entité et les propriétés de navigation de l'entité sont pris en charge.

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

        return View(debts);
    }

Ma classe Modèle

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

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

La requête fonctionne si je supprime la clause "Where" et affiche des informations correctes sur les paiements. Avez-vous une idée de ce qui ne va pas dans ce code ?

Résolu comme la réponse suggérée avec :

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

        return View(debts);
    }

15 votes

Réponse simple : Vous ne pouvez pas utiliser les propriétés non mappées dans les requêtes linq-to-entities ! Seules les propriétés mappées sont traduites en SQL.

115voto

Eugene S. Points 1765

Entity essaie de convertir votre propriété Paid en SQL et n'y parvient pas car elle ne fait pas partie du schéma de la table.

Ce que vous pouvez faire, c'est laisser Entity interroger la table avec le filtre "non payé", puis filtrer les "non payés".

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);
}

Cela signifie, bien sûr, que vous ramenez toutes les données au serveur web et que vous les filtrez. Si vous voulez filtrer sur le serveur de base de données, vous pouvez créer une colonne calculée sur la table ou utiliser une procédure stockée.

26voto

Koen Luyten Points 263

J'ai dû résoudre un problème similaire. Les solutions ci-dessus nécessitent un traitement en mémoire, ce qui est une mauvaise pratique (chargement paresseux).

Ma solution a été d'écrire une aide qui renvoie un prédicat :

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

Vous pouvez réécrire votre déclaration linq comme :

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

C'est pratique lorsque vous voulez réutiliser la logique de calcul (DRY). L'inconvénient est que la logique n'est pas dans votre modèle de domaine.

17voto

Jess Points 2039

Ce problème peut également provenir d'un [NotMapped] qui a le même nom dans votre modèle de base de données et votre modèle de vue.

AutoMapper essaie de la sélectionner dans la base de données lors d'une projection, et la propriété NotMapped n'existe évidemment pas dans la base de données.

La solution consiste à Ignore la propriété dans la configuration de l'AutoMapper lors du mappage du modèle de base de données vers le modèle de vue.

  1. Recherchez un [NotMapped] propriété avec le nom Foo dans votre modèle de base de données.
  2. Recherchez une propriété portant le même nom, Foo dans votre modèle de vue.
  3. Si c'est le cas, modifiez votre configuration AutoMapper. Ajouter .ForMember(a => a.Foo, b => b.Ignore());

15voto

T Gupta Points 209

Linq convertit les déclarations en déclarations SQL et les exécute dans la base de données.

Désormais, cette conversion ne se produit que pour les membres des entités, les initialisateurs et les propriétés de navigation des entités. Ainsi, pour réaliser la comparaison de la fonction ou de la propriété get, nous devons d'abord les convertir en liste en mémoire, puis appliquer la fonction pour récupérer les données.

Donc en totalité,

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);

22 votes

Je pense que demander à quelqu'un de faire un toList() sur les commandes est dangereux car cela signifierait récupérer la liste entière.

0 votes

C'est une bonne chose pour moi, car la propriété qui me pose problème se trouve dans une fonction Sum Linq et non dans la clause Where. Ainsi, je n'obtiens pas de données inutiles et sur les données récupérées, je fais la fonction Linq Sum qui fonctionne sur la liste. Je vous remercie ! Ce qui peut sembler mauvais au début peut être très utile dans certaines situations !

11voto

Serj Sagan Points 2731

L'autre raison probable est que vous utilisez IEnumerable pour votre propriété, au lieu de ICollection

Donc, au lieu de :

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

Faites-le :

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

Et tout va bien... C'est stupide de perdre 2 heures pour ça.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X