web-dev-qa-db-fra.com

Seuls les constructeurs et les initialiseurs sans paramètre sont pris en charge 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 de comment résoudre ce problème? J'essaye avec n'importe quelle combinaison d'expression ...: /

115
netmajor

sans plus d'informations sur 'Paiements', cela n'aidera pas grand chose, mais si vous voulez 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();
112
James Manning

Si vous souhaitez toujours utiliser votre constructeur pour l'initialisation et non des 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 l'impossibilité d'appeler un constructeur avec des paramètres dans Select(…) disparaîtra.

Donc, votre code devrait ressembler à quelque chose comme ça:

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();
105
Tony

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

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

46
Gene C

Si vous êtes comme moi et ne voulez 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 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));

Désormais, votre objet personnalisé (qui prend deux objets en paramètre) peut renseigner vos propriétés à votre guise.

17
Justin Helgerson

D'abord, j'éviterais la solution avec 

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

Cela nécessite un constructeur vide et ignore l'encapsulation. Par conséquent, vous indiquez que new Payments () est un paiement valide sans données, mais l'objet doit au moins comporter une valeur et probablement d'autres champs obligatoires, selon votre domaine.

Il est préférable d'avoir un constructeur pour les champs obligatoires mais d'apporter uniquement 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();
7
eugen

Juste ToList() la DbSet avant l'instruction Select .. La DbSet réelle est enregistrée en tant que requête, elle n'est pas encore remplie .. Après avoir appelé ToList(), vous jouez avec des objets et 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 pour les petits ensembles.

2
eiran

Vous pouvez essayer de faire la même chose, mais en utilisant les méthodes d’extension. Quel est le fournisseur de la base de données utilisée?

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();
1
m-r Tarakanoff

J'ai eu le même problème aujourd'hui et ma solution était similaire à celle décrite par Yoda, mais elle ne fonctionne que dans une syntaxe fluide.

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

    /// <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 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 bloc d'initialisation de membre 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 à la syntaxe Query. Je ne crois plus que ce soit le cas. Je pense que si vous voulez pouvoir utiliser ce type de construction abrégée, vous aurez besoin d'une méthode pour chaque fluent (requête, fluent) tel que décrit ci-dessus, pouvant exister dans la classe d'objet 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 la requête veut utiliser un IQueryable plutôt que T)

Voici un exemple de ce que j’avais utilisé pour que cela fonctionne enfin avec 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 eu 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'usage

var naleznosci = (from nalTmp in db.Naleznosci
    where nalTmp.idDziecko == idDziec
    select nalTmp).Initializer().ToList();
1
wode

Désolé d’être en retard pour la soirée, mais après avoir trouvé ceci , j’ai pensé que cela devrait être partagé, car c’est la mise en œuvre la plus propre, la plus rapide et la plus économisante en mémoire que je puisse 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 gros avantages ici (comme Damien Guard l'a souligné dans les commentaires sur le lien) sont les suivants:

  • Vous empêche d'utiliser le modèle d'initialisation à chaque occurrence.
  • L'utilisation via var foo = createPayments(bar); ainsi que l'utilisation via myIQueryable.ToPayments () sont possibles.
1
Yoda

yeh, essayez 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 créera votre objet Paiement à l'aide d'un constructeur sans paramètre, puis initialisera les propriétés répertoriées à l'intérieur des accolades { }

1
Muad'Dib

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.

Donc, 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();
1
Mahesh

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

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

Cela présente également l'avantage de rendre la vie plus facile lors de la création 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();

Cependant, vous vous souviendrez qu'analyser une collection en tant que Enumerable la récupère en mémoire et nécessite donc beaucoup de ressources! La prudence devrait être utilisée ici.

0
XtraSimplicity

Même s'il est tard pour répondre, cela pourrait quand même aider une personne en détresse. Étant donné que LINQ to entity ne prend pas en charge les constructions d'objet sans paramètre. Cependant, les méthodes de projection pour IEnumerable.

Donc, avant la sélection, il suffit de convertir votre IQueryable à IEnumerable en utilisant ce code:

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

Cela fonctionnera bien. Cependant, il perdra bien entendu les avantages des requêtes natives.

0
arslanahmad656