Seuls les constructeurs et initialiseurs sans paramètre sont pris en charge dans LINQ to Entities

132

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 comment résoudre ce problème? J'essaye avec n'importe quelle combinaison d'expression ...: /

netmajor
la source
1
pouvez-vous afficher la classe Paiements? ou au moins le ctor qui est appelé ici, et plus précisément si cet appel à 8 paramètres peut être échangé en toute sécurité contre un appel à 0 paramètre et définir 8 propriétés sur l'objet?
James Manning
23
J'ai eu cette même erreur lors de l'utilisation d'un Struct au lieu d'une classe pour l'objet que je «nouveauté».
HuckIt le
3
TL; DR chose est que EF-LINQ essaie d'envoyer l'instruction select au fournisseur EF, c'est-à-dire. le convertir en SQL. Pour sortir d'EF-LINQ, appelez ToList () avant toute création d'objet.

Réponses:

127

sans plus d'informations sur les `` paiements '', cela n'aide pas beaucoup, mais en supposant que vous souhaitiez créer un objet Payments et définir certaines de ses propriétés en fonction des valeurs de colonne:

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();
James Manning
la source
10
Cela fonctionne très bien, n'oubliez pas d'ajouter un constructeur vide pour la classe.
live-love
58
Juste pour ajouter à cette réponse, vous ne pouvez pas faire cela avec Structs, seulement les classes - m'a pris un peu de temps pour comprendre cela!
naspinski
4
Oui, je pense que la réponse de Tony est meilleure que celle-ci car elle résout en fait 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 peut-être d'être immuable.
Stephen Holt
cela a l'air affreux. Une meilleure façon avec EF6?
Boîte à outils
115

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

Donc, votre code devrait ressembler à 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();
Tony
la source
21
Juste pour clarifier pourquoi cela fonctionne, le problème avec le code initialement déclaré est qu'Entity Framework tente de transmettre l'appel du constructeur au SQL avec le reste de la requête LINQ, et bien sûr, il n'y a aucun moyen pour SQL de construire objets complexes! En insérant l'appel ToList (), vous déplacez l'énumérable d'une requête SQL non encore exécutée vers une liste concrète d'objets en mémoire, que vous pouvez ensuite manipuler comme vous le souhaitez.
Stephen Holt
19
N'utilisez pas ToX()pour cela, utilisez AsEnumerable().
Rawling
1
.ToList () // Voici le transfert vers LINQ vers Collections. est la ligne qui résout le problème pour moi.
Ram
15
Sachez que cela sélectionnera toutes les colonnes au niveau de la base de données alors que normalement, il ne sélectionnera que les colonnes requises
Hugh Jeffner
4
Non seulement cela, mais vous allez probablement avoir plusieurs énumérations. Je n'aime pas cette solution.
Bluebaron le
47

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

Dans ce cas, la conversion Paymenten classe et l'utilisation de la syntaxe d'initialisation d'objet résoudront le problème.

Gène C
la source
Cela résout le problème de moi. En fait, cette requête avec le sélecteur de structure est prise en charge dans LINQ-2-SQL et c'est un problème lorsque vous mettez à niveau vers EntityFramework.
Tomas Kubes
Je déteste les structs. Ils ne finissent jamais par faire ce que je veux
Simon_Weaver
DateTimeJ'ai créé un (qui est une structure) dans ma requête, ce qui entraîne la même erreur. l'extraction vers une variable locale l'a corrigé pour moi. Merci pour l'indice struct.
LuckyLikey
20

Si vous êtes comme moi et que vous ne souhaitez pas avoir à renseigner vos propriétés pour chaque requête que vous créez, 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 disposez d'un IQueryable contenant un objet anonyme. Si vous souhaitez 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 comme paramètre) peut remplir vos propriétés selon vos besoins.

Justin Helgerson
la source
Cela a fonctionné pour moi et est devenu la solution la plus propre. Ceux qui ont suggéré d'éliminer le constructeur et d'utiliser la syntaxe d'initialisation ne doivent pas avoir eu de logique dans le constructeur. C'est la seule fois où je m'appuie sur les constructeurs pour renseigner les propriétés d'un objet. Merci pour le partage.
Bonez024
9

J'éviterais d'abord la solution avec

from ....
select new Payments
{
  Imie = nalTmp.Dziecko.Imie,
  ....
}

Cela nécessite un constructeur vide et ignore l'encapsulation, vous dites donc que new Payments () est un paiement valide sans aucune donnée, mais à la place, l'objet doit avoir au moins une valeur et probablement d'autres champs obligatoires en fonction de votre domaine.

