В LINQ to Entities поддерживаются только конструкторы и инициализаторы без параметров - PullRequest
121 голосов
/ 26 августа 2010

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

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

Ответы [ 13 ]

115 голосов
/ 26 августа 2010

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

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();
107 голосов
/ 14 декабря 2012

Если вы все еще хотите использовать свой конструктор для инициализации, а не свойства (иногда такое поведение желательно для целей инициализации), перечислите запрос, вызвав ToList() или ToArray(), а затем используйте Select(…). Таким образом, он будет использовать LINQ to Collections, и это ограничение невозможности вызова конструктора с параметрами в 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();
46 голосов
/ 23 августа 2011

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

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

19 голосов
/ 11 апреля 2013

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

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

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

8 голосов
/ 30 декабря 2016

Во-первых, я бы избежал решения с

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

Для этого требуется пустой конструктор и игнорируется инкапсуляция, поэтому вы говорите, что новый 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();
2 голосов
/ 25 января 2018

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

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

2 голосов
/ 26 августа 2010

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

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 голос
/ 01 апреля 2017

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

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

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

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

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

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

Для синтаксиса запросапотребуется метод расширения (или какой-либо метод вне используемого базового класса).(так как синтаксис запроса хочет работать с 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();
1 голос
/ 30 марта 2017

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

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

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 () возможно.
1 голос
/ 16 марта 2016

Кроме того, если вы хотите использовать конструктор с несколькими объектами для инициализации, вы можете получить ошибку, если 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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...