2014-12-04 2 views
0

Знаете ли вы, на каком фоне я родом, я был профессиональным программистом более двенадцати лет. Мой лучший язык на сегодняшний день - это C#, но я сделал C, C++ и совсем недавно objectC. Я проделал большую работу по доступу к данным в базах данных, но я не делал так много работы с пользовательским интерфейсом, как большинство людей (кроме IOS).Память/эффективность с Linq и большими наборами данных

Недавно я начал использовать инфраструктуру Entity в C# для задания, и должен сказать, что я бы хотел, чтобы я открыл ее раньше. Я бы не сказал, что это лучше всего, так как нарезанный хлеб, но он довольно проклят близко. После использования его некоторое время он заставил меня задуматься о лучших практиках и использовании по сравнению со старым школьным методом использования IDBConnections и IDBCommands для всего.

Я кодировал ситуацию, когда я собирался перечислять содержимое таблицы пользователей из базы данных в связанной сетке данных с намерением дать пользователю возможность делать стандартные материалы CRUD. Я начал с создания класса User и интерфейса IUserManager с соответствующей реализацией. Каждому пользователю назначается отдел, и, естественно, должен быть способ выполнить CRUD на отделах, поэтому я добавил класс отдела, интерфейс IDepartmentManager и реализацию для этого. Я настроил его так, чтобы сетка привязывалась к результатам метода .GetAll() на интерфейсе IUserManager. Затем я начал заполнять кишки.

У меня нет кода передо мной, но я в основном использовал IDBConnection для доступа к хранилищу данных с IDBCommand с использованием SQL-запроса. Затем я вызвал команду .ExecuteReader() и повторил метод .Read() объекта IDataReader. Используя порядковый номер для каждого столбца, я вытащил данные, проверил его и поместил в класс User и добавил класс в словарь, который затем вернет метод. Все классы DB, конечно же, IDisposable, поэтому обертывание их при использовании заботится о том, чтобы очистить беспорядок.

Довольно стандартный материал, я делал это в bazillion раз.

Именно тогда я понял, что отдел, который я вытаскивал из БД, не был тем, что я хотел отобразить в своей сетке. Говорить кому-то «этот парень в отделе 7» не так полезен, как сказать «этот парень в бухгалтерском учете». Поэтому я сначала поиграл с модингом моего запроса, чтобы получить имя отдела и имя и сохранить имя в пользовательском объекте для отображения позже. Затем я решил дать пользователю экземпляр класса Департамента, который он будет висеть во время его жизни, которое будет заполнено. Именно тогда я переделал кишки в linq.

public Dictionary<int, User> GetAll() 
    { 
     var result = new Dictionary<int, User>(); 

     using (var datastore = new myEntities()) 
     { 
      result = (from user in datastore.userInfoes 
         join department in datastore.userDepartmentInfoes on user.departmentID equals department.departmentID 
        select new User() 
        { 
         UserIndex = user.id, 
         FirstName = user.firstName, 
         LastName = user.lastName, 
         Department = new Department() 
         { 
          DepartmentId = user.departmentID.Value, 
          DepartmentName = department.departmentName, 
                              }, 
         Username = user.userName, 
        } 
       ).ToDictionary(x => x.UserIndex, x => x); 
     } 

     return result; 
    } 

Вот где я начал думать (читай: более-анализа, вероятно)

Реализация я имел бы работать нормально. Это было бы неплохо для небольшого набора данных. Это даже отлично работает для довольно большого набора данных (скажем, 10 000). Даже если вы посчитали каждого человека в компании, в которой я сейчас работаю пять раз, у вас будет менее тысячи человек.

Но что, если на секунду я работал на действительно большую компанию-гусеницу, в которой было 10 миллионов сотрудников? Это приведет к тому, что строки departmentName будут дублироваться потенциально в миллионы раз.

Это также заставило меня думать, что в отличие от реализации MVC от IOS эта конкретная ситуация не собиралась запрашивать достаточно пользователей, чтобы заполнить экран, а затем обрабатывать пейджинг и прочее. Как только вызывающий код обновит привязку данных, он будет собирать все 10 миллионов пользователей одновременно и передать коллекцию. Это будет медленно.

Так что это оставляет мне идею в голове, что этот метод является медленным и неэффективным с большими наборами данных.Не только это, но и тот факт, что с этим набором данных может быть зарегистрировано 2 миллиона экземпляров «Учет», это будет серьезный всплеск памяти. Мы также отменяем цель реляционной базы данных здесь из-за класса отдела внутри пользователя. В БД вы просто имеете внутренний ключ departmentId int, ссылающийся на запись в другой таблице. Ссылка возникает только при перекрестке ссылки на другую таблицу, и даже тогда есть только одна строка «Учет» в любой момент времени. В приведенном выше коде у вас будет целая серия строк «Бухгалтерия», плавающих вокруг ожидающих очистки.

Сценарий MVC в основном «знает», что для заполнения области просмотра сетки требуется X количество записей. Он будет запрашивать X только за период, начинающийся с индекса Y, и по мере того, как пользователь будет осуществлять навигацию, он будет запрашивать и отображать дополнительные записи по мере необходимости. Это чертовски много лучше, чем опросить все 10 миллионов и позволить им болтаться где-нибудь, отображаются ли они или нет.

Как я уже сказал, я вполне могу пересмотреть это. В некоторых моих предположениях я мог бы также ошибаться в том, как работает linq. Но в интересах обучения я решил, что должен был спросить: «Каков наилучший способ сделать что-то подобное? Это что-то вроде хорошего для небольших наборов данных? Будет ли все это лучше, чем реализация MCV, а не вытягивать весь набор данных, который будет отображаться в сетке?

ответ

0

Если вам нужен весь набор данных в памяти - вам все равно придется его загрузить. Я уверен, что вы не будете перечислять пользователей 10kk в сетке, не так ли? Приемы, которые возникают, - пейджинг. Проверьте this article from msdn с примерами.

Что касается объектов отделов, имеет ли ваш UserInfo внешний ключ для отдела? Если это так, у вас должно быть только userInfo.Department, и вам не нужны никакие объединения.

Если вы привязываете данные отдела к столбцам сетки, почему у вас есть свойство Department? Я предполагаю, что ваш класс User - это то, что вы связываете с пользовательским интерфейсом. Свести его в:

class User 
{ 
Username 
UserIndex 
FirstName 
LastName 
DepartmentId 
DepartmentName 
} 

Какова цель GetAll()? Вы возвращаете словарь, и вам кажется, что вам нужно включить поиск по id. Или вы используете результат для перечисления пользователей?

Для поиска, подумайте о том, чтобы поговорить с базой данных, чтобы получить вам единичные данные пользователя в случае необходимости. Реализуйте кеширование, если имеет смысл следующее.

Для перечисления не возвращайте словарь - это объект «все в памяти», возвращайте IEnumerable с результатами (paged?) Или даже лучше IQueryable, чтобы вызов GetAll() не выполнял сразу вызов sql, и вызывающий код может охватить вызов путем добавления необходимых фильтров

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