Il est préférable d'avoir un constructeur pour les champs obligatoires mais de n'apporter que les données nécessaires:

from ....
select new
{
  Imie = nalTmp.Dziecko.Imie,
  Nazwisko = nalTmp.Dziecko.Nazwisko
  ....
}
.ToList() // Here comes transfer to LINQ to Collections.
.Select(nalImp => new Payments
 (
  nalTmp.Imie,//assume this is a required field
  ...........
  )
  {
     Nazwisko = nalTmp.Nazwisko //optional field
  })
.ToList();
Eugen
la source
C'est le moindre mal.
Chalky
Je préfère aussi quelque chose comme ça. J'essayais d'utiliser Tuple mais Tuple n'a pas de paramètre sans constructeur. J'ai rempli un objet anonyme, puis sélectionnez Tuple.
Tchaps
un pour embrasser l'encapsulation et le domaine
inrandomwetrust
2

Vous pouvez essayer de faire de même, mais en utilisant les méthodes d'extension. Quel est le fournisseur de l'utilisation 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();
monsieur Tarakanoff
la source
2

Juste ToList()l' DbSetavant la Selectdéclaration .. le réel DbSetest enregistré comme une requête, ce n'est pas remplie encore. Après avoir appelé, ToList()vous jouez avec des objets, puis vous pouvez utiliser un constructeur autre que celui par défaut dans la requête.

Ce n'est pas le moyen le plus efficace en termes de temps d'utilisation, mais c'est une option sur les petits ensembles.

Eiran
la source
1

oui, essayez-le comme ça ...

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

cela réinitialisera votre objet de paiement à l'aide d'un constructeur sans paramètre, puis initialisera les propriétés répertoriées à l'intérieur des accolades. { }

Muad'Dib
la source
3
FYI the ()in the Payemnts n'est pas nécessaire, donc il peut être `select new Payments {// init values}
PostMan
maintenant, j'ai une erreur: Impossible d'initialiser le type 'Paiements' avec un initialiseur de collection car il
n'implémente
right - si vous créiez un type anon (au lieu d'une instance de la classe Payments), le code de Muad conviendrait car les propriétés à définir seraient implicitement les noms des propriétés lues. Comme il s'agit d'une classe «réelle», cependant, vous devrez spécifier les propriétés à définir sur les différentes valeurs.
James Manning
1

En plus des méthodes susmentionnées, vous pouvez également l'analyser en tant que collection Enumerable, comme ceci:

(from x in table
....
).AsEnumerable()
.Select(x => ...)

Cela a également l'avantage supplémentaire de rendre la vie plus facile lors de la construction d'un objet anonyme, comme ceci:

 (from x in tableName
select x.obj)
.Where(x => x.id != null)
.AsEnumerable()
.Select(x => new {
   objectOne = new ObjectName(x.property1, x.property2),
   parentObj = x
})
.ToList();

Rappelez-vous, cependant, que l'analyse d'une collection comme Enumerable la tire en mémoire, donc elle peut être gourmande en ressources! La prudence doit être utilisée ici.

XtraSimplicité
la source
1

De plus, si vous souhaitez utiliser un constructeur avec plusieurs objets à initialiser, vous risquez d'obtenir une erreur si aucune valeur n'est renvoyée par Linq.

Vous voudrez peut-être faire quelque chose comme ceci:

(from x in table_1
   join y in table_2
   on x.id equals y.id
   select new {
   val1 = x,
   val2 = y
})
.DefaultIfEmpty()
.ToList()
.Select(a => new Val_Constructor(a.val1 != null ? a.val1 : new Val_1_Constructor(),
                            a.val2 != null ? a.val2 : new Val_2_Constructor()))
.ToList();
Mahesh
la source
1

Désolé d'être en retard à la fête, mais après avoir trouvé cela , j'ai pensé que cela devrait être partagé car c'est l'implémentation la plus propre, la plus rapide et la plus économiseuse de mémoire que j'ai pu trouver.

Adapté à votre exemple, vous écririez:

public static IQueryable<Payments> ToPayments(this IQueryable<Naleznosci> source)
{
  Expression<Func<Naleznosci, Payments>> createPayments = naleznosci => new Payments
  {
    Imie = source.Dziecko.Imie,
    Nazwisko = source.Dziecko.Nazwisko,
    Nazwa= source.Miesiace.Nazwa,
    Kwota = source.Kwota,
    NazwaRodzajuOplaty = source.RodzajeOplat.NazwaRodzajuOplaty,
    NazwaTypuOplaty = source.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
    DataRozliczenia = source.DataRozliczenia,
    TerminPlatnosci = source.TerminPlatnosci,
  };

  return source.Select(createPayments);
}

Les grands avantages ici (comme l'a souligné Damien Guard dans les commentaires sur le lien) sont:

  • Vous évite d'utiliser le modèle d'initialisation à chaque occurrence.
  • Utilisation via var foo = createPayments(bar);ainsi que via myIQueryable.ToPayments () possible.
Yoda
la source
1

J'ai eu le même problème aujourd'hui et ma solution était similaire à celle répertoriée par Yoda, mais elle ne fonctionne qu'avec une syntaxe fluide.

Adapter ma solution à votre code: j'ai ajouté la méthode statique suivante à la classe d'objets

    /// <summary>
    /// use this instead of a parameritized constructor when you need support
    /// for LINQ to entities (fluent syntax only)
    /// </summary>
    /// <returns></returns>
    public static Func<Naleznosci, Payments> Initializer()
    {
        return n => new Payments
        {
             Imie = n.Dziecko.Imie,
             Nazwisko = n.Dziecko.Nazwisko,
             Nazwa = n.Miesiace.Nazwa,
             Kwota = n.Kwota,
             NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
             NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
             DataRozliczenia = n.DataRozliczenia,
             TerminPlatnosc = n.TerminPlatnosci
        };
    }

puis a mis à jour la requête de base comme suit:

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select new Payments.Initializer());

Ceci est logiquement équivalent à la solution de James Manning avec l'avantage de pousser le gonflement de l'initialisation des membres vers l'objet de transfert de classe / données

Remarque: À l'origine, j'utilisais des noms plus descriptifs que "Initializer" mais après avoir examiné comment je l'utilisais, j'ai trouvé que "Initilizer" était suffisant (du moins pour mes besoins).

Note finale:
Après avoir proposé cette solution, je pensais à l'origine qu'il serait simple de partager le même code et de l'adapter pour qu'il fonctionne également pour la syntaxe de requête. Je ne pense plus que ce soit le cas. Je pense que si vous voulez pouvoir utiliser ce type de construction abrégée, vous auriez besoin d'une méthode pour chaque (requête, fluent) fluide comme décrit ci-dessus qui peut exister dans la classe d'objets elle-même.

Pour la syntaxe de requête, une méthode d'extension (ou une méthode en dehors de la classe de base utilisée) serait requise. (puisque la syntaxe de requête veut faire fonctionner un IQueryable plutôt que T)

Voici un exemple de ce que j'ai utilisé pour que cela fonctionne enfin pour la syntaxe de requête. (Yoda l'a déjà cloué mais je pense que l'utilisation pourrait être plus claire car je ne l'ai pas compris au début)

