Entity Framework и Repository Pattern (проблема с IQueryable) - PullRequest
7 голосов
/ 27 ноября 2010

Я только что переключился с Linq 2 SQL на Entity Framework, и я вижу некоторые странные поведения в EF, с которыми, я надеюсь, кто-то может помочь.Я попробовал поискать в Google, но я не смог найти других людей с такой же проблемой.Я смоделировал сценарий, чтобы объяснить ситуацию.

Если я работаю напрямую с контекстом 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 to 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;
    }
}

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

        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.

Любая информация будет принята с благодарностью.

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

Ответы [ 2 ]

7 голосов
/ 27 ноября 2010

Я подозреваю, что происходит то, что 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(); 
3 голосов
/ 27 ноября 2010

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

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

Странно, а?

Так что вместо

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

Вы можете сделать

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

Что касается параметризованных запросов (что, очевидно, нельзя сделать со свойством), см. Ответ на мой вопрос здесь: Пользовательская функция в запросе Entity Framework иногда транслируется правильно, иногда - не

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

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

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

...