Внутренняя ошибка поставщика данных .NET Framework 1025
IQueryable<Organization> query = context.Organizations;
Func<Reservation, bool> predicate = r => !r.IsDeleted;
query.Select(o => new {
Reservations = o.Reservations.Where(predicate)
}).ToList();
этот запрос выдает исключение "Ошибка поставщика данных в среде .NET Framework 1025", но ниже этого запроса нет.
query.Select(o => new {
Reservations = o.Reservations.Where( r => !r.IsDeleted)
}).ToList();
Мне нужно использовать первый, потому что мне нужно проверить несколько операторов if для построения правильного предиката. Я знаю, что я не могу использовать инструкции if в этом случае, поэтому я передаю делегат как параметр.
Как я могу выполнить первый запрос?
Ответы
Ответ 1
В то время как приведенные выше ответы верны, обратите внимание, что при попытке использовать его после оператора select нужно явно вызывать AsQueryable()
, иначе компилятор предположит, что мы пытаемся использовать методы IEnumerable, которые ожидают, что Func
и не Expression<Func>
.
Вероятно, это была проблема с оригинальным плакатом, так как в противном случае компилятор будет жаловаться большую часть времени, когда он ищет Expression<Func>
, а не Func
.
Демо:
Не удалось выполнить следующее:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).Any(expr))
.Load()
Пока будет работать следующее:
MyContext.MySet.Where(m =>
m.SubCollection.Select(s => s.SubItem).AsQueryable().Any(expr))
.Load()
Ответ 2
После создания баунти (крысы!), я нашел этот ответ, который решил мою проблему. (Моя проблема связана с вызовом .Any()
, который немного сложнее, чем этот вопрос...)
Короче говоря, вот ваш ответ:
IQueryable<Organization> query = context.Organizations;
Expression<Func<Reservation, bool>> expr = r => !r.IsDeleted;
query.Select(o => new { Reservations = o.Reservations.Where(expr) })
.ToList();
Прочитайте указанный ответ для объяснения, почему вам нужна локальная переменная expr
, и вы не можете напрямую ссылаться на другой метод возвращаемого типа Expression<Func<Reservation, bool>>
.
Ответ 3
Спасибо, что рассмеялся. В конце концов, я был на правильном пути.
Во всяком случае, повторять, LINQ to Entities (спасибо Jon Skeet за то, что вы исправили меня, когда меня перепутали в моем собственном мыслительном процессе в комментариях) работает на Деревья выражений; он позволяет проектировать для преобразования лямбда-выражения в SQL с помощью QueryProvider
.
Обычный Func<>
хорошо работает для LINQ to Objects.
Итак, в этом случае, когда вы используете Entity Framework, любой предикат, переданный EF IQueryable
, должен быть Expression<Func<>>
.
Ответ 4
Я просто испытал эту проблему в другом сценарии.
У меня есть статический класс, полный предикатов Expression
, которые я могу затем объединить или передать в EF-запрос. Один из них:
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(statuses.Contains);
}
Это вызвало ошибку 1025 из-за группового вызова метода Contains
. Структура сущности ожидала выражения и обнаружила группу методов, которая привела к ошибке. Преобразование кода для использования лямбда (который может быть неявно применено к выражению) исправил ошибку
public static Expression<Func<ClientEvent, bool>> ClientHasAttendeeStatus(
IEnumerable<EventEnums.AttendeeStatus> statuses)
{
return ce => ce.Event.AttendeeStatuses
.Where(a => a.ClientId == ce.Client.Id)
.Select(a => a.Status.Value)
.Any(x => statuses.Contains(x));
}
Кроме того, я затем упростил выражение до ce => ce.Event.AttendeeStatuses.Any(a => a.ClientId == ce.Client.Id && statuses.Contains(a.Status.Value));
Ответ 5
Была аналогичная проблема. Библиотека ViewModels, которая выглядит так:
public class TagViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public static Expression<Func<SiteTag, TagViewModel>> Select = t => new TagViewModel
{
Id = t.Id,
Name = t.Name,
};
Это работает:
var tags = await db.Tags.Take(10).Select(TagViewModel.Select)
.ToArrayAsync();
Но это не скомпилируется:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).Select(TagViewModel.Select)
})
.ToArrayAsync();
Потому что второй .Select
является беспорядок - первый из них действительно вызывается из ICollection, который не является IQueryable, поэтому он потребляет это первое выражение как простой Func
, а не Expression<Func...
. Это возвращает IEnumerable<...
, как описано на этой странице. Итак .AsQueryable()
на помощь:
var post = await db.Posts.Take(10)
.Select(p => new {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();
Но это создает новую, более сложную проблему: либо я получаю Internal Framework... Ошибка 1025, либо получаю переменную post с полностью загруженным свойством .Post
, но свойство .Tags
имеет объект прокси EF, который кажется, используется для Lazy-Loading.
Решение состоит в том, чтобы управлять возвращаемым типом тегов, заканчивая использование класса Anonymous:
public class PostViewModel
{
public Post Post { get; set; }
public IEnumerable<TagViewModel> Tags { get; set; }
Теперь выберите это, и все это работает:
var post = await db.Posts.Take(10)
.Select(p => new PostViewModel {
Post = p,
Tags = p.Tags.Select(pt => pt.Tag).AsQueryable()
.Select(TagViewModel.Select)
})
.ToArrayAsync();