In LINQ to Entities sono supportati solo costruttori e inizializzatori senza parametri


132

Ho questo errore in questa espressione 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();

Qualche idea su come risolvere questo problema? Provo con qualsiasi combinazione di espressione ...: /


1
puoi mostrare la classe Pagamenti? o almeno il ctor viene chiamato qui, e in particolare se quella chiamata a 8 parametri può essere scambiata in modo sicuro con una chiamata a 0 parametri e impostando 8 proprietà sull'oggetto?
James Manning,

23
Ho avuto questo stesso errore quando ho usato un Struct invece di una classe per l'oggetto che stavo "rinnovando".
HuckIt,

3
TL; DR cosa è che EF-LINQ sta provando a inviare l'istruzione select al fornitore EF, ad es. convertilo in SQL. Per uscire da EF-LINQ, chiamare ToList () prima di creare qualsiasi oggetto.

Risposte:


127

senza ulteriori informazioni su "Pagamenti" questo non aiuta molto, ma supponendo che tu voglia creare un oggetto Pagamenti e impostarne alcune proprietà in base ai valori della colonna:

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
Funziona alla grande, non dimenticare di aggiungere un costruttore vuoto per la classe.
live-love

58
Solo per aggiungere a questa risposta, non puoi farlo con Struct, solo Classi - mi ci è voluto un po 'per capirlo!
naspinski,

4
Sì, penso che la risposta di Tony sia migliore di questa perché risolve effettivamente il problema immediato, mentre questo elude il problema cambiando la natura della classe Payments e forse impedendogli di essere immutabile.
Stephen Holt,

questo sembra brutto af. Un modo migliore con EF6?
Toolkit

115

Se si desidera ancora utilizzare il costruttore per l'inizializzazione e non le proprietà (a volte questo comportamento è desiderato ai fini dell'inizializzazione), enumerare la query chiamando ToList()o ToArray(), quindi utilizzare Select(…). Quindi utilizzerà LINQ to Collections e tale limitazione di non poter chiamare il costruttore con parametri in Select(…)svanirà.

Quindi il tuo codice dovrebbe assomigliare a questo:

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

21
Solo per chiarire il motivo per cui funziona, il problema con il codice originariamente indicato è che Entity Framework tenta di passare la chiamata del costruttore all'SQL insieme al resto della query LINQ e, naturalmente, non c'è modo per SQL di costruire oggetti complessi! Inserendo la chiamata ToList () si sposta l'enumerabile da una query SQL non ancora eseguita a un elenco concreto di oggetti in memoria, che è quindi possibile manipolare nel modo desiderato.
Stephen Holt,

19
Non usare ToX()per questo, usa AsEnumerable().
Rawling

1
.ToList () // Ecco che arriva il trasferimento a LINQ in Collezioni. è la linea che risolve il problema per me.
Ram

15
Tieni presente che questo selezionerà tutte le colonne a livello di db dove normalmente selezionerà solo le colonne richieste
Hugh Jeffner

4
Non solo, ma probabilmente avrai più enumerazioni. Non mi piace questa soluzione.
Bluebaron,

47

Avendo riscontrato questo errore da solo, ho pensato di aggiungere che se il Paymenttipo è a struct, si riscontrerebbe anche lo stesso errore perchéstruct tipi non supportano i costruttori senza parametri.

In tal caso, la conversione Paymentin una classe e l'utilizzo della sintassi dell'inizializzatore di oggetti risolverà il problema.


Questo risolve il problema da parte mia. In realtà questa query con selettore di struttura è supportata in LINQ-2-SQL ed è un problema durante l'aggiornamento a EntityFramework.
Tomas Kubes,

Odio le strutture. Non finiscono mai per fare quello che voglio
Simon_Weaver,

Creato un DateTime(che è una struttura) all'interno della mia query, che risulta nello stesso errore. l'estrazione in una variabile locale l'ha risolto per me. Grazie per il suggerimento strutt.
LuckyLikey,

20

Se sei come me e non vuoi popolare le tue proprietà per ogni query che stai creando, c'è un altro modo per risolvere questo problema.

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

A questo punto hai un IQueryable contenente un oggetto anonimo. Se vuoi popolare il tuo oggetto personalizzato con un costruttore, puoi semplicemente fare qualcosa del genere:

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

Ora il tuo oggetto personalizzato (che accetta due oggetti come parametro) può popolare le tue proprietà secondo necessità.


Questo ha funzionato per me ed è diventata la soluzione più pulita. Coloro che hanno suggerito di eliminare il costruttore e di utilizzare la sintassi dell'inizializzatore non devono avere una logica all'interno del costruttore. Questa è l'unica volta in cui mi appoggio ai costruttori per popolare le proprietà di un oggetto. Grazie per aver condiviso.
Bonez024,

9

Innanzitutto eviterei la soluzione con

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

Ciò richiede un costruttore vuoto e ignora l'incapsulamento, quindi stai dicendo che New Payments () è un pagamento valido senza dati, ma l'oggetto deve avere almeno un valore e probabilmente altri campi obbligatori a seconda del tuo dominio.

È meglio avere un costruttore per i campi richiesti ma portare solo i dati necessari:

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

Questo è il male minore.
Chalky,

