135 votes

Seuls les constructeurs et initialisateurs sans paramètres sont supportés dans LINQ to Entities.

J'ai cette erreur dans cette expression linq :

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              )).ToList();

Une idée pour résoudre ce problème ? J'ai essayé avec toutes les combinaisons d'expressions... :/

1 votes

Pouvez-vous montrer la classe Payments ? ou au moins le ctor appelé ici, et plus précisément si cet appel de ctor à 8 paramètres peut être remplacé en toute sécurité par un appel de ctor à 0 paramètre et définir 8 propriétés sur l'objet ?

24 votes

J'ai obtenu la même erreur en utilisant un Struct au lieu d'une Class pour l'objet que je "newais".

3 votes

En résumé, EF-LINQ essaie d'envoyer l'instruction select au fournisseur EF, c'est-à-dire de la convertir en SQL. Pour sortir de EF-LINQ, appelez ToList() avant toute création d'objet.

129voto

James Manning Points 7989

Sans plus d'informations sur les 'Paiements', cela ne nous aide pas beaucoup, mais en supposant que vous voulez créer un objet 'Paiements' et définir certaines de ses propriétés en fonction des valeurs des colonnes :

var naleznosci = (from nalTmp in db.Naleznosci
                              where nalTmp.idDziecko == idDziec
                              select new Payments
                              {
                                  Imie = nalTmp.Dziecko.Imie,
                                  Nazwisko = nalTmp.Dziecko.Nazwisko,
                                  Nazwa= nalTmp.Miesiace.Nazwa,
                                  Kwota = nalTmp.Kwota,
                                  NazwaRodzajuOplaty = nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  NazwaTypuOplaty = nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  DataRozliczenia = nalTmp.DataRozliczenia,
                                  TerminPlatnosci = nalTmp.TerminPlatnosci,
                              }).ToList();

10 votes

Cela fonctionne bien, n'oubliez pas d'ajouter un constructeur vide pour la classe.

58 votes

Juste pour ajouter à cette réponse, vous ne pouvez pas faire cela avec les Structs, seulement avec les Classes - il m'a fallu un peu de temps pour comprendre cela !

4 votes

Oui, je pense que la réponse de Tony est meilleure que celle-ci car elle résout réellement le problème immédiat, alors que celle-ci contourne le problème en changeant la nature de la classe Payments et en l'empêchant éventuellement d'être immuable.

117voto

Tony Points 332

Si vous voulez toujours utiliser votre constructeur pour l'initialisation et non les propriétés (parfois ce comportement est souhaité à des fins d'initialisation), énumérer la requête en appelant ToList() o ToArray() et ensuite utiliser Select(…) . Ainsi, il utilisera LINQ to Collections et la limitation de ne pas pouvoir appeler le constructeur avec des paramètres dans la base de données. Select(…) disparaîtra.

Votre code devrait donc ressembler à quelque chose comme ceci :

var naleznosci = db.Naleznosci
                          .Where(nalTmp => nalTmp.idDziecko == idDziec)
                          .ToList() // Here comes transfer to LINQ to Collections.
                          .Select(nalImp => new Payments
                              (
                                  nalTmp.Dziecko.Imie,
                                  nalTmp.Dziecko.Nazwisko,
                                  nalTmp.Miesiace.Nazwa,
                                  nalTmp.Kwota,
                                  nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                  nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                  nalTmp.DataRozliczenia,
                                  nalTmp.TerminPlatnosci
                              ))
                          .ToList();

0 votes

C'est une excellente réponse. En général, j'aime créer des constructeurs de type copie-constructeur pour éviter une longue liste d'affectation de propriétés et pour promouvoir la réutilisation du code. Avant de voir cette réponse, j'étais forcé d'utiliser l'approche d'assignation de propriété et je la déteste vraiment. Merci !

23 votes

Pour clarifier pourquoi cela fonctionne, le problème avec le code original est qu'Entity Framework tente de passer l'appel du constructeur au SQL avec le reste de la requête LINQ, et bien sûr il n'y a aucune façon pour le SQL de construire des objets complexes ! En insérant l'appel ToList(), vous faites passer l'énumérable d'une requête SQL non encore exécutée à une liste concrète d'objets en mémoire, que vous pouvez ensuite manipuler comme bon vous semble.

21 votes

N'utilisez pas ToX() pour cela, utilisez AsEnumerable() .

47voto

Gene C Points 1042

Ayant moi-même rencontré cette erreur, j'ai pensé ajouter que si l'option Payment est un struct vous rencontrerez également la même erreur car struct ne prennent pas en charge les constructeurs sans paramètre.

Dans ce cas, la conversion Payment à une classe et en utilisant la syntaxe de l'initialisateur d'objet résoudra le problème.

0 votes

Cela résout le problème pour moi. En fait, cette requête avec le sélecteur struct est supportée dans LINQ-2-SQL et c'est un problème lors de la mise à jour vers EntityFramework.

0 votes

Je déteste les structures. Ils ne finissent jamais par faire ce que je veux

0 votes

Création d'un DateTime (qui est une structure) dans ma requête, ce qui donne lieu à la même erreur. L'extraire dans une variable locale a permis de résoudre le problème. Merci pour le conseil sur les structures.

23voto

Justin Helgerson Points 8052

Si vous êtes comme moi et que vous ne voulez pas avoir à remplir vos propriétés pour chaque requête que vous construisez, il existe un autre moyen de résoudre ce problème.

var query = from orderDetail in context.OrderDetails
            join order in context.Orders on order.OrderId equals orderDetail.orderId
            select new { order, orderDetail };

À ce stade, vous avez un IQueryable contenant un objet anonyme. Si vous voulez remplir votre objet personnalisé avec un constructeur, vous pouvez simplement faire quelque chose comme ceci :

return query.ToList().Select(r => new OrderDetails(r.order, r.orderDetail));

Maintenant, votre objet personnalisé (qui prend deux objets en paramètre) peut remplir vos propriétés selon vos besoins.

0 votes

Cela a fonctionné pour moi et s'est avéré être la solution la plus propre. Ceux qui ont suggéré d'éliminer le constructeur et d'utiliser la syntaxe de l'initialisateur ne devaient pas avoir de logique dans le constructeur. C'est la seule fois où je m'appuie sur les constructeurs pour remplir les propriétés d'un objet. Merci de partager.

3voto

m-r Tarakanoff Points 23

Vous pouvez essayer de faire la même chose, mais en utilisant les méthodes d'extension. Quel est l'usage du fournisseur de la base de données ?

var naleznosci = db.Naleznosci
                          .Where<TSource>(nalTmp => nalTmp.idDziecko == idDziec)
                          .Select<TSource, TResult>(
                             delegate(TSource nalTmp) { return new Payments
                             (
                                 nalTmp.Dziecko.Imie,
                                 nalTmp.Dziecko.Nazwisko,
                                 nalTmp.Miesiace.Nazwa,
                                 nalTmp.Kwota,
                                 nalTmp.RodzajeOplat.NazwaRodzajuOplaty,
                                 nalTmp.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
                                 nalTmp.DataRozliczenia,
                                 nalTmp.TerminPlatnosci
                             ); })
                          .ToList();

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