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


132

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

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


1
вы можете показать класс Payments? или, по крайней мере, здесь вызывается ctor, и, в частности, можно ли безопасно заменить этот вызов ctor с 8 параметрами на вызов ctor с 0 параметрами и установить 8 свойств объекта?
— Джеймс Мэннинг,

23
Я получил ту же ошибку при использовании структуры вместо класса для объекта, который я «новичок».
— HuckIt

3
TL; DR дело в том, что EF-LINQ пытается отправить оператор select поставщику EF, т.е. преобразовать его в SQL. Чтобы выйти из EF-LINQ, вызовите ToList () перед созданием любого объекта.

Ответы:


127

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

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
Это отлично работает, не забудьте добавить пустой конструктор для класса.
— live-love

58
Чтобы добавить к этому ответу, вы не можете сделать это со структурами, только с классами - мне потребовалось немного времени, чтобы понять это!
— naspinski 06

4
Да, я думаю, что ответ Тони лучше, чем этот, потому что он фактически решает непосредственную проблему, тогда как этот позволяет обойти проблему, изменяя природу класса Payments и, возможно, предотвращая его неизменность.
— Стивен Холт

это выглядит некрасиво. Есть ли лучший способ с EF6?
— Toolkit

115

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

21
Чтобы прояснить, почему это работает, проблема с первоначально заявленным кодом заключается в том, что Entity Framework пытается передать вызов конструктора в SQL вместе с остальной частью запроса LINQ, и, конечно же, SQL не может приступить к построению сложные объекты! Вставляя вызов ToList (), вы перемещаете перечислимый объект из еще не выполненного SQL-запроса в конкретный список объектов в памяти, которым вы затем можете манипулировать любым удобным для вас способом.
— Стивен Холт

19
Не используйте ToX()для этого, используйте AsEnumerable().
— Rawling

1
.ToList () // А вот и переход в LINQ to Collections. это строка, которая решает проблему для меня.
— Ram

15
Имейте в виду, что при этом будут выбраны все столбцы на уровне
— базы данных, тогда

4
Не только это, но у вас, вероятно, будет несколько перечислений. Мне не нравится это решение.
— Bluebaron

47

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

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


Это решает мою проблему. На самом деле этот запрос с селектором структуры поддерживается в LINQ-2-SQL, и это проблема при обновлении до EntityFramework.
— Tomas Kubes

Ненавижу структуры. Они никогда не заканчивают делать то, что я хочу
— Simon_Weaver

Создал DateTime(который является структурой) внутри моего запроса, что приводит к той же ошибке. извлечение его в локальную переменную исправило это для меня. Спасибо за подсказку по структуре.
— LuckyLikey

20

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

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

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


Это сработало для меня и стало самым чистым решением. У тех, кто предлагал исключить конструктор и использовать синтаксис инициализатора, не должно быть логики в конструкторе. Это единственный раз, когда я полагаюсь на конструкторы для заполнения свойств объекта. Спасибо, что поделились.
— Bonez024

9

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

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

Это меньшее зло.
— Меловой

Я тоже предпочитаю что-то подобное. Я пытался использовать Tuple, но у Tuple нет конструктора без параметров. Я заполнил анонимный объект, а затем выбрал Tuple.
— Tchaps

один за охват инкапсуляции и домена
— inrandomwetrust

2

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

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

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

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


1

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

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

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


3
К вашему сведению, ()в Payemnts нет необходимости, так что это может быть `select new Payments {// init values}
— PostMan

теперь у меня ошибка: не могу инициализировать тип «Payments» с помощью инициализатора коллекции, потому что он не реализует «System.Collections.IEnumerable»
— netmajor

правильно - если вы создавали анонимный тип (вместо экземпляра класса Payments), код Muad был бы в порядке, поскольку устанавливаемые свойства неявно были бы именами свойств, из которых выполняется чтение. Однако, поскольку это «настоящий» класс, вам нужно будет указать, какие свойства устанавливать для различных значений.
— Джеймс Мэннинг,

1

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

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

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


1

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

1

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

В соответствии с вашим примером вы должны написать:

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

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

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

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

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

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

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

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

добавил раздел, посвященный синтаксису запроса, для полноты, когда я понял, что мой первоначальный ответ не подходит. Ответ @yoda, вероятно, лучше в отношении синтаксиса запроса.
— wode

0

Хотя и поздно отвечать, он все же может помочь кому-то в беде. Поскольку LINQ to entity не поддерживает конструкции объектов без параметров. Однако методы проецирования IEnumerable .

Поэтому перед выбором просто преобразуйте свой IQueryable в IEnumerable с помощью этого кода:

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

Это будет работать нормально. Однако это, конечно, потеряет преимущества собственных запросов.


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; }
        ...
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.