Preferisco anche qualcosa del genere. Stavo provando a usare Tuple ma Tuple non ha un parametro meno costruttore. Ho popolato un oggetto anonimo e quindi ho selezionato Tupla.
Arriva il

uno per abbracciare incapsulamento e dominio
inrandomwetrust

2

Puoi provare a fare lo stesso, ma usando i metodi di estensione. A cosa serve il provider del database?

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

2

Proprio ToList()la DbSetprima che la Selectdichiarazione .. il vero e proprio DbSetviene salvato come una query, non è ancora soddisfatta. Dopo aver chiamato ToList()stai giocando con gli oggetti, quindi puoi utilizzare un costruttore non predefinito nella query.

Non è il modo più efficiente in termini di tempo di utilizzo, ma è un'opzione su piccoli set.


1

yeh, provalo così ....

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

questo rinnoverà l'oggetto Payment utilizzando un costruttore senza parametri, quindi inizializzerà le proprietà elencate tra parentesi graffe { }


3
Cordiali saluti il ()in Payemnts non è necessario, quindi può essere `selezionare nuovi pagamenti {// valori iniziali}
PostMan

ora ho un errore: Impossibile inizializzare il tipo 'Pagamenti' con un inizializzatore di riscossione perché non implementa 'System.Collections.IEnumerable'
netmajor

giusto - se stavi creando un tipo anone (anziché un'istanza della classe Payments), il codice di Muad andrebbe bene poiché le proprietà da impostare sarebbero implicitamente i nomi delle proprietà da leggere. Poiché si tratta di una classe "reale", tuttavia, è necessario specificare quali proprietà impostare sui vari valori.
James Manning,

1

Oltre ai suddetti metodi, puoi anche analizzarlo come una raccolta Enumerable, in questo modo:

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

Ciò ha anche l'ulteriore vantaggio di rendere la vita più semplice quando si costruisce un oggetto anonimo, come questo:

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

Ricordare, tuttavia, che l'analisi di una raccolta come Enumerable la tira in memoria, quindi può richiedere molte risorse! Attenzione dovrebbe essere usata qui.


1

Inoltre, se si desidera utilizzare un costruttore con più oggetti per l'inizializzazione, è possibile che venga visualizzato un errore se Linq non restituisce alcun valore.

Quindi potresti voler fare qualcosa del genere:

(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

Ci scusiamo per il ritardo alla festa, ma dopo aver trovato questo , ho pensato che questo dovrebbe essere condiviso in quanto è l'implementazione più pulita, più veloce e anche per risparmiare memoria che ho trovato.

Adattato al tuo esempio, dovresti scrivere:

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

I grandi vantaggi qui (come ha sottolineato Damien Guard nei commenti al link) sono:

  • Ti impedisce di utilizzare il modello di inizializzazione per ogni occorrenza.
  • Utilizzo tramite var foo = createPayments(bar);e utilizzo tramite myIQueryable.ToPayments () possibile.

1

Ho avuto lo stesso problema oggi e la mia soluzione era simile a quella elencata da Yoda, tuttavia funziona solo con una sintassi fluida.

Adattamento della mia soluzione al tuo codice: ho aggiunto il seguente metodo statico alla classe di oggetti

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

e quindi aggiornata la query di base alla seguente:

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

Ciò è logicamente equivalente alla soluzione di James Manning con il vantaggio di spingere il gonfiore dell'inizializzazione dei membri nell'oggetto Class / Data Transfer

Nota: Inizialmente stavo usando nomi più descrittivi di "Inizializzatore" ma dopo aver esaminato come lo stavo usando, ho scoperto che "Inizializzatore" era sufficiente (almeno per i miei scopi).

Nota finale:
Dopo aver escogitato questa soluzione, inizialmente pensavo che sarebbe stato semplice condividere lo stesso codice e adattarlo per funzionare anche con la sintassi delle query. Non credo più che sia così. Penso che se si desidera essere in grado di utilizzare questo tipo di costruzione abbreviata, sarebbe necessario un metodo per ogni (fluente, fluido) fluente come descritto sopra che può esistere nella classe oggetto stessa.

Per la sintassi della query sarebbe richiesto un metodo di estensione (o un metodo esterno alla classe base). (poiché la sintassi della query vuole operare un IQueryable anziché T)

Ecco un esempio di ciò che ho usato per far finalmente funzionare questo per la sintassi delle query. (Yoda lo ha già risolto, ma penso che l'uso potrebbe essere più chiaro perché all'inizio non l'ho capito)

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

e l'uso

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

aggiunta una sezione relativa alla sintassi della query per completezza quando ho realizzato che la mia risposta iniziale non si estendeva bene. La risposta di @ yoda è probabilmente migliore per quanto riguarda la sintassi delle query.
wod

0

Anche se è tardi per rispondere, potrebbe comunque aiutare qualcuno in difficoltà. Poiché LINQ alle entità non supporta le costruzioni di oggetti senza parametri. Tuttavia, i metodi di proiezione per IEnumerable .

Quindi, prima della selezione, converti IQueryable in IEnumerable usando questo codice:

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

Funzionerà benissimo. Tuttavia, ovviamente, perderanno i benefici delle query native.


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; }
        ...
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.