Seuls les initialiseurs, les membres d'entité et les propriétés de navigation d'entité sont pris en charge

102

J'obtiens cette exception:

Le membre de type spécifié «Payé» n'est pas pris en charge dans LINQ to Entities. Seuls les initialiseurs, les membres d'entité et les propriétés de navigation d'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 de modèle

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

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

Paiements est une table associée contenant le montant du champ, la requête fonctionne si je supprime la clause Where affichant des informations correctes sur les paiements, un indice quel est le problème avec le 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);
    }
Marc
la source
15
Réponse simple: vous ne pouvez pas utiliser de propriétés non mappées dans les requêtes linq-à-entités! Seules les propriétés mappées sont traduites en SQL.
Ladislav Mrnka

Réponses:

114

L'entité tente de convertir votre propriété payante en SQL et ne peut pas, car elle ne fait pas partie du schéma de table.

Ce que vous pouvez faire est de laisser Entity interroger la table sans filtre payant, puis de filtrer les non payants.

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 signifierait bien sûr que vous rapportiez toutes les données au serveur Web et que vous filtriez les données sur celui-ci. Si vous souhaitez 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.

Eugène S.
la source
25

Juste eu à résoudre un problème similaire. Les solutions ci-dessus nécessitent un traitement en mémoire, ce qui est une mauvaise pratique (chargement différé).

Ma solution était d'écrire un helper qui retournait 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 instruction linq comme suit:

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

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

Koen Luyten
la source
1
Il existe un certain nombre de bibliothèques qui tentent de rendre cette approche plus "intégrée" voir: stackoverflow.com/a/27383641/470183 . Linq-to-entity est limité aux expressions utilisant les «fonctions canoniques» - qui peuvent être transformées en SQL. C # 6 a introduit des «fonctions à corps d'expression» mais ce ne sont pas de véritables lambdas (voir: stackoverflow.com/a/28411444/470183 ). Il serait toujours bon d'avoir cela dans le cadre d'où le WIBNI data.uservoice.com/forums
James Close
1
Merci pour cet exemple simple et concis de Expression<Func<xx,yy>>. Je l'ai déjà compris avant, mais cela semble évident maintenant.
AlexB
17

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

AutoMapper essaie de le sélectionner dans la base de données pendant une projection; et la propriété NotMapped n'existe évidemment pas dans la base de données.

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

  1. Recherchez une [NotMapped]propriété avec un nom Foodans 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 tel est le cas, modifiez la configuration de votre AutoMapper. Ajouter.ForMember(a => a.Foo, b => b.Ignore());
Jess
la source
Dang AutoMapper Projection m'a aussi attrapé, merci pour la réponse!
Chase Florell
15

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

Désormais, cette conversion se produit uniquement pour les membres d'entités, les initialiseurs et les propriétés de navigation d'entité. Donc, pour obtenir une fonction ou une comparaison de propriétés, nous devons d'abord les convertir en liste en mémoire, puis appliquer une fonction pour récupérer les données.

Donc dans la totalité,

var debts = storeDB.Orders.toList()
        .Where(o => o.Paid == false)
        .OrderByDescending(o => o.DateCreated);
T Gupta
la source
21
Je suggérerais que demander à quelqu'un de créer une toList () sur les commandes est dangereux car cela signifierait récupérer la liste entière
elgrego
C'est bon pour moi car ma propriété problématique se trouve dans une fonction Sum Linq et non dans la clause Where. Je ne reçois donc pas de données inutiles et sur les données récupérées, je fais la fonction Linq Sum qui travaille sur la liste. Je vous remercie! Ce qui peut paraître mauvais au début peut être très utile dans certaines situations!
Dov Miller
11

L'autre raison probable est que vous utilisez IEnumerablepour votre propriété au lieu deICollection

Donc au lieu de:

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

Faites ceci:

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

Et vous êtes dory hunky ... chose stupide à perdre 2 heures de plus.

Serj Sagan
la source
2

Cette situation peut également se produire si vous utilisez des types EntityFramework non pris en charge , tels que unsigned int.

C'était mon cas d'une telle erreur.

Consultez plus d'informations sur les types pris en charge: https://msdn.microsoft.com/en-us/library/ee382832(v=vs.100).aspx

Il existe une solution de contournement pour de telles situations, expliquées par GFoley83: Comment utiliser des types int / long non signés avec Entity Framework?

Ony
la source
Ce lien a fait gagner pas mal de temps! Merci beaucoup!
Vladimir Semashkin
0

J'ai rencontré ce problème car j'avais une variable membre avec uniquement une get without setpropriété

cela signifie son auto calculatedet en not storedtant que colonne dansthe table

donc c'est not existdans letable schema

de sorte make sureque toute variable membre not auto calculatedde havea getteret setterpropriétés

Basheer AL-MOMANI
la source
-1

votre edmx et votre modèle de contexte ont des propriétés différentes qui sont nouvellement ajoutées dans db.

Mettez à jour votre EDMX, actualisez-le correctement. Bulidez votre projet et relancez-le.

Cela résoudra votre problème.

Cordialement, Ganesh Nikam

Ganesh Nikam
la source