LINQ to Entities'de yalnızca parametresiz oluşturucular ve başlatıcılar desteklenir


132

Bu linq ifadesinde şu hatayı görüyorum:

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

Bu problemi nasıl çözdüğüne dair bir fikrin var mı? Herhangi bir ifade kombinasyonunu denerim ...: /


1
Payments sınıfını gösterebilir misin? veya en azından burada çağrılan ctor ve özellikle bu 8-param ctor çağrısının bir 0-param ctor çağrısı için güvenli bir şekilde değiştirilip değiştirilemeyeceği ve nesnede 8 özellik ayarlanıp ayarlanamayacağı?
James Manning

23
"Newing" yaptığım nesne için Class yerine Struct kullanırken aynı hatayı aldım.
HuckIt

3
TL; DR meselesi, EF-LINQ'nun select ifadesini EF sağlayıcısına, yani. SQL'e dönüştürün. EF-LINQ'dan çıkmak için, herhangi bir nesne oluşturmadan önce ToList () çağırın.

Yanıtlar:


127

'Ödemeler' hakkında daha fazla bilgi olmadan bu çok yardımcı olmaz, ancak bir Payments nesnesi oluşturmak ve bazı özelliklerini sütun değerlerine göre ayarlamak istediğinizi varsayarsak:

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
Bu harika çalışıyor, sınıf için boş bir kurucu eklemeyi unutmayın.
live-love

58
Sadece bu yanıta eklemek için, bunu Yapılar ile yapamazsınız, sadece Sınıflar - bunu anlamam biraz zaman aldı!
naspinski

4
Evet, bence Tony'nin cevabının bundan daha iyi olduğunu düşünüyorum çünkü aslında mevcut sorunu çözüyor, oysa bu, Payments sınıfının doğasını değiştirerek ve muhtemelen değişmez olmasını engelleyerek sorunu çözüyor.
Stephen Holt

bu çirkin görünüyor. EF6 ile daha iyi bir yol var mı?
Araç Seti

115

Oluşturucunuzu özellikleri değil de başlatma için kullanmak istiyorsanız (bazen bu davranış başlatma amaçları için istenir), ToList()veya çağırarak sorguyu numaralandırın ToArray()ve ardından kullanın Select(…). Böylece LINQ to Collections'ı kullanacak ve yapıcıyı parametrelerle arayamama sınırlamasıSelect(…) yapıcıyı ortadan kalkacaktır.

Yani kodunuz şunun gibi görünmelidir:

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
Bunun neden işe yaradığını açıklığa kavuşturmak için, başlangıçta belirtilen kodla ilgili sorun, Entity Framework'ün yapıcı çağrısını LINQ sorgusunun geri kalanıyla birlikte SQL'e geçirmeye çalışması ve tabii ki SQL'in inşa etmeye devam etmenin bir yolu yoktur. karmaşık nesneler! ToList () çağrısını ekleyerek, numaralandırılabilir olanı henüz çalıştırılmamış bir SQL sorgusundan bellekteki somut nesneler listesine taşır ve daha sonra bunları istediğiniz şekilde değiştirebilirsiniz.
Stephen Holt

19
Bunun için kullanmayın ToX(), kullanın AsEnumerable().
Rawling

1
.ToList () // LINQ'dan Koleksiyonlara transfer geliyor. benim için sorunu çözen çizgi.
Ram

15
Bu seçecek unutmayın tüm db seviyesinde sütunları nerede sadece gerekli sütunları seçecektir normalde olarak
Hugh Jeffner

4
Sadece bu değil, muhtemelen birden fazla numaralandırmanız olacak. Bu çözümü beğenmedim.
Bluebaron

47

Bu hatayla henüz karşılaştıktan sonra, ekleyeceğimi düşündüm, eğer Paymenttür bir a ise struct, aynı hatayla da karşılaşacağınızı çünkü structtürler parametresiz kurucuları desteklemiyor.

Bu durumda, Paymentbir sınıfa dönüştürmek ve nesne başlatıcı sözdizimini kullanmak sorunu çözecektir.


Bu benden problemi çözüyor. Aslında yapı seçicili bu sorgu LINQ-2-SQL'de desteklenir ve EntityFramework'a yükseltirken bir sorundur.
Tomas Kubes

Yapılardan nefret ediyorum. Asla istediğimi yapmıyorlar
Simon_Weaver

DateTimeSorgumun içinde aynı Hataya neden olan bir (yapı olan) oluşturuldu . yerel bir Değişkene çıkarmak benim için düzeltdi. Yapı ipucu için teşekkürler.
LuckyLikey

20

Benim gibiyseniz ve oluşturduğunuz her sorgu için mülklerinizi doldurmak istemiyorsanız, bu sorunu çözmenin başka bir yolu vardır.

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

Bu noktada, anonim bir nesne içeren bir IQueryable'ınız var. Özel nesnenizi bir yapıcıyla doldurmak istiyorsanız, bunun gibi bir şey yapabilirsiniz:

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

Artık özel nesneniz (parametre olarak iki nesne alır), özelliklerinizi gerektiği gibi doldurabilir.


Bu benim için çalıştı ve en temiz çözüm oldu. Yapıcıyı ortadan kaldırmayı ve başlatıcı sözdizimini kullanmayı önerenlerin yapıcı içinde mantığı olmamalıdır. Bu, bir nesnenin özelliklerini doldurmak için yapıcılara dayandığım tek zamandır. Paylaşım için teşekkürler.
Bonez 024

9

İlk önce çözümden kaçınırdım

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

Bu, boş bir kurucu gerektirir ve kapsüllemeyi yok sayar, bu nedenle new Payments () 'ın herhangi bir veri içermeyen geçerli bir ödeme olduğunu söylüyorsunuz, ancak bunun yerine nesnenin en az bir değere ve etki alanınıza bağlı olarak muhtemelen diğer gerekli alanlara sahip olması gerektiğini söylüyorsunuz.

Gerekli alanlar için bir kurucuya sahip olmak, ancak yalnızca gerekli verileri getirmek daha iyidir:

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

Bu daha az kötüdür.
Chalky

Ben de böyle bir şeyi tercih ederim. Tuple'ı kullanmayı deniyordum ama Tuple'ın parametresi daha az yapıcıya sahip değil. Anonim bir nesne doldurdum ve ardından Tuple'ı seçtim.
2017'de

kapsülleme ve etki alanı için bir yukarı
inrandomwetrust

2

Aynı şeyi yapmayı deneyebilirsiniz, ancak uzantı yöntemlerini kullanarak. Veritabanı kullanımının sağlayıcısı nedir?

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

İfadeden ToList()hemen DbSetönce Select.. gerçek DbSetsorgu olarak kaydedilir, henüz yerine getirilmemiştir. Çağırdıktan sonra ToList()nesnelerle oynuyorsunuz ve ardından sorguda varsayılan olmayan bir kurucu kullanabilirsiniz.

Kullanım süresi açısından en verimli yol değil, ancak küçük setlerde bir seçenektir.


1

evet, böyle dene ....

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

bu, parametresiz bir kurucu kullanarak Ödeme nesnenizi yeniler ve ardından küme parantezleri içinde listelenen özellikleri başlatır { }


3
Bilginize, ()Payemnt'lere gerek yoktur, bu nedenle `yeni Ödemeler
seçilebilir

şimdi
hatam var:

sağ - eğer bir anon türü yaratıyor olsaydınız (Payments sınıfının bir örneği yerine), Muad'ın kodu iyi olurdu çünkü ayarlanacak özellikler dolaylı olarak okunan özelliklerin isimleri olurdu. "Gerçek" bir sınıf olduğu için, çeşitli değerlere hangi özelliklerin ayarlanacağını belirlemeniz gerekir.
James Manning

1

Yukarıda belirtilen yöntemlere ek olarak, bunu bir Numaralandırılabilir koleksiyon olarak da ayrıştırabilirsiniz, örneğin:

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

Bu ayrıca, anonim bir nesne oluştururken hayatı kolaylaştırmak gibi ek bir faydaya da sahiptir, örneğin:

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

Bununla birlikte, bir koleksiyonu Enumerable olarak ayrıştırmanın onu belleğe çektiğini ve böylece yoğun kaynak gerektirebileceğini hatırlamak! Burada dikkatli olunmalıdır.


1

Ayrıca, başlatmak için birden çok nesneye sahip bir kurucu kullanmak isterseniz, Linq tarafından hiçbir değer döndürülmezse hata alabilirsiniz.

Yani şuna benzer bir şey yapmak isteyebilirsiniz:

(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

Bulduktan sonra partiye geç kaldığım için üzgünüm, ama bu , bunu en hızlı ve aynı zamanda hafıza tasarrufu uygulaması bulabildiğim, en temiz olduğu gibi bu paylaşılması gereken düşündüm.

Örneğinize uyarlandığında şunları yazarsınız:

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

Buradaki büyük avantajlar (bağlantıdaki yorumlarda Damien Guard'ın belirttiği gibi):

  • Her seferinde başlatma modelini kullanmaktan sizi korur.
  • İle kullanımı var foo = createPayments(bar);aynı zamanda myIQueryable.ToPayments ile kullanımı () mümkün.

1

Bugün de aynı sorunu yaşadım ve çözümüm Yoda'nın listelediğine benziyordu, ancak yalnızca akıcı sözdizimi ile çalışıyor.

Çözümümü kodunuza uyarlamak: Nesne sınıfına aşağıdaki statik yöntemi ekledim

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

ve ardından temel sorguyu şu şekilde güncelledi:

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

Bu mantıksal olarak James Manning'in çözümüne eşdeğerdir ve üye başlatma şişkinliğini Sınıf / Veri Aktarım Nesnesine itme avantajına sahiptir.

Not: Başlangıçta "Başlatıcı" dan daha açıklayıcı isimler kullanıyordum, ancak onu nasıl kullandığımı gözden geçirdikten sonra "Başlatıcı" nın yeterli olduğunu buldum (en azından benim amaçlarım için).

Son Not:
Bu çözümü bulduktan sonra, aslında aynı kodu paylaşmanın ve bunu Sorgu sözdizimi için de çalışacak şekilde uyarlamanın basit olacağını düşünüyordum. Artık böyle olduğuna inanmıyorum. Bence bu tür bir steno yapıyı kullanmak istiyorsanız, yukarıda açıklandığı gibi her biri (sorgu, akıcı) için nesne sınıfının kendisinde var olabilecek bir yönteme ihtiyacınız olacaktır.

Sorgu sözdizimi için bir uzantı yöntemi (veya kullanılan temel sınıfın dışında bir yöntem) gerekli olacaktır. (sorgu sözdizimi T yerine IQueryable çalıştırmak istediğinden)

İşte sonunda bunu sorgu sözdizimi için çalıştırmak için kullandığım şeyin bir örneği. (Yoda bunu zaten çiviledi, ancak kullanımın daha net olabileceğini düşünüyorum çünkü ilk başta anlamadım)

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

ve kullanım

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

İlk cevabımın iyi genişlemediğini fark ettiğimde tamlık için sorgu sözdizimi ile ilgili bir bölüm ekledi. @ yoda'nın cevabı muhtemelen sorgu sözdizimi açısından daha iyidir.
33'te wode

0

Cevap vermek için geç olmasına rağmen, yine de sıkıntıda olan birine yardımcı olabilir. LINQ to varlıklara parametresiz nesne yapılarını desteklemediğinden. Ancak, IEnumerable için projeksiyon yöntemleri .

Yani önce seçime, sadece dönüştürmek IQueryable için IEnumerable bu kodu kullanarak:

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

İyi çalışacak. Ancak, elbette yerel sorguların faydalarını kaybedecektir.


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; }
        ...
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.