2010-11-27 2 views
7

Я только что перешел из Linq 2 SQL в Entity Framework, и я вижу странное поведение в EF, что я надеюсь, что кто-то может помочь. Я попробовал Googling, но я не смог найти других людей с этой же проблемой. Я издевался над сценарием, чтобы объяснить ситуацию.Структура Entity Framework и шаблон хранилища (проблема с IQueryable)

Если я работаю напрямую с контекстом EF, я могу сделать выбор в пределах выбора. Например, это выполняется прекрасно:

 // this is an Entity Framework context that inherits from ObjectContext 
     var dc = new MyContext(); 

     var companies1 = (from c in dc.Companies 
          select new { 
           Company = c, 
           UserCount = (from u in dc.CompanyUsers 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

Однако, если я использую хранилище шаблон, где хранилище возвращающегося IQueryable (или даже ObjectSet или ObjectQuery), я получаю NotSupportedException (LINQ к Entities не распознает метод «System.Linq.IQueryable`1) ...

Вот пример моего репозитория:

public class Repository { 
    private MyContext _dc; 

    public Repository() { 
     _dc = new MyContext(); 
    } 

    public IQueryable<Company> GetCompanies() { 
     return _dc.Companies; 
    } 

    public IQueryable<CompanyUser> GetCompanyUsers() { 
     return _dc.CompanyUsers; 
    } 
} 

// Я использую хранилище внутри другого класса (например, в моем слое Services)

 var repository = new Repository(); 

     var companies2 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in repository.GetCompanyUsers() 
              where u.CompanyId == c.Id 
              select u).Count() 
          }).ToList(); 

Вышеупомянутый код генерирует исключение NotSupportedException.

Я понимаю, что если есть связь между компаниями и CompanyUsers, то я могу просто сделать это, и он будет работать нормально:

 var companies3 = (from c in repository.GetCompanies() 
          select new { 
           Company = c, 
           UserCount = (from u in c.CompanyUsers 
              select u).Count() 
          }).ToList(); 

... но мой пример просто упрощенная версия более сложным где у меня нет связи между сущностями.

Так что я очень смущен, почему Entity Framework бросает NotSupportedException. Как получается, что запрос работает отлично, когда я напрямую работаю с контекстом EF, но он не поддерживается, если я работаю с IQueryable, возвращенным из другого метода. Это отлично работало с Linq 2 SQL, но, похоже, оно не работает в Entity Framework.

Любое понимание было бы весьма полезным.

Заранее спасибо.

ответ

7

Я подозреваю, что это происходит, что EF видит выражение для repository.GetCompanyUsers() внутри лямбда для первого select и не знает, что делать с ним, потому что repository не контекст EF. Я думаю, что если вы передадите IQueryable напрямую вместо выражения, которое возвращает его, оно должно работать.

Как насчет если вы сделаете это:

var companyUsers = repository.GetCompanyUsers(); 
    var companies2 = (from c in repository.GetCompanies() 
         select new { 
          Company = c, 
          UserCount = (from u in companyUsers 
             where u.CompanyId == c.Id 
             select u).Count() 
         }).ToList(); 
+0

Зачем нам это нужно, я имею в виду, что у нас есть ассоциация здесь? – paragy

+0

+1 U ответил тоже. – paragy

+0

Для этого требуется два раунда поездки на сервер, хотя ... подождите, не так ли? –

3

Это одна из тех странных причуд с Linq к SQL/EF. По-видимому, они реализовали способ перевести с getter свойство в SQL, но не способ перевести с геттера функция на SQL.

Если вы используете функцию GetCompanyUsers(), вы используете свойство, подобное CompanyUsers, оно должно работать.

Weird eh?

Таким образом, вместо

public IQueryable<CompanyUser> GetCompanyUsers() { 
    return _dc.CompanyUsers; 
} 

Вы могли бы сделать

public IQueryable<CompanyUser> CompanyUsers { 
    get { return _dc.CompanyUsers; } 
} 

Насколько параметризованные запросы идут (которые вы не можете сделать с собственностью, очевидно), см мой вопрос ответил здесь: Custom function in Entity Framework query sometimes translates properly, sometimes doesn't

У вас также может быть wheres и selects; они будут хорошо переводить. Например, если у меня был блог с таблицей статей, которая содержит несколько статей, которые не являются онлайн:

public IQueryable<Article> LiveArticles { 
    get { return _dc.Articles.Where(a => !a.IsDraft); } 
} 

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

+0

Очень странно, но ты абсолютно прав. Свойства, возвращающие работу IQueryable с EF. Linq to SQL также работал с методами, но я думаю, что EF этого не реализовал. –

Смежные вопросы