Как создать выражение LINQ из свойства навигации Entity Framework? - PullRequest
4 голосов
/ 28 мая 2009

У меня есть следующий бит кода:

Expression<Func<Subscription, Service>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService).FirstOrDefault();

Создает выражение, которое я могу использовать позже как часть запроса со структурой сущностей. Фактический код, который я использую, имеет предложение where, но я опускаю его для удобства чтения. Это отлично работает, и я могу запустить его в LINQPad, который я использую для тестирования.

Если я изменю код на:

Expression<Func<Subscription, IQueryable<Service>>> service2= subscription => (from relationship in subscription.ChildRelationships
    select relationship.SecondService);

Больше не компилируется и имеет следующую ошибку:

Невозможно преобразовать лямбда-выражение в тип делегата 'System.Func >' потому что некоторые из возвращаемых типов в блок не является неявным обратимый к возвращению делегата тип

Похоже, потому что ChildRelationships, который является свойством навигации, не реализует IQueryable. На самом деле он имеет тип EntityCollection, который реализует IEnumerable, но не подходит для создания выражений, работающих с EF.

Мне кажется, я понимаю, почему второй блок кода не работает, и хотел бы знать, как его переписать, чтобы он работал. Меня также озадачивает, почему первый блок кода работает. Он также использует свойство навигации ChildRelationships, но без проблем становится выражением, которое работает с EF.

Может ли кто-нибудь пролить свет?

Ответы [ 3 ]

1 голос
/ 28 мая 2009

Проблема не в том, что FirstOrDefault каким-то образом заставляет Expression работать; как вы говорите, ChildRelationships сама по себе не реализует IQueryable. Вы можете обойти это двумя различными способами, в зависимости от ваших потребностей.

Вы можете использовать метод IEnumerable.AsQueryable. Это, вероятно, лучше, если сервисы уже загружены из контекста.

Если службы не загружены, вы можете при необходимости использовать методы «Загрузить» или «Включить», чтобы загрузить их в соответствующее время, а затем использовать вышеуказанное решение.

Если у вас есть ссылка на ваш ObjectContext, вы можете использовать ObjectQuery, который реализует IQueryable:

Expression<Func<Subscription, MyEntities, IQueryable<Service>>> service2 = 
    (subscription, context) => (
    from s in context.Subscriptions // here is the IQueryable
    where s.Id == subscription.Id
    from relationship in s.ChildRelationships
    select relationship.SecondService);
0 голосов
/ 13 июня 2009

Вам нужно, чтобы оно было IQueryable ? Я думаю, что select возвращает IEnumerable . LINQ работает непосредственно с IEnumerables, поэтому вы можете просто объявить service2 как выражение >>.

0 голосов
/ 28 мая 2009

Я думаю, CompiledQuery класс может помочь вам в этом случае:

Expression<Func<Subscription, IQueryable<Service>>> service2 = 
    CompiledQuery.Compile( 
        subscription => (
            from relationship in subscription.ChildRelationships
            select relationship.SecondService
        )
    );
...