В LINQ для 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();
Любая идея, как решить эту проблему? Я пытаюсь с любой комбинацией выражения...:/
Ответы
Ответ 1
без дополнительной информации о "Платежах" это не очень помогает, но при условии, что вы хотите создать объект "Платежи" и установить некоторые его свойства на основе значений столбцов:
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();
Ответ 2
Если вы все еще хотите использовать свой конструктор для инициализации, а не для свойств (иногда это поведение желательно для целей инициализации), перечислите запрос, вызвав 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();
Ответ 3
Я только что столкнулся с этой ошибкой, я думал, что добавлю, что если тип Payment
является struct
, вы также столкнетесь с той же ошибкой, потому что типы struct
не поддерживают конструкторы без параметров.
В этом случае преобразование Payment
в класс и использование синтаксиса инициализатора объекта разрешит проблему.
Ответ 4
Если вы похожи на меня и не хотите заполнять свои свойства для каждого запроса, который вы строите, есть другой способ решить эту проблему.
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));
Теперь ваш пользовательский объект (который принимает два объекта в качестве параметра) может заполнить ваши свойства по мере необходимости.
Ответ 5
Сначала я бы избегал решения с помощью
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();
Ответ 6
Просто ToList()
DbSet
перед оператором Select
. Фактический DbSet
сохраняется как запрос, но еще не выполнен. После вызова ToList()
вы играете с объектами, а затем вы можете использовать конструктор не по умолчанию в запросе.
Не самый эффективный способ использования по времени, но это вариант для небольших наборов.
Ответ 7
yeh, попробуйте вот так....
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 с помощью конструктора без параметров, а затем инициализирует свойства, которые перечислены внутри фигурных скобок { }
Ответ 8
Вы можете попытаться сделать то же самое, но используя методы расширения. Что использует поставщик базы данных?
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();
Ответ 9
Кроме того, если вы хотите использовать конструктор с несколькими объектами для инициализации, вы можете получить ошибку, если 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();
Ответ 10
Извините за опоздание на вечеринку, но после того, как я нашел этот, я подумал, что это должно быть общим, поскольку это самый чистый, быстрый и также я мог бы найти реализацию сохранения памяти.
Адаптированный к вашему примеру, вы должны написать:
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);
}
Большие преимущества здесь (как отметил Damien Guard в комментариях по ссылке):
- Защищает вас от использования шаблона инициализации в каждом случае.
- Использование через
var foo = createPayments(bar);
, а также использование через myIQueryable.ToPayments() возможно.
Ответ 11
У меня была такая же проблема сегодня, и мое решение было похоже на то, что перечислял Йода, однако он работает только с плавным синтаксисом.
Адаптация моего решения к вашему коду:
Я добавил следующий статический метод в класс объекта
/// <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());
Это логически эквивалентно решению Джеймса Мэннинга с тем преимуществом, что нажатие раздувания инициализации члена на объект класса/передачи данных
Примечание. Первоначально я использовал более описательные имена, которые "Инициализатор"
но после изучения того, как я его использовал, я обнаружил, что "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();
Ответ 12
В дополнение к вышеупомянутым методам вы также можете проанализировать его как коллекцию 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 вызывает ее в памяти, поэтому она может быть ресурсоемкой! Здесь следует соблюдать осторожность.