/// <summary>
/// use this instead of a parameritized constructor when you need support
/// for LINQ to entities (query syntax only)
/// </summary>
/// <returns></returns>
public static IQueryable<Payments> Initializer(this IQueryable<Naleznosci> source)
{
    return source.Select(
        n => new Payments
        {
            Imie = n.Dziecko.Imie,
            Nazwisko = n.Dziecko.Nazwisko,
            Nazwa = n.Miesiace.Nazwa,
            Kwota = n.Kwota,
            NazwaRodzajuOplaty = n.RodzajeOplat.NazwaRodzajuOplaty,
            NazwaTypuOplaty = n.RodzajeOplat.TypyOplat.NazwaTypuOplaty,
            DataRozliczenia = n.DataRozliczenia,
            TerminPlatnosc = n.TerminPlatnosci
    };
}

et l'utilisation

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();
wode
la source
ajouté une section concernant la syntaxe de requête pour l'exhaustivité lorsque j'ai réalisé que ma réponse initiale ne s'étalait pas bien. La réponse de @ yoda est probablement meilleure en ce qui concerne la syntaxe des requêtes.
wode
0

Bien qu'il soit tard pour répondre, cela pourrait encore aider une personne en détresse. Depuis LINQ aux entités ne prend pas en charge les constructions d'objets sans paramètre. Cependant, les méthodes de projection pour IEnumerable .

Donc, avant la sélection, convertissez simplement votre IQueryable en IEnumerable en utilisant ce code:

var result = myContext.SomeModelClass.AsEnumerable().Select(m => m.ToString());

Cela fonctionnera très bien. Cependant, il perdra bien sûr les avantages des requêtes natives.

arslanahmad656
la source
0
IQueryable<SqlResult> 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,
                          });
Repeater1.DataSource  = naleznosci.ToList(); 
Repeater1.DataBind();


public class SqlResult
{
        public string Imie { get; set; }
        public string Nazwisko { get; set; }
        ...
}
Jair Marques
la source