webentwicklung-frage-antwort-db.com.de

In LINQ to Entities werden nur parameterlose Konstruktoren und Initialisierer unterstützt

Ich habe diesen Fehler in diesem Linq-Ausdruck:

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

Irgendeine Idee, wie man dieses Problem löst? Ich versuche es mit einer beliebigen Kombination von Ausdrücken ...: /

115
netmajor

ohne weitere Informationen zu 'Payments' ist dies nicht viel hilfreich, vorausgesetzt, Sie möchten ein Payments-Objekt erstellen und einige seiner Eigenschaften basierend auf Spaltenwerten festlegen:

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

Wenn Sie Ihren Konstruktor weiterhin für die Initialisierung und nicht für Eigenschaften verwenden möchten (manchmal ist dieses Verhalten zu Initialisierungszwecken erwünscht), listen Sie die Abfrage auf, indem Sie ToList() oder ToArray() aufrufen und dann Select(…) verwenden. Daher wird LINQ to Collections verwendet, und die Einschränkung, Konstruktor nicht mit Parametern in Select(…) aufrufen zu können, wird verschwinden.

Ihr Code sollte also etwa so aussehen:

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

Nachdem ich gerade auf diesen Fehler gestoßen bin, dachte ich, ich würde hinzufügen, dass, wenn der Payment-Typ eine struct ist, derselbe Fehler auch auftritt, da struct-Typen keine parameterlosen Konstruktoren unterstützen.

In diesem Fall lösen Sie das Problem, indem Sie Payment in eine Klasse konvertieren und die Objektinitialisiersyntax verwenden.

46
Gene C

Wenn Sie wie ich sind und nicht Ihre Eigenschaften für jede Abfrage, die Sie erstellen, ausfüllen möchten, gibt es eine andere Möglichkeit, dieses Problem zu lösen.

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

Zu diesem Zeitpunkt haben Sie ein IQueryable, das ein anonymes Objekt enthält. Wenn Sie Ihr benutzerdefiniertes Objekt mit einem Konstruktor füllen möchten, können Sie einfach Folgendes tun:

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

Jetzt kann Ihr benutzerdefiniertes Objekt (das zwei Objekte als Parameter verwendet) Ihre Eigenschaften nach Bedarf auffüllen.

17

Zuerst würde ich die Lösung mit vermeiden 

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

Dies erfordert einen leeren Konstruktor und ignoriert die Kapselung, sodass Sie sagen, dass es sich bei payments () um eine gültige Zahlung ohne Daten handelt. Stattdessen muss das Objekt mindestens einen Wert und möglicherweise andere erforderliche Felder enthalten, je nach Ihrer Domäne.

Es ist besser, einen Konstruktor für die erforderlichen Felder zu haben, aber nur die erforderlichen Daten mitzubringen:

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

Nur ToList() die DbSet vor der Select-Anweisung .. Die tatsächliche DbSet wird als Abfrage gespeichert. Sie ist noch nicht erfüllt. Nach dem Aufruf von ToList() spielen Sie mit Objekten, und Sie können einen nicht standardmäßigen Konstruktor verwenden in der Abfrage. 

Nicht die effizienteste Art der Nutzungszeit, aber es ist eine Option für kleine Sets.

2
eiran

Sie können dasselbe versuchen, jedoch die Erweiterungsmethoden verwenden. Was ist der Anbieter der Datenbanknutzung?

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

Ich hatte heute das gleiche Problem und meine Lösung war ähnlich wie bei Yoda, jedoch funktioniert es nur mit fließender Syntax.

Anpassung meiner Lösung an Ihren Code: Ich habe der Objektklasse die folgende statische Methode hinzugefügt

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

und dann die Basisabfrage folgendermaßen aktualisiert:

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

Dies entspricht logisch der Lösung von James Manning, mit dem Vorteil, dass die Initialisierung der Mitglieder auf das Klassen-/Datenübertragungsobjekt aufgebläht wird

Hinweis: Ursprünglich hatte ich eher beschreibende Namen als "Initializer" Verwendet, aber nachdem ich mir die Verwendung des Namens angesehen hatte, stellte sich heraus, dass "Initilizer" (zumindest für meine Zwecke) ausreichend war.

Schlussnote:
Nachdem ich diese Lösung gefunden hatte, dachte ich ursprünglich, es wäre einfach, den gleichen Code zu teilen und diesen anzupassen, um auch für die Abfragesyntax zu funktionieren. Ich glaube nicht mehr, dass dies der Fall ist. Ich denke, wenn Sie diese Art von Abkürzungskonstruktion verwenden möchten, benötigen Sie eine Methode für jede (Abfrage, fließend) flüssig wie oben beschrieben, die in der Objektklasse selbst vorhanden sein kann.

Für die Abfragesyntax wäre eine Erweiterungsmethode (oder eine Methode außerhalb der verwendeten Basisklasse) erforderlich. (da die Abfragesyntax eher ein IQueryable als ein T betreiben möchte)

Hier ist ein Beispiel von dem, was ich verwendet habe, um dies schließlich für die Abfragesyntax zu erreichen. (Yoda hat dies bereits genagelt, aber ich denke, die Verwendung könnte klarer sein, weil ich es zuerst nicht verstanden habe.)

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

und die Verwendung

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

Tut mir leid, dass ich zu spät auf die Party gekommen bin, aber nachdem ich this gefunden hatte, dachte ich, dass dies geteilt werden sollte, da es die sauberste, schnellste und auch speichersparendste Implementierung ist, die ich finden kann.

Passend zu Ihrem Beispiel würden Sie schreiben:

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

Die großen Vorteile hier (wie Damien Guard in den Kommentaren unter dem Link hervorgehoben hat) sind:

  • Schützt Sie vor der Verwendung des Initialisierungsmusters bei jedem Auftreten.
  • Nutzung über var foo = createPayments(bar); sowie Nutzung über myIQueryable.ToPayments () möglich.
1
Yoda

ja, probier es so aus ....

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

dadurch wird Ihr Payment-Objekt mithilfe eines parameterlosen Konstruktors neu erstellt. Anschließend werden die Eigenschaften initialisiert, die in den geschweiften Klammern aufgeführt sind. { }

1
Muad'Dib

Wenn Sie einen Konstruktor mit mehreren Objekten zum Initialisieren verwenden möchten, erhalten Sie möglicherweise eine Fehlermeldung, wenn von Linq keine Werte zurückgegeben werden.

Vielleicht möchten Sie so etwas tun:

(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

Zusätzlich zu den oben genannten Methoden können Sie es auch als Enumerable-Sammlung analysieren, z.

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

Dies hat auch den zusätzlichen Vorteil, dass das Erstellen eines anonymen Objekts wie folgt einfacher wird:

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

Wenn Sie sich jedoch daran erinnern, dass das Parsen einer Sammlung als Enumerable diese in den Speicher zieht, kann dies ressourcenintensiv sein! Hier ist Vorsicht geboten.

0
XtraSimplicity

Obwohl es spät ist zu antworten, könnte es dennoch jemandem in Not helfen. Da LINQ to-Entitäten die parameterlosen Objektkonstruktionen nicht unterstützen. Die Projektionsmethoden für IEnumerable.

Konvertieren Sie also vor der Auswahl einfach Ihre IQueryable zu IEnumerable mit diesem Code:

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

Es wird gut funktionieren. Es wird jedoch natürlich die Vorteile von systemeigenen Abfragen verlieren.

0
arslanahmad656