Знаете ли вы, на каком фоне я родом, я был профессиональным программистом более двенадцати лет. Мой лучший язык на сегодняшний день - это 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, а не вытягивать весь набор данных, который будет отображаться в сетке?