В LINQ to Entities поддерживаются только конструкторы без параметров и инициализаторы

У меня есть эта ошибка в этом выражении 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();

есть идеи, как решить эту проблему? Я стараюсь использовать любую комбинацию выражений... :/

12 ответов


без дополнительной информации о "платежах" это не очень помогает, Но если вы хотите создать объект платежей и установить некоторые из его свойств на основе значений столбцов:

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

если вы все еще хотите использовать свой конструктор для инициализации, а не свойства (иногда это поведение желательно для целей инициализации), перечислите запрос, вызвав ToList() или ToArray(), а затем использовать Select(…). Таким образом, он будет использовать LINQ для коллекций и это ограничение невозможности вызова конструктора с параметрами в Select(…) исчезнет.

так что ваш код должен выглядеть примерно так:

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

только что столкнувшись с этой ошибкой сам, я думал, что добавлю, что если Payment тип struct, вы также столкнетесь с той же ошибкой, потому что struct типы не поддерживают конструкторы без параметров.

в этом случае, преобразования Payment к классу и с помощью синтаксиса инициализатора объекта решит проблему.


Если вы похожи на меня и не хотите заполнять свои свойства для каждого создаваемого запроса, есть другой способ решить эту проблему.

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

на данный момент у вас есть IQueryable, содержащий анонимный объект. Если вы хотите заполнить свой пользовательский объект конструктором, вы можете просто сделать что-то вроде этого:

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

теперь ваш пользовательский объект (который принимает два объекта в качестве параметра) может заполнить свои свойства по мере необходимости.


сначала я бы избегал решения с

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

для этого требуется пустой конструктор и игнорируется инкапсуляция, поэтому вы говорите, что new Payments() является допустимым платежом без каких-либо данных, но вместо этого объект должен иметь хотя бы значение и, вероятно, другие обязательные поля в зависимости от вашего домена.

лучше иметь конструктор для обязательных полей, но приносить только необходимые данные:

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

да, попробуйте вот так....

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

это создаст новый объект оплаты с помощью конструктора без параметров, а затем инициализирует свойства, перечисленные внутри фигурных скобок { }


вы можете попробовать сделать то же самое, но с помощью методов расширения. Каков поставщик использования базы данных?

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

кроме того, если вы хотите использовать конструктор с несколькими объектами для инициализации, вы можете получить ошибку, если Linq не возвращает значения.

поэтому вы можете сделать что-то вроде этого:

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

Извините, что опоздал на вечеринку, но я после нахождения этой, Я думал, что это должно быть общим, так как это самая чистая, быстрая и также экономия памяти реализация, которую я мог найти.

адаптированный к вашему примеру, вы напишете:

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

большие преимущества здесь (как отметил Дэмиен Гуард в комментариях по ссылке):

  • предохраняет вас от использования шаблона инициализации в каждом случае.
  • использование через var foo = createPayments(bar); а также использование через myIQueryable.ToPayments () возможно.

у меня была та же проблема сегодня, и мое решение было похоже на то, что перечислил йода, однако оно работает только с беглым синтаксисом.

адаптация моего решения к вашему коду: Я добавил следующий статический метод в класс object

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

а затем обновил базовый запрос до следующего:

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

это логически эквивалентно решению Джеймса Мэннинга с преимуществом нажатия раздувания инициализации члена в класс / передачу данных Объект

Примечание: Первоначально я использовал более описательные имена, которые " инициализатор" но, просмотрев, как я его использовал, я обнаружил, что "инициализатор" был достаточным (по крайней мере, для моих целей).

Конечная Нота:
После создания этого решения я изначально думал, что будет просто поделиться тем же кодом и адаптировать его для работы с синтаксисом запроса. Я больше не верю, что это так. Я думаю, что если вы хотите иметь возможность использовать этот тип стенографическая конструкция вам понадобится метод для каждого (query,fluent) fluent, как описано выше, который может существовать в самом классе объектов.

для синтаксиса запроса потребуется метод расширения (или какой-либо метод вне используемого базового класса). (поскольку синтаксис запроса хочет работать с IQueryable, а не T)

вот пример того, что я использовал, чтобы, наконец, заставить это работать для синтаксиса запроса. (Йода уже прибил это, но я думаю, что использование может быть более ясным потому что я сначала не понял)

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

и использование

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

в дополнение к вышеупомянутым методам вы также можете разобрать его как перечисляемую коллекцию, например:

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

Это также имеет дополнительное преимущество, облегчающее жизнь при создании анонимного объекта, например:

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

запоминание, однако, что разбор коллекции как перечисляемой тянет ее в память, поэтому она может быть ресурсоемкой! Здесь следует соблюдать осторожность.


просто ToList () DbSet перед оператором Select.. фактический DbSet сохраняется как запрос, он еще не выполнен. после вызова ToList () вы играете с объектами, и там вы можете использовать нестандартный конструктор в запросе. не самый эффективный способ использования времени, но это вариант для небольших наборов.