взрыв запроса c # EF

#c# #sql-server #entity-framework

#c# #sql-сервер #entity-framework

Вопрос:

Работа с тремя таблицами — Company, Employee и User.

В компании 0 или много сотрудников. У сотрудника есть обнуляемый int FK для Company. На практике это всегда имеет значение. У сотрудника есть ненулевой int FK для пользователя. У пользователя отключено битовое поле AccountIsDisabled.

В моей модели данных у меня есть частичный класс, расширяющий класс модели EF для Company. При этом вызов ActiveEmployees возвращает всех сотрудников, которые активны для компании.

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

Я перепробовал много методов для принудительной загрузки, но безрезультатно.

Кто-нибудь там видит причину, по которой я получаю этот взрыв запроса?

 namespace JCS.Data
{
    public partial class Company : IIdentifiable
    {
        public IEnumerable<Employee> ActiveEmployees
        {
            get
            {
               return Employees.Where(e => !e.User.AccountIsDisabled);
            }
        }
    }
}
  

Извините за недостающую информацию.

Взрыв запросов происходит при обращении к свойству bool в соответствующем классе Employee. Вот так

 namespace JCS.Data
{
    public partial class Employee : IIdentifiable
    {
        public bool ApprovesTimesheets
        {
            get
            {
                return Company.ActiveEmployees.Any(
                    employee => employee.TimesheetApproverEmployeeID == ID
                            amp;amp; employee.TimesheetsEnabled);
            }
        }
    }
}
  

Так что в любом месте кода я иду

 bool approvesTimesheets = employee.ApprovesTimesheets;
  

Я получаю 1 тыс. запросов.

Я попытался добавить ToLis() в компанию.Активные сотрудники. Никакой радости.

например, в классе Employee

 var activeEmployees = Company.ActiveEmployees.ToList();
var approvesTimesheets = activeEmployees .Any(
                      employee => employee.TimesheetApproverEmployeeID == ID
                        amp;amp; employee.TimesheetsEnabled);
  

последняя в длинной череде неудачных попыток:

 public List<Employee> ActiveEmployees
    {
        get
        {
            var employees = Employees.AsQueryable().Include(x => x.User).ToList();
            return employees.Where(e => !e.User.AccountIsDisabled).ToList();
            //return Employees.Where(e => !e.User.AccountIsDisabled);
        }
    }
  

Комментарии:

1. Выполняется ли этот запрос linq медленно?

2. Какую быструю загрузку вы пробовали?

3. Можете ли вы показать нам, как вы используете ActiveEmployees property?

4. перечислите значения там с помощью . ToList(), в противном случае объект запроса будет возвращен (не выполнен), а затем при перечислении он будет выполняться для каждого перечисляемого объекта

5. Вопрос в том, как вы извлекаете этот Company класс из базы данных, потому что это единственное место, где вы можете применить быструю загрузку.

Ответ №1:

Вам нужно вызвать .ToList() or ToListAsync() , чтобы получить все данные сразу, иначе он будет получать данные «на лету» для каждой записи.

Это проблема с отложенным выполнением по сравнению с немедленным выполнением. Когда вы не материализуете список, .Where(foo).ToList() он загружает каждую запись всякий раз, когда вы пытаетесь получить к ней доступ, поэтому вызывает 1000 DB.

редактировать: Обратите внимание, что вы также используете навигационное свойство, которое указывает на другой объект (я предполагаю, что это объект, сопоставленный непосредственно с таблицей), поэтому при попытке получить этот объект вы также выполняете дополнительные вызовы DB. чтобы избежать этого, сделайте что-то вроде этого :

 public partial class Company : IIdentifiable
    {
        public IEnumerable<Employee> ActiveEmployees
        {
            get
            {
               return Employees.Where(e => !e.User.AccountIsDisabled).Include(x=>x.User).ToList();
            }
        }
    }
  

Комментарии:

1. это не отложенная загрузка (то есть вы не исправляете ее этим), но это «отложенное выполнение» LINQ. конечно, отложенная загрузка несет те же риски, но вы бы исправили это с помощью быстрой загрузки (.Include()), а не . ToList().

2. @MisterPositive Ответ? Любой, кто работает с EF, знал бы, что Employees это свойство навигации (что-то вроде ICollection<Employee> ), которое представляет собой лениво загруженную коллекцию памяти, поэтому ToList или нет ToList , на самом деле не имеет значения. По-видимому, проблема с операционной системой связана с отложенной загрузкой Employee.User свойства. И это не может быть исправлено в этом месте кода, что бы вы ни делали. Смотрите мой комментарий к сообщению.

3. Иван, ты прав, я не заметил свойство навигации, которое должно быть с. .Include()

4. ‘EntityCollection<Сотрудник>. Include(bool, bool)’ недоступен из-за его уровня защиты

5. @Aviatrix эта ошибка была вызвана попыткой вашего решения. спасибо …. пытаюсь разобраться. Таким образом, Compay имеет связанную EntityCollection сотрудников.

Ответ №2:

ХОРОШО, проблема возникла из-за использования свойств навигации в POCOs.

Как указал @IvanStoev, я мог только принудительно выполнять быструю загрузку при первоначальном вызове БД.

Поэтому, когда я загружаю начальный объект Employee, мне нужно загрузить все связанные объекты. Итак..

 _currentUser = Repository.Context.Employees.Include("User").Include("Company.Employees.User").FirstOrDefault(e => e.User.Person.Email == HttpContext.User.Identity.Name);
  

Решает проблему. Теперь я беспокоюсь, что у меня загружено много данных. Company.Employee — это объект 1K для большой компании.

Требуется еще некоторое тестирование, но исследование значительно улучшило мое понимание EF. Спасибо за помощь.