
- •Раздел 1.
- •Характеристика видов бизнеса.
- •2. Экономическое управление предприятием
- •Характеристика налоговой системы рф. Налог на добавленную стоимость.
- •Издержки. Классификация, виды и их особенности.
- •Прибыль. Сущность, виды и способы формирования и распределения
- •Рентабельность. Сущность, виды и способы расчета.
- •Понятие бизнес-плана предприятия и его основные разделы.
- •Эффективность предприятия в рыночной экономике.
- •Виртуальные предприятия. Их функционирование.
- •Общая характеристика процесса проектирования информационной системы.
- •Разработка пользовательского интерфейса.
- •Инструментальные средства проектирования информационных систем: классификация и примеры.
- •Типизация проектных решений.
- •Управление проектом информационной системы.
- •Основы функционирования важнейших видов информационного бизнеса
- •Индустрия информации, ее структура, продукция и место в экономике страны. Основные особенности продукции индустрии информации.
- •18. Основные и оборотные средства предприятий индустрии информации.
- •Информационный маркетинг как процесс коммуникации.
- •20. Правовая охрана интеллектуальной и промышленной собственности в информационной сфере.
- •Раздел VII. Права на результаты интеллектуальной деятельности и средства индивидуализации
- •Корпоративные информационные системы. Определение, структура, функции.
- •Корпоративные информационные системы. Классификация. Характеристики. Примеры.
- •Инфраструктура корпоративных информационных систем. Состав, краткая характеристика компонентов.
- •Преимущества и недостатки централизованной и распределенной модели управления данными.
- •27. Клиент-серверная и с сервисно - ориентированная информационные системы: свойства, отличительные особенности, архитектура.
- •28. Особенности проектирования и разработки oltp и olap приложений.
- •29. Особенности построения систем поддержки принятия управленческих решений (dss).
- •31. Разработка бизнес - логики на уровне сервера баз данных (хранимые процедуры, пользовательские функции, триггеры, механизмы поддержки целостности данных).
- •32. Классификация операционных систем. Управление задачами. Управление процессами и потоками в операционной системе.
- •33. Управление основной памятью. Виртуальная память и виртуальное адресное пространство приложения.
- •35. Назначение разделов основного диска. Типы и назначение динамических томов. Обеспечение отказоустойчивости динамических томов.
- •36. Протоколы локальных и глобальных сетей. Уровни сетевой архитектуры модели osi.
- •38. Понятие it-сервиса: основные требования по формированию, itil-библиотека.
- •41.Протокол sмтр. Сеанс и команды sмтр. Спецификация мiме. Кодирование в base64.
- •42. Мониторинг характеристик операционной системы.
- •Мониторинг и анализ локальных сетей.
- •44. Функциональные группы задач управления корпоративными сетями.
- •45. Формальные грамматики и языки. Синтаксические деревья. Задачи разбора и вывода.
- •46. Определение и процесс функционирования автомата с магазинной памятью.
- •47. Понятие автоматной грамматики. Построение и формальное описание конечного автомата.
- •48. Разбор с возвратами. Построение и формальное описание автомата с двумя магазинами.
- •49. Генерация объектного кода. Построение синтаксического дерева. Генерация объектного кода для линейных участков программ.
- •Виды резервирования надежности.
- •Перспективы развития информационных технологий.
- •Определение понятия информации.
- •60. Основные понятия оптимизационной экономико-математической модели
- •61. Переменные и ограничения оптимизационной экономико-математической модели(см.60)
- •Основные этапы решения оптимизационной задачи
- •67. Основные модели нейронов, применение нейронных сетей для задач распознавания образов.
- •1. Многослойные нейронные сети
- •2. Нейронные сети высокого порядка
- •3. Нейронные сети Хопфилда
- •4. Самоорганизующиеся нейронные сети Кохонена
- •5. Когнитрон
- •6. Достоинства и недостатки
- •68. Назначение врм-модуля для принятия управленческих решений. Инструментальные средства управления корпоративными знаниями.
- •Три составные части bpm
- •69.Классификация, основные свойства вi и км компонентов кис.
- •Характеристика и содержание основных этапов маркетинговых исследований. Основные методы проведения маркетинговых исследований.
- •Виды проектов маркетинговых исследований, их основные характеристики и взаимосвязь между ними.
- •Методы сбора данных. Вторичные и первичные данные, их преимущества и недостатки.
- •Виды измерительных шкал и их основные характеристики.
- •Методы выборочных исследований. Виды вероятностных и детерминированных выборок. Источники ошибок выборочных исследований.
- •Ошибки выборки
- •Ошибки наблюдений (измерений)
- •Ошибки отсутствия наблюдений
- •Анализ данных. Состав работ на этапе анализа данных. Кодирование открытых и закрытых вопросов.
- •Источники возникновения и цели реинжиниринга бизнес-процессов.
- •Оценка эффективности реинжиниринга бизнес-процессов.
- •Основные функции и свойства реинжиниринга бизнес-процессов.
- •Участники реинжиниринговой деятельности и их функции.
- •Определение понятия «бизнес - процесс».
- •82. Ресурсный подход к деятельности фирмы.
- •83.Корпоративная архитектура и ее составляющие.
- •84. Цели процессного подхода. Система терминов процессного подхода.
- •85. Применение правил выделения процессов. Пошаговое выделение процессов организации.
- •Раздел 6 «Управление ресурсами» — ресурсам процесса;
- •Раздел 7 «Выпуск продукции» — технологии процесса (учет требований потребителя, проектирование, закупки, выпуск продукции и т.Д.);
- •Раздел 8 «Измерения, анализ и улучшения» — организация мониторинга и улучшений процесса.
- •87.Управление данными
- •Управление данными: цели, задачи и основные направления
- •Иерархическая модель данных: типы структур, основные операции и ограничения
- •Сетевая модель данных: типы структур, основные операции и ограничения
- •Реляционная модель данных: типы структур, основные операции и ограничения
- •Инфологическая модель предметной области
- •4.1 Установка субд
- •4.2 Физическая организация базы данных. Файлы и файловые группы
- •4.3 Объекты базы данных
- •4.4 Модель безопасности
- •Резервное копирование и восстановление после сбоев
- •Высокая доступность данных
- •4.7 Репликация данных
- •4.8 Автоматизация административных задач
- •4.9 Мультисерверная среда
- •Разработка бизнес - логики базы данных. Хранимые процедуры и триггера
- •Целостность базы данных
- •Вопрос № 92
- •Толстый клиент
- •Тонкий клиент
- •Тонкие клиенты, работающие в терминальном режиме
- •Протоколы, используемые тонкими клиентами
- •Примеры тонких клиентов
- •Вопрос № 93
- •Вопрос № 94
- •Вопрос № 95
- •Вопрос № 96
- •97:Аутсорсинг. Классификация и модели
- •98. Промышленные сети. Требования, предъявляемые к ним.
- •99. Характеристика промышленной сети Profibus-dp
- •100. Характеристика промышленной сети Profibus-pa
- •101. Характеристика промышленной сети Profibus-fms
- •Формат кадра Базовый формат кадра данных
- •Расширенный формат кадра данных
- •107. Программируемый логический контроллер. Принцип работы. Eeprom
- •Программируемый логический контроллер. Основные характеристики. Eprom.
- •Характеристики
- •109. Составные модули пакета ArcInfo.
- •110.Геоинформационные системы Geograph
- •Расчет абсолютной эффективности
- •Учет фактора времени
- •Учет фактора неопределенности
- •Сравнение вариантов автоматизации
- •115. Система моделирования aris. Состав, возможности.
- •117. Языки bpel, uml. Возможности, сферы применения.
- •Диаграмма классов
- •Диаграмма компонентов
- •Диаграмма композитной/составной структуры
- •Диаграмма развёртывания
- •Диаграмма объектов
- •Диаграмма пакетов
- •Диаграмма деятельности
- •Диаграмма автомата
- •Диаграмма вариантов использования
- •Диаграммы коммуникации и последовательности
- •Диаграмма обзора взаимодействия
- •Диаграмма синхронизации
Вопрос № 94
Методологические подходы дизайна модели данных (Model first, Database first) для построения клиент-серверных приложений
http://easy4web.ru/?p=1255
Автор: Вячеслав Гринин | веб-программирование | 06 Июн 2011 2:24 пп
Прежде всего – вам нужно иметь установленную Visual Studio 2010 (кажется 2008 тоже подойдет) и установленный пакет ADO.NET Entity Framework 4.1 найдите его по ссылке или в поиске на microsoft.com. Теперь нам становятся доступны все возможности ADO.NET Entity Framework. Замечу, что четвертая версия отличается от более ранних, так что, если у вас установлена более ранняя версия, то не гарантирую, что у вас будет работать тот код, что я привел в статье.
Итак, создаем обычное консольное приложение. Присваиваем ему имя test1. Добавляем в проект модель данных.
Add -> New Item... -> ADO.NET Entity Data Model
, называем ее MyEFModel.edmx.
Среда разработки предлагает нам два варианта создания модели: генерация из базы данных и пустую модель. В этой статье мы собираемся рассмотреть принцип Model First, то есть начинать разработку мы будет с создания модели данных, из которой впоследствии будет сгенерирована схема данных (таблицы и связи в базе данных). А это значит, что мы выберем вариант создания пустой модели (Empty Model). После этого перед нами откроется пустое поле дизайнера модели данных. Если дизайнер не открылся, то сделайте двойной клик на модели MyEFModel.edmx в Solution Explorer’е. Итак, мы хотим спроектировать модель данных для проверки пользовательских прав на те или иные операции. А значит в модели данных у нас будут присутствовать следующие сущности: Пользователь (User), Группа (Group) и Право (Right). Каждый пользователь обязательно принадлежит какой либо группе, и при этом строго одной. Каждая группа содержит в себе несколько прав, или не содержит ни одного. Все просто – при регистрации нового пользователя создается одна сущность User, у которой есть свойство Group, привязывающее пользователя к конкретной группе пользователей(например, администраторы, модераторы, авторы, читатели, посетители). Группа в нашем случае это набор прав (таких как: “блокирует пользователя”, “редактирует чужую статью”, “создает статью”, “читает статью” и т.д.).
Перед тем как создавать сущности, давайте скажем дизайнеру модели, по каким правилам будут формироваться имена коллекций сущностей. Я вот о чем. Одновременно с сущностью в модели создается также контейнер этих сущностей, то есть по сути коллекция. Аналогия с таблицей в БД прямая: сущность – это строка в таблице, контейнер сущностей – сама таблица. Дизайнер дает имя контейнеру исходя из имени сущности (имя контейнера, впрочем, всегда можно поменять), и для сущности User, контейнер он может назвать UserSet или Users. По мне так название Users гораздо приятнее. А потому для самой модели в свойствах мы выставим Pluralize New Objects = True.
Итак, создаем сущности(сущности создаются правым кликом на диаграмме модели данных и выбором контекстного меню Add -> Entity…):
1) Entity Name = User Entity Set = Users Остальные свойства оставим без изменений. Заметим лишь, что по умолчанию дизайнер модели создает первичный ключ Id целочисленного типа, что для нас вполне приемлемо. Добавляем в эту сущность свойства(Properties): Login (Type = String, Max Length = 255) Registered (Type = DateTime) Свойства добавляются в контекстном меню самой сущности (Add -> Scalar Property).
2) Entity Name = Group Entity Set = Groups Свойства: Name (Type = String, Max Length = 255)
3) Entity Name = Right Entity Set = Rights Свойства: Description (Type = String, Max Length = 255)
Итак, теперь у нас есть три сущности, пока еще не связанные друг с другом. Займемся этим вопросом. Создаем связи (Add -> Association):
1) Association Name = UserGroup Начало: Entity = User, Multiplicity = Many Конец: Entity = Group, Multiplicity = One Это соотношения “Один ко многим”, то есть каждый пользователь может состоять только в одной группе, но в каждой группе может быть много пользователей. (Если бы у сущности Group мы выбрали Multiplicity = Zero or One, то мы бы получили отношение “Один ко многим”, но дали бы при этом возможность иметь пользователей не принадлежащих ни одной группе, то есть у пользователя свойство Group было бы Nullable).
2) Association Name = GroupRight Начало: Entity = Group, Multiplicity = Many Конец: Entity = Right, Multiplicity = Many Это соотношение “Многие ко многим”, то есть группа содержит в себе множество прав, при этом каждое право может принадлежать нескольким различным группам.
Вот
какая схема данных у нас появилась:
Обратите внимание, что после создания связей, в каждой из сущностей появились дополнительные навигационные свойства. Например, свойство Group сущности User дает нам возможность узнать группу, в которой состоит пользователь, а свойство Rights сущности Group дает нам возможность получить список всех прав группы. Таким образом, обратившись к concreteUser.Group.Rights мы получим список прав пользователя. Как видно из схемы данных, среда разработки создала также и встречные (обратные) навигационные свойства Right.Groups и Group.Users.
Ну что же, модель данных готова. Но чтобы начать писать программный код, оперирующий с данными, нужно для начала создать саму базу данных. Используя Visual Studio2010 сделать это очень легко.
Правый щелчок мыши на диаграмме, затем в контекстном меню “Generate Database from Model…“. Здесь мы можем выбрать существующее подключение к БД или создать новое. Мы создадим новое подключение (кнопка New Connection…), здесь вы выбираете сервер БД, способ и параметры аутентификации, и имя базы данных (если хотите создать новую, то просто введите имя новой БД в поле Select or enter a database name), жмем OK. (Я создал базу EFUSERS на локальном компьютере). Мастер спросит вас нужно ли создать БД, вы соглашайтесь. После этого мы снова вернемся в мастер генерации БД, убедитесь, что галочка “Save entity connection settings in App.config as” установлена и нажмите Next. После этого во вкладке DDL мастер отобразит вам DDL-скрипт схемы данных. Здесь вы нажмите Finish.
После работы мастера мы получили следующее: 1) скрипт схемы данных в файле MyEFModel.edmx.sql, 2) строка подключения MyEFModelContainer в файле App.config (откройте его и посмотрите в раздел сonnectionStrings) 3) пока еще пустая база данных EFUSERS.
Мастер всего лишь создает, но не выполняет DDL-скрипт и это правильно, потому что скрипт этот при выполнении уничтожает все данные в БД. Понятно, что при первом выполнении скрипта это нормально, ведь данных в БД еще нет. Но вот при последующих изменениях в модели данных и генерации новой схемы данных это может сыграть роковую роль и уничтожить все созданные ранее данные в базе. В последующих статьях мы еще рассмотрим способы внесения изменений в БД без потери существующих данных.
Теперь
нам нужно выполнить DDL-крипт, чтобы
создать все необходимые объекты в БД.
Вы можете выполнить файл скрипта в Query
Analizer, а можете сделать это прямо в Visual
Studio, открыв скрипт и нажав кнопку Execute
SQL в панели инструментов, или нажав
комбинацию Ctrl+Shift+E.
Студия запросит у вас параметры
подключения к БД а затем выполнит скрипт.
Открыв теперь базу в Enterprise Manager мы можем
увидеть, что там создано 4 таблицы: Users,
Rights, Groups, GroupRight.
Первые три таблицы хранят в себе сущности
трех видов, а вот четвертую Junction-таблицу
(GroupRight)
Entity Framework создал для того чтобы хранить
отношения “Многие ко многим” между
сущностями Group и Right. Как видим, при помощи
подхода Model First мы полностью ушли от
этапа проектирования таблиц в БД, всю
работу Entity Framework сделал за нас. Обратим
также внимание, что в таблице Users появилась
колонка Group_Id ссылающаяся на группу, в
которую входит пользователь, колонка
эта NOT NULL, и это гарантирует что пользователь
обязательно будет принадлежать той или
иной группе. Ниже приведена схема БД,
на которой отображены поля таблиц,
первичные и внешние ключи.
Я не буду приводить здесь DDL-скрипт, сгенерированный студией, но он доступен для ознакомления по ссылке MyEFModel.edmx
Ну вот, вся предварительная работа сделана, модель данных нарисована, схема данных создана, пустые таблицы в БД присутствуют. Осталось теперь попробовать поработать со всем этим добром.
Чтобы обратиться к данным в БД, нужно создать экземпляр контейнера модели. Класс контейнера называется MyEFModelContainer, инстанцируем его:
view plaincopy to clipboardprint?
MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer");
MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer");
В качестве аргумента конструктора мы задаем имя строки подключения к БД. Эта строка подключения представляет собой нечто большее, чем обычная ConnectionString для подключения к базе данных, потому что в нашем случае используется провайдер System.Data.EntityClient, которому для инициализации требуется гораздо больше параметров. Строка подключения была создана мастером ранее и хранится в файле App.config, строка подключения выглядит так:
metadata=res://*/MyEFModel.csdl|res://*/MyEFModel.ssdl|res://*/MyEFModel.msl;provider=System.Data.SqlClient;provider connection string="data source=.;initial catalog=EFUSERS;integrated security=True;multipleactiveresultsets=True;App=EntityFramework"
Посмотрите внимательно, строка подключения имеет в себе три обязательных параметра: metadata, provider и provider connection string. Первый параметр задает ссылки на файлы CSDL, SSDL и MSL моделей. Все эти три файла хранятся в папке edmxResourcesToEmbed(найдите ее в дереве каталогов проекта) и представляют собой XML-файлы, описывающие соответственно: CSDL – концептуальную(объектную) модель в терминах бизнес-уровня, SSDL – схема хранения (реляционная схема), MSL – схема сопоставления (то есть связи между элементами CSDL и SSDL). Собственно все эти XML-файлы объединены в один с расширением EDMX.
Параметры provider и provider connection string задают как раз привычную нам строку подключения к БД.
Конструктор класса MyEFModelContainer можно вызвать также совсем без параметров, тогда по умолчанию строка подключения будет взята из App.config и станет равна той строке подключения, для которой собственно генерировался изначально DDL-скрипт.
В качестве аргумента конструктора класса MyEFModelContainer можно подать также экземпляр класса EntityConnection, который есть по сути объектная обертка вокруг строки подключения, мы не будет усложнять код, потому что конструктор класса MyEFModelContainer все равно сделает эту работу за нас.
Итак, теперь у нас есть контейнер данных, который помимо огромного количества прочих полезностей, содержит в себе три коллекции: Users, Groups, Rights – это те самые коллекции, ради которых собственно все и затевалось. Операции, производимые нами над этими коллекциями, автоматически (ну или почти автоматически) отображаются в базу данных, добавляя, удаляя или изменяя строки в соответствующих таблицах. Попытка чтения из коллекции приведет к выполнению SELECT-запроса к соответствующей таблице.
Пока наши таблицы пусты попробуем добавить в них некоторые данные. Пусть мы хотим, чтобы существовали два пользователя: chitatel и pushkin. Первый по статусу положено только чтение статей, второму – и чтение и запись. Понятно, что никаких статей у нас пока нет, важно только само разграничение прав на операции. При этом chitatel принадлежит группе “Читатели”, а pushkin – группе “Писатели”. Таким образом нам нужно создать два права: “Читать статьи” и “Редактировать статьи”; две группы: “Читатели” и “Писатели”; двух пользователей: chitatel и pushkin. А также – создать между ними корректные связи. Приведу сразу кусок кода, который надо выполнить единожды для того, чтобы создать все необходимые записи в таблицах.
view plaincopy to clipboardprint?
// Создаем объекты прав
Right canRead = new Right()
{
Description="Читать статьи",
};
Right canWrite = new Right()
{
Description="Редактировать статьи",
};
// Создаем объекты групп
Group groupReaders = new Group()
{
Name = "Читатели"
};
Group groupWriters = new Group()
{
Name = "Писатели"
};
// Присваиваем группам соответствующие права
groupReaders.Rights.Add(canRead); // читатели могут только читать
groupWriters.Rights.Add(canRead); // писатели могут и читать
groupWriters.Rights.Add(canWrite); // и писать
// Создаем объекты пользователей и заносим их в соответствующую группу
User userChitatel = new User()
{
Login = "chitatel",
Registered = DateTime.Now,
Group = groupReaders
};
User userPushkin = new User()
{
Login = "pushkin",
Registered = DateTime.Now,
Group=groupWriters
};
// Теперь все эти существующие исключительно в памяти объекты
// добавляем в соответствующие коллекции контейнера
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
cnt.Rights.AddObject(canRead);
cnt.Rights.AddObject(canWrite);
cnt.Groups.AddObject(groupReaders);
cnt.Groups.AddObject(groupWriters);
cnt.Users.AddObject(userChitatel);
cnt.Users.AddObject(userPushkin);
// И финальный аккорд - сохраняем все изменения в БД
cnt.SaveChanges();
}
// Создаем объекты прав
Right canRead = new Right()
{
Description="Читать статьи",
};
Right canWrite = new Right()
{
Description="Редактировать статьи",
};
// Создаем объекты групп
Group groupReaders = new Group()
{
Name = "Читатели"
};
Group groupWriters = new Group()
{
Name = "Писатели"
};
// Присваиваем группам соответствующие права
groupReaders.Rights.Add(canRead); // читатели могут только читать
groupWriters.Rights.Add(canRead); // писатели могут и читать
groupWriters.Rights.Add(canWrite); // и писать
// Создаем объекты пользователей и заносим их в соответствующую группу
User userChitatel = new User()
{
Login = "chitatel",
Registered = DateTime.Now,
Group = groupReaders
};
User userPushkin = new User()
{
Login = "pushkin",
Registered = DateTime.Now,
Group=groupWriters
};
// Теперь все эти существующие исключительно в памяти объекты
// добавляем в соответствующие коллекции контейнера
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
cnt.Rights.AddObject(canRead);
cnt.Rights.AddObject(canWrite);
cnt.Groups.AddObject(groupReaders);
cnt.Groups.AddObject(groupWriters);
cnt.Users.AddObject(userChitatel);
cnt.Users.AddObject(userPushkin);
// И финальный аккорд - сохраняем все изменения в БД
cnt.SaveChanges();
}
После
выполнения приведенного кода мы получим
вот такие данные в таблицах:
Рассмотрим теперь алгоритм выборки данных из контейнера. Я для этих целей буду использовать язык встроенных запросов LINQ.Предположим, что некий пользователь вошел в систему, и нам нужно определить, имеет ли он право редактировать статьи. То есть мы должны по имени пользователя получить его группу и посмотреть, имеет ли эта группа соответствующее право.
Для этого мы выполним следующий код:
view plaincopy to clipboardprint?
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
var check = from u in cnt.Users
where u.Login == "pushkin" &&
u.Group.Rights.Any(f => f.Description == "Редактировать статьи")
select u;
Console.WriteLine(check.Count());
}
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
var check = from u in cnt.Users
where u.Login == "pushkin" &&
u.Group.Rights.Any(f => f.Description == "Редактировать статьи")
select u;
Console.WriteLine(check.Count());
}
Дословно, LINQ-запрос выбирает из БД всех пользователей, имеющих заданный логин(то есть единственного пользователя), и в правах группы в поле Description имеющих содержимое “Редактировать статьи”. Если такой пользователь существует, то check.Count() == 1, а значит пользователь имеет требуемое право. Текст SQL-запроса, выполненного в БД, выглядит так:
view plaincopy to clipboardprint?
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (N'pushkin' = [Extent1].[Login]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[GroupRight] AS [Extent2]
INNER JOIN [dbo].[Rights] AS [Extent3] ON [Extent2].[Rights_Id] = [Extent3].[Id]
WHERE (N'Редактировать статьи' = [Extent3].[Description]) AND ([Extent1].[Group_Id] = [Extent2].[Groups_Id])
))
) AS [GroupBy1]
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (N'pushkin' = [Extent1].[Login]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[GroupRight] AS [Extent2]
INNER JOIN [dbo].[Rights] AS [Extent3] ON [Extent2].[Rights_Id] = [Extent3].[Id]
WHERE (N'Редактировать статьи' = [Extent3].[Description]) AND ([Extent1].[Group_Id] = [Extent2].[Groups_Id])
))
) AS [GroupBy1]
Здесь мы видим безобразный SELECT FROM SELECT отягощенный EXISTS-ом и INNER JOIN-ом. На мой взгляд, запрос далеко не оптимален. Немного упростить запрос, убрав из него INNER JOIN можно, если искать права не по названию (“Редактировать статьи”), что в любом случае являет плохим тоном, потому что зависит от способа написания названия права, а искать по Id права, ведь согласитесь сопоставление ID и DESCRIPTION в нашей базе всегда будет сохраняться. Таким образом, упрощаем LINQ-запрос:
view plaincopy to clipboardprint?
var check = from u in cnt.Users
where u.Login == "pushkin" &&
u.Group.Rights.Any(f => f.Id == 2)
select u;
var check = from u in cnt.Users
where u.Login == "pushkin" &&
u.Group.Rights.Any(f => f.Id == 2)
select u;
И получаем слегка укороченный SQL-запрос:
view plaincopy to clipboardprint?
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (N'pushkin' = [Extent1].[Login]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[GroupRight] AS [Extent2]
WHERE (2 = [Extent2].[Rights_Id]) AND ([Extent1].[Group_Id] = [Extent2].[Groups_Id])
))
) AS [GroupBy1]
SELECT
[GroupBy1].[A1] AS [C1]
FROM ( SELECT
COUNT(1) AS [A1]
FROM [dbo].[Users] AS [Extent1]
WHERE (N'pushkin' = [Extent1].[Login]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[GroupRight] AS [Extent2]
WHERE (2 = [Extent2].[Rights_Id]) AND ([Extent1].[Group_Id] = [Extent2].[Groups_Id])
))
) AS [GroupBy1]
Я думаю, что на начальном этапе создания проекта, когда таблицы будут еще не слишком большими, подойдет и этот запрос, в дальнейшем же мы просто будем выносить все часто используемые не слишком оптимальные запросы в отдельные оптимизированные хранимые функции и процедуры. К счастью в Entity Framework 4 заложена возможность вызова хранимых процедур и функций, и не просто их вызова, а даже использования их в LINQ-запросах, что согласитесь очень хорошо.
Итак мы научились создавать модель данных, генерировать из нее схему данных, создавать сущности и делать из них выборку.
В следующей статье я планирую коснуться изменения (UPDATE) сущностей и изменения схемы данных (ALTER), создания и использования хранимых функций и процедур.
ADO.NET Entity Framework. Часть 2.
Автор: Вячеслав Гринин | веб-программирование | 19 Июн 2011 2:59 пп
Будем продолжать развивать решение, полученное в первой части статьи. Напомню, что мы спроектировали концептуальную схему данных, сгенерировали и выполнили DDL-скрипт, создавший для нас структуру хранения данных, а также научились создавать объекты в концептуальном поле и делать выборку из коллекций. Естественно, все изменения мы сохраняли в БД, обеспечив таким образом синхронность данных в концептуальном слое и в БД.
Предположим, что перед нами теперь возникла необходимость дополнить объект User новыми полями, пусть это будет FIO и BirthDate. Для этого мы снова открываем диаграмму модели *.edmx и командой Right Mouse Click -> Add -> Scalar Property выполенной для сущности User создаем два новых свойства(Properties):
FIO (Type = String, Max Length = 255, Nullable = True) BirthDate (Type = DateTime, Nullable = True)
Теперь сущность User имеет следующий вид:
После этого выполняем Right Mouse Click -> Generate Database from Model и получаем в свое распоряжение DDL-скрипт, полностью описывающий схему хранения данных для новой модели. Понятно, что более 90% скрипта будет повторять предыдущий, в таблицу Users добавятся два новых поля BirthDate и FIO:
view plaincopy to clipboardprint?
CREATE TABLE [dbo].[Users] (
[Id] int IDENTITY(1,1) NOT NULL,
[Login] nvarchar(255) NOT NULL,
[Registered] datetime NOT NULL,
[FIO] nvarchar(255) NULL,
[BirthDate] datetime NULL,
[Group_Id] int NOT NULL
);
CREATE TABLE [dbo].[Users] (
[Id] int IDENTITY(1,1) NOT NULL,
[Login] nvarchar(255) NOT NULL,
[Registered] datetime NOT NULL,
[FIO] nvarchar(255) NULL,
[BirthDate] datetime NULL,
[Group_Id] int NOT NULL
);
Еще прошу обратить внимание, что DDL-скрипт начинается с вызова DROP CONSTRAINT и DROP TABLE, выполненных для всех таблиц и внешних ключей. То есть, выполнив этот скрипт мы потеряем все накопленные на текущий момент данные. Для нашего учебного примера это не страшно, но что если изменения нужно внести в уже работающий проект?
Мы поступим следующим образом. Сгенерим с помощью этого скрипта ДРУГУЮ базу. Пусть она будет называться [EFUSERS2]. Создадим ее сначала при помощи SQL Managment Studio, после чего выполним в ней весь скрипт MyEFModel.edmx.sql, только не забудьте сначала в скрипте поменять инструкцию USE в самом начале скрипта: USE [EFUSERS2] иначе вы сами того не желая внесете изменения в существущую базу EFUSERS удалив из нее все данные.
Итак, а нашем распоряжении теперь есть две базы, одна имеет старую структуру, но содержит в себе все накопленные данные, другая имеет новую структуру, но при этом пустая. Наша задача так обновить структуру старой базы, чтобы в ней сохранились все существующие данные. Мы воспользуемся для этого утилитой Adept SQLDiff. После зппуска она предложит выбрать два подключения к БД, Primary Database (здесь мы выберем EFUSERS) и Secondary Database (выберем EFUSERS2).
После этого в графическом представлении утилита покажет нам все изменения в схеме данных, которые она сможет отыскать между двумя базами. Красным цветом будут выделена все различия между двумя БД. Осталось только сгенерировать скрипт разности, то есть тот скрипт, который после выполнения на Secondary Database внесет в нее все необходимые изменения. Скрипт это генерируется нажатием на кнопку “Show Changes to Left-hand DB”, она находится в панели инструментов прямо над деревом структуры данных. При этом скрипт будет сгенерирован только для выделенного узла дерева, а поэтому надо выделить самый верхний уровень иерархии Shema of EFUSERS. Полученный скрипт выглядит так:
view plaincopy to clipboardprint?
alter table Users add
FIO nvarchar(255),
BirthDate datetime
go
alter table Users add
FIO nvarchar(255),
BirthDate datetime
go
Этот скрипт мы должны скопировать и выполнить для базы EFUSERS. Теперь в нашем распоряжении в таблице Users появились два новых поля FIO и BirthDate. Попробуем написать фрагмент программы, задающий значения этих полей для уже существующих пользователей нашей БД.
view plaincopy to clipboardprint?
using (MyEFModelContainer cnt =
MyEFModelContainer("name=MyEFModelContainer"))
{
// Привязка к записям идет по ключам Id
User userChitatel = new User()
{
Id = 1
};
User userPushkin = new User()
{
Id = 2
};
// Привязываем объекты к записям в таблице
cnt.Users.Attach(userPushkin);
cnt.Users.Attach(userChitatel);
// Изменяем поля
userPushkin.FIO = "Пушкин Александр Сергеевич";
userPushkin.BirthDate = new DateTime(1799, 6, 6);
userChitatel.FIO = "Гринин Вячеслав Николаевич";
userChitatel.BirthDate = new DateTime(1980, 11, 12);
// Сохраняем изменения в БД
cnt.SaveChanges();
}
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
// Привязка к записям идет по ключам Id
User userChitatel = new User()
{
Id = 1
};
User userPushkin = new User()
{
Id = 2
};
// Привязываем объекты к записям в таблице
cnt.Users.Attach(userPushkin);
cnt.Users.Attach(userChitatel);
// Изменяем поля
userPushkin.FIO = "Пушкин Александр Сергеевич";
userPushkin.BirthDate = new DateTime(1799, 6, 6);
userChitatel.FIO = "Гринин Вячеслав Николаевич";
userChitatel.BirthDate = new DateTime(1980, 11, 12);
// Сохраняем изменения в БД
cnt.SaveChanges();
}
Запросы, которые в результате выполнятся в БД кратки, в них нет ничего лишнего, что не может не радовать:
view plaincopy to clipboardprint?
exec sp_executesql N'update [dbo].[Users]
set [FIO] = @0, [BirthDate] = @1
where ([Id] = @2)
',N'@0 nvarchar(255),@1 datetime,@2 int',
@0=N'Гринин Вячеслав Николаевич',
@1=''1980-11-12 00:00:00:000'',@2=1
go
exec sp_executesql N'update [dbo].[Users]
set [FIO] = @0, [BirthDate] = @1
where ([Id] = @2)
',N'@0 nvarchar(255),@1 datetime,@2 int',
@0=N'Пушкин Александр Сергеевич',
@1=''1799-06-06 00:00:00:000'',@2=2
go
exec sp_executesql N'update [dbo].[Users]
set [FIO] = @0, [BirthDate] = @1
where ([Id] = @2)
',N'@0 nvarchar(255),@1 datetime,@2 int',
@0=N'Гринин Вячеслав Николаевич',
@1=''1980-11-12 00:00:00:000'',@2=1
go
exec sp_executesql N'update [dbo].[Users]
set [FIO] = @0, [BirthDate] = @1
where ([Id] = @2)
',N'@0 nvarchar(255),@1 datetime,@2 int',
@0=N'Пушкин Александр Сергеевич',
@1=''1799-06-06 00:00:00:000'',@2=2
go
А теперь я хочу добавить еще нескольких пользователей, попробовать удалить пользователей по определенным критериям и посмотреть, какие запросы будут сгенерированы в результате.
view plaincopy to clipboardprint?
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
// Создаем объект-группу и "присоединяем" ее к БД
Group groupReader = new Group()
{
Id = 1 // читатели
};
cnt.Groups.Attach(groupReader);
// Добавляем пользователей (пока просто создаем объекты в памяти)
for (int i = 0; i < 5; i++)
{
User user = new User()
{
BirthDate = DateTime.Now,
FIO = "Пользователь " + i,
Login = "user" + i,
Registered = DateTime.Now,
Group = groupReader // Задаем группу
};
cnt.Users.AddObject(user);
}
// здесь произойдет добавление новых пользователей в БД
cnt.SaveChanges();
// Выбираем всех недавно добавленных пользователей(по их логинам,
// начинающимся с "user"). Здесь произойдет запрос
// SELECT LIKE к БД, так как мы явно вызвали метод ToList()
List<User> users = cnt.Users.Where(
u => u.Login.StartsWith("user")).ToList();
// Пробегаем в цикле по всем выбранным пользователям и удаляем их
// пока только из модели
users.ForEach(u =>
{
cnt.Users.DeleteObject(u);
});
// Здесь произойдет физическое удаление DELETE из БД
cnt.SaveChanges();
}
using (MyEFModelContainer cnt =
new MyEFModelContainer("name=MyEFModelContainer"))
{
// Создаем объект-группу и "присоединяем" ее к БД
Group groupReader = new Group()
{
Id = 1 // читатели
};
cnt.Groups.Attach(groupReader);
// Добавляем пользователей (пока просто создаем объекты в памяти)
for (int i = 0; i < 5; i++)
{
User user = new User()
{
BirthDate = DateTime.Now,
FIO = "Пользователь " + i,
Login = "user" + i,
Registered = DateTime.Now,
Group = groupReader // Задаем группу
};
cnt.Users.AddObject(user);
}
// здесь произойдет добавление новых пользователей в БД
cnt.SaveChanges();
// Выбираем всех недавно добавленных пользователей(по их логинам,
// начинающимся с "user"). Здесь произойдет запрос
// SELECT LIKE к БД, так как мы явно вызвали метод ToList()
List<User> users = cnt.Users.Where(
u => u.Login.StartsWith("user")).ToList();
// Пробегаем в цикле по всем выбранным пользователям и удаляем их
// пока только из модели
users.ForEach(u =>
{
cnt.Users.DeleteObject(u);
});
// Здесь произойдет физическое удаление DELETE из БД
cnt.SaveChanges();
}
Итак, после выполнения первой инструкции SaveChanges() выполнится следующий запрос (я оставил лишь один из пяти одинаковых запросов вставки, и немного отформатировал его для удобочитаемости):
view plaincopy to clipboardprint?
exec sp_executesql '
insert [dbo].[Users]
(
[Login],
[Registered],
[FIO],
[BirthDate],
[Group_Id])
values
(
@0,
@1,
@2,
@3,
@4
)
select
[Id]
from
[dbo].[Users]
where
@@ROWCOUNT > 0
and [Id] = scope_identity()'
,
'@0 nvarchar(255), @1 datetime, @2 nvarchar(255), @3 datetime, @4 int',
@0='user0',
@1=''2011-06-17 17:30:25:977'',
@2='Пользователь 0',
@3=''2011-06-17 17:30:25:977'',
@4=1
go
exec sp_executesql '
insert [dbo].[Users]
(
[Login],
[Registered],
[FIO],
[BirthDate],
[Group_Id])
values
(
@0,
@1,
@2,
@3,
@4
)
select
[Id]
from
[dbo].[Users]
where
@@ROWCOUNT > 0
and [Id] = scope_identity()'
,
'@0 nvarchar(255), @1 datetime, @2 nvarchar(255), @3 datetime, @4 int',
@0='user0',
@1=''2011-06-17 17:30:25:977'',
@2='Пользователь 0',
@3=''2011-06-17 17:30:25:977'',
@4=1
go
Достаточно своеобразная конструкция, давайте попробуем разобраться, что здесь произошло. Прежде всего выполняется системная процедура sp_executesql, первым аргументом для которой идет сам текст запроса, вторым аргументом – список и типы аргументов запроса, все остальные аргументы процедуры – собственно значения аргументов запроса. Надеюсь, я вас не запутал. Самое интересное здесь – это текст выполняемого запроса на вставку, он расположен со 2 по 21 строку листинга включительно. Как видно, первая часть – это сама вставка, а вторая часть – это по сути выборка scope_identity(), причем только в том случае, если вставка записи прошла успешно, о чем сигнализирует @@ROWCOUNT > 0 и тот факт, что scope_identity() присутствует среди набора Id таблицы Users.
Выборка Id последней вставленной записи нужна фреймворку, чтобы актуализировать значение поля Id в объекте user в основной программе, а столь хитрое его определение сделано чтобы удостовериться, что запись действительно вставлена в таблицу(а ведь она может и не вставиться, в случае, если не заполнены все необходимые поля объекта user).
Вернемся к рассмотрению листинга алгоритма манипуляции с данными. После выполнения операции ToList() (найдите ее в листинге), в БД отправится следующий запрос:
view plaincopy to clipboardprint?
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Login] AS [Login],
[Extent1].[Registered] AS [Registered],
[Extent1].[FIO] AS [FIO],
[Extent1].[BirthDate] AS [BirthDate],
[Extent1].[Group_Id] AS [Group_Id]
FROM [dbo].[Users] AS [Extent1]
WHERE [Extent1].[Login] LIKE N'user%'
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Login] AS [Login],
[Extent1].[Registered] AS [Registered],
[Extent1].[FIO] AS [FIO],
[Extent1].[BirthDate] AS [BirthDate],
[Extent1].[Group_Id] AS [Group_Id]
FROM [dbo].[Users] AS [Extent1]
WHERE [Extent1].[Login] LIKE N'user%'
Как видим, фреймворк преобразовал вызов метода StartsWith(“user”) в конструкцию WHERE [Login] LIKE N’user%’, что не может нас не порадовать.
Выполнение последней инструкции SaveChanges() вызовет выполнение пяти почти одинаковых запросов, похожих на этот:
view plaincopy to clipboardprint?
exec sp_executesql N'delete [dbo].[Users]
where (([Id] = @0) and ([Group_Id] = @1))',N'@0 int,@1 int',@0=8,@1=1
go
exec sp_executesql N'delete [dbo].[Users]
where (([Id] = @0) and ([Group_Id] = @1))',N'@0 int,@1 int',@0=8,@1=1
go
Здесь мы можем заметить еще одну интересную особенность – удаление происходит не по одному критерию ([Id] = @0) как мы ожидали, а по двум ([Id] = @0) and ([Group_Id] = @1). То есть в критерий WHERE зачем-то была добавлена еще и группа Group_Id. Судя по всему, Entity Framework в критерий WHERE добавляет все первичные и внешние ключи таблицы. На всякий случай.
Проверка показала, что вот такой код вообще не хочет выполняться:
view plaincopy to clipboardprint?
User user = new User()
{
Id = 29
};
cnt.Users.Attach(user);
cnt.Users.DeleteObject(user);
cnt.SaveChanges();
User user = new User()
{
Id = 29
};
cnt.Users.Attach(user);
cnt.Users.DeleteObject(user);
cnt.SaveChanges();
и на операции SaveChanges() выбрасывает вот такое исключение: Entities in ‘MyEFModelContainer.Users’ participate in the ‘UserGroup’ relationship. 0 related ‘Group’ were found. 1 ‘Group’ is expected. Что как бы намекает нам о необходимости явно указать еще и группу пользователя, что вызывает у меня некоторое недоумение. Как же мне удалить пользователя, заданного исключительно по Id? К сожалению, ничего умнее вот этого я не придумал:
cnt.Users.DeleteObject(cnt.Users.Where(u => u.Id == 30).FirstOrDefault());
cnt.SaveChanges();
cnt.Users.DeleteObject(cnt.Users.Where(u => u.Id == 30).FirstOrDefault());
cnt.SaveChanges();
Ну здесь все понятно: нашли первую (и единственную) запись, удовлетворяющую критерию Id=30, и удалили ее из контекста. При этом в БД ушел сначала SELECT, а затем его результаты были использованы для DELETE, при этом мы снова видим, что критерий по Group_Id так и остался.
view plaincopy to clipboardprint?
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Login] AS [Login],
[Extent1].[Registered] AS [Registered],
[Extent1].[FIO] AS [FIO],
[Extent1].[BirthDate] AS [BirthDate],
[Extent1].[Group_Id] AS [Group_Id]
FROM [dbo].[Users] AS [Extent1]
WHERE 30 = [Extent1].[Id]
exec sp_executesql N'delete [dbo].[Users]
where (([Id] = @0) and ([Group_Id] = @1))',N'@0 int,@1 int',@0=30,@1=1
SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Login] AS [Login],
[Extent1].[Registered] AS [Registered],
[Extent1].[FIO] AS [FIO],
[Extent1].[BirthDate] AS [BirthDate],
[Extent1].[Group_Id] AS [Group_Id]
FROM [dbo].[Users] AS [Extent1]
WHERE 30 = [Extent1].[Id]
exec sp_executesql N'delete [dbo].[Users]
where (([Id] = @0) and ([Group_Id] = @1))',N'@0 int,@1 int',@0=30,@1=1
Итак, сегодня мы научились вносить изменения в существующую модель данных, не потеряв при этом уже существующие данные. Научились обновлять, вставлять и удалять данные, а также рассмотрели структуру выполняющихся при этом SQL-запросов. По этой ссылке вы можете скачать проект, содержащий в себе весь рассмотренный в статье программный код.