Как связать таблицы в visual studio

Обновлено: 04.07.2024

В предыдущей статье вы узнали о соглашениях Code-First по конфигурации столбцов таблицы базы данных. Здесь мы рассмотрим соглашения по созданию между таблицами в базе данных. Эти соглашения определяют то, как связываются классы в модели и настраиваются внешние ключи, определяющие эти связи. Для настройки этих конфигураций в основном используется Fluent API, а не аннотации данных.

Вы уже видели ранее использование некоторых связей между таблицами. Например, при рассмотрении примера в статье “Использование Code-First” мы создали следующую модель данных:

Code-First видит в этом примере, что вы определили навигационное свойство Orders в таблице Customer, ссылающееся на коллекцию объектов Order, что говорит о создании отношения один-ко-многим (one-to-many) между этими таблицами. Так же Code-First определит автоматически эту связь, создав внешний ключ для таблицы Order, привязанный к первичному ключу CustomerId таблицы Customer.

Далее мы опишем все соглашения, которые используются в Code-First для описания связей между таблицами.

Использование навигационных свойств

Как вы видели, Code-First автоматически добавляет отношения между таблицами если видит навигационные свойства в коде модели и, необязательно, явно указанные внешние ключи. Мы подробно опишем использование внешних ключей чуть позже, а сейчас давайте остановимся на определении отношений между таблицами без внешних ключей.

Отношения между таблицами можно определить в модели только за счет использования навигационных свойств, которые могут быть указаны в обеих таблицах (двусторонняя связь, как показано в примере выше) или в одной таблице (односторонняя связь). Ниже описаны некоторые соглашения Code-First при использовании навигационных свойств:

Code-First также будет предполагать связь один-ко-многим, если навигационное свойство используется только в одной таблице, вне зависимости от типа этого свойства (т.е. если используется односторонняя связь).

Если в обеих таблицах навигационные свойства имеют тип коллекций, то Code-First предполагает наличие связи между ними многие-ко-многим (many-to-many).

Если в обеих таблицах навигационные свойства представлены в виде ссылок друг на друга, то Code-First предполагает отношение между таблицами один-к-одному (one-to-one).

В случае реализации отношения один-к-одному, вы должны будете предоставить некоторую дополнительную информацию, чтобы Code-First знал, какая сущность является основной, а какая зависимой. Если в таблицах явно не указан внешний ключ, то Code-First смоделирует отношение один-или-ноль-к-одному (zero-or-one-to-one, 0..1-1), т.е. добавление данных в главную таблицу, необязательно должно вести к добавлению данных в зависимую таблицу.

Посмотрев пример нашей модели, показанной выше, можно проследить использование этих соглашений на классах Customer и Order. Например, Code-First автоматически создаст отношение между таблицами один-ко-многим, т.к. мы использовали тип коллекции в одной таблице и простую ссылку в другой. Также можно догадаться, что в отношении этих таблиц Customer будет главной, а Order зависимой, т.е. мы можем вставить данные заказчика в таблицу Customer, не добавляя при этом для него заказы. И наоборот, в таблицу Order мы можем вставить только заказ, привязанный к конкретному покупателю.

Стоит также сказать, что без явного указания внешних ключей в коде модели, Code-First будет генерировать эти ключи автоматически, используя имя, сочетающие в себе название базовой таблицы и первичного ключа этой таблицы. Например, для таблицы Order будет сгенерирован внешний ключ Customer_CustomerId.

Большую часть конфигурации по настройке навигационных свойств мы можем выполнить с помощью Fluent API. Некоторые настройки можно выполнить и с помощью аннотаций данных, хотя в данном случае этот подход намного более ограничен, чем использование Fluent API. Например, мы можем указать для автоматически генерируемого внешнего ключа, чтобы он не поддерживал значения NULL:

Если вы запустите приложение и обновите структуру базы данных, то обнаружите, что Code-First изменил тип внешнего ключа Customer_CustomerId – теперь он не может поддерживать значения NULL:

Отключение поддержки NULL для внешнего ключа с помощью аннотаций данных

Напомню, что мы используем в качестве примера приложение, созданное в статье “Использование Code-First” и для воссоздания базы данных при изменении модели требуется либо ее ручное удаление всякий раз, когда база данных изменилась, либо использование настроек Code-First по автоматическому обнаружению изменений в модели.

Настройка отношений с помощью Fluent API может показаться несколько запутанной, если вы не потратите некоторое время, чтобы понять основные идеи. При использовании аннотаций данных для настройки отношений вы просто устанавливаете атрибуты для навигационных свойств в коде модели. Это сильно отличается от подхода с Fluent API, где вы должны в буквальном смысле настроить отношения между таблицами. Для этого используется следующий общий шаблон (он не зависит от того, хотите ли вы использовать одностороннюю или двустороннюю связь):

Параметр Multiplicity в этом шаблоне указывает на окончание используемых методов Has… и With…, он может иметь следующие значения: Optional (навигационное свойство может иметь один или ноль экземпляров), Required (навигационное свойство может иметь только один экземпляр) и Many (навигационное свойство содержит коллекцию экземпляров).

Соответственно Entity Framework определяет следующий набор методов, определяющих настройки первичных навигационных свойств:

Для настройки отношений во второй таблице, используются следующие методы:

При вызове этих методов в качестве параметра, им передается делегат, в котором указывается навигационное свойство. При использовании односторонней связи (когда в одной из таблиц отсутствует навигационное свойство), можно вызвать соответствующий метод без параметров. Ниже показан пример настройки навигационных свойств для наших таблиц, который соответствует автоматическим соглашениям Code-First. Т.е. фактически он создает связь один-ко-многим между нашими таблицами, где таблица Customer является главной:

Ранее мы показали, как с помощью аннотаций данных можно ограничить поддержку NULL значений для автоматически сгенерированного внешнего ключа. Давайте реализуем это с помощью Fluent API:

В этом примере мы просто поменяли вызов метода WithOptional() на WithRequired(). Если вы запустите пример и посмотрите на структуру таблицы Orders, то увидите, что ее структура аналогична той, которая показана на первом рисунке в статье, когда мы использовали атрибуты метаданных. На рисунке ниже наглядно показано, как выполняется этот запрос:

Выполнение запроса Fluent API для определения отношений между таблицами

Указание внешних ключей

Ранее мы рассмотрели, как реализовать отношения между таблицами без прямого использования внешних ключей. Например, класс Order содержит свойство-ссылку на класс Customer, но при этом в этом классе не определено свойство, которое будет использоваться в качестве внешнего ключа для связи между таблицами. В этом случае мы видели, что Code-First автоматически сгенерирует внешний ключ за вас. Теперь давайте рассмотрим, что происходит, если мы явно задаем внешний ключ.

Первое что мы сделаем, это добавим новое свойство CustomerId в класс модели Order:

Запустите приложение. Code-First поймет, что вы внесли изменения в модель и воссоздаст базу данных. Если вы рассмотрите структуру столбцов таблицы Order, то заметите, что Code-First автоматически распознал поле CustomerId как внешний ключ и заменил автоматически генерируемый ключ Customer_CustomerId на CustomerId:

Явное указание внешнего ключа для таблицы

Как вы понимаете, Code-First использует определенные соглашения при поиске внешнего ключа в свойствах модели. Эти правила основаны на имени свойства и придерживаются следующих шаблонов:

В нашем случае имя свойства CustomerId подходит под первое правило, т.к. в главной таблице Customer используется одноименное свойство, которое является первичным ключом таблицы. Также стоит отметить, что Code-First не чувствителен к регистру символов при поиске внешнего ключа, т.е. если бы в таблице Order у нас было бы свойство CusToMeRId, то Code-First автоматически бы распознал его как внешний ключ.

К данному моменту может возникнуть вопрос, зачем может понадобиться явное определение внешних ключей в классе модели, если Code-First способен автоматически создавать эти ключи? Ответом на этот вопрос будет то, что иногда гораздо удобней получить доступ к родительскому объекту в коде через внешний ключ, нежели чем через ссылку. Например, в коде вы могли бы создать новый объект Order и указать через ссылку объект Customer, к которому он должен принадлежать:

Очевидно, для того, чтобы загрузить объект Customer в экземпляр myCustomer, вам необходимо будет сначала обратиться к базе данных. Использование внешнего ключа позволяет просто указать идентификатор заказчика не ссылаясь на него. Чтобы получить идентификатор заказчика, зачастую нужно также обратиться к базе данных, но бывают случаи, когда у вас есть доступ к значению ключа этого объекта. Например, если мы знаем что заказ myOrder принадлежит заказчику с идентификатором 5, мы могли бы использовать внешний ключ вместо ссылки на объект:

Кроме того, при использовании ссылки иногда возникает более серьезная ошибка. Entity Framework отслеживает состояние объектов сущностных классов и при их изменении помечает объект, изменяя свойство DbEntityEntry.State. Если вы создадите новый объект myOrder, укажите в нем ссылку на уже существующий в памяти объект myCustomer и попытаетесь сохранить объект myCustomer в базе данных, то EF пометит состояние этого объекта как Added, а не Modified, т.к. в коде изменился список заказов, связанных с этим покупателем и EF предполагает, что был создан новый покупатель. В результате в таблицу будет добавлен новый заказчик, хотя предполагалось просто добавить заказ для уже существующего заказчика. Эту проблему можно избежать либо сохранив в базе данных только объект myOrder, либо используя внешний ключ.

Есть еще один момент, который нужно упомянуть при обсуждении соглашений Code-First по внешним ключам. Когда ранее мы не использовали внешних ключей, Code-First автоматически генерировал ключ, который поддерживал значения NULL. Если вы взгляните на рисунок выше, то увидите, что внешний ключ CustomerId в таблице Order имеет не обнуляемый тип NOT NULL, поэтому вы не сможете сохранить новый заказ не указав идентификатор покупателя (используя внешний ключ или ссылку). Тем не менее, можно явно указать, что внешний ключ должен поддерживать значения NULL. Для этого измените тип свойства CustomerId в классе модели Order на обнуляемый тип данных:

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

Настройка внешних ключей в обход соглашениям Code-First

Иногда может возникнуть вопрос, что происходит, если имя вашего внешнего ключа не соответствует соглашениям Code-First? Например, вы могли бы использовать в таблице Order внешний ключ с именем UserId, как показано ниже:

Если вы запустите приложение и воссоздадите базу данных, то увидите, что Code-First проигнорировал поле UserId и создал автоматически генерируемый внешний ключ Customer_CustomerId, а поле UserId было добавлено как обычный столбец. Вы можете решить эту проблему используя атрибут ForeignKey в классе модели данных, как показано в примере:

В конструкторе этого атрибута указывается имя навигационного свойства, если оно имеется в классе модели. Альтернативным способом является применение атрибута ForeignKey к навигационному свойству:

В данном случае в конструкторе указывается имя свойства, являющегося внешним ключом. В Fluent API используется специальный метод HasForeignKey(), как показано в примере ниже:

Указание произвольного внешнего ключа

Работа с обратными навигационными свойствами

Пока мы использовали по одному навигационному свойству между двумя классами модели, Code-First понимал, как настроить отношения между ними. Существует такие случаи, когда между двумя таблицами базы данных нужно определить несколько отношений. Например, таблица Customers могла бы ссылаться на все заказы, на обработанные заказы (которые оплатил покупатель) и необработанные заказы. Логичнее всего решить данную проблему, это просто добавить новый столбец, например IsProcess, в таблицу Orders, который имел бы логическое значение и указывал бы на то, обработан заказ или нет. Но также эту проблему можно решить использовав три внешних ключа, связывающих эти таблицы.

В модели классов это решение будет выглядеть следующим образом:

В данном примере Code-First не сможет автоматически распознать связь между навигационными свойствами этих классов. Если вы выполните этот пример, то увидите, что в созданной таблице Orders было добавлено пять внешних ключей – по одному для каждого несвязанного навигационного свойства, и один ключ для связанных свойств Orders и Customer (если вы удалите настройку Fluent API, показанную ранее, в которой мы привязали эти свойства и указали внешний ключ, то Code-First сгенерирует 6 внешних ключей).

Отсутствие связи между навигационными свойствами

Согласно соглашениям, Code-First может самостоятельно определить двунаправленную связь между таблицами, только когда существует всего одна пара навигационных свойств. В нашем примере их несколько, поэтому Code-First создаст по одному внешнему ключу для каждого навигационного свойства.

Теперь будет создано три внешних ключа, как и требовалось:

Настройка нескольких отношений между таблицами

Использование однонаправленной связи между таблицами

В предыдущих примерах мы рассмотрели способы создания двунаправленных связей между таблицами, т.е. когда в обоих классах модели определяется навигационное свойство. Тем не менее определение пары навигационных свойств не является обязательным при работе с Entity Framework. В нашей модели мы можем указать ссылку только в одном классе, например, в классе Customer оставить ссылку на коллекцию объектов Order, а в классе Order удалить ссылку на Customer:

В этом примере мы не стали удалять внешний ключ CustomerId, благодаря чему, Entity Framework четко определяет связь между таблицами с использованием этого ключа, используя соглашения, описанные выше. Теперь давайте сделаем еще один шаг и удалим внешний ключ из таблицы Order:

Ранее мы уже сказали, что по соглашению Code-First сгенерирует автоматически внешний ключ, если он не объявлен явно в классе модели. Это же соглашение работает, если мы используем однонаправленную связь между таблицами. Класс Customer по прежнему имеет навигационное свойство, определяющие его отношение с Order, поэтому будет генерироваться внешний ключ с именем Customer_CustomerId в таблице Orders.

Что будет если мы захотим удалить оба навигационных свойства, а использовать для связи явно заданный внешний ключ? Сама платформа Entity Framework поддерживает этот сценарий, но не подход Code-First. В Code-First требуется для создания отношений определить как минимум одно навигационное свойство, иначе свойство модели, которое мы планировали использовать как внешний ключ, будет просто преобразовано в столбец в таблице и отношения между таблицами не будут созданы.

Теперь давайте рассмотрим случай, когда мы явно указываем внешний ключ в зависимой таблице и при этом желаем изменить его имя, которое не будет соответствовать соглашениям Code-First по именованию внешних ключей, например:

Как мы описывали раньше, чтобы явно указать классу Order, что UserId является внешним ключом, можно использовать атрибут ForeignKey и передать ему имя навигационного свойства в параметре. Что делать, если мы используем одностороннюю связь и в классе Order не используем навигационное свойство?

Для решения этой проблемы мы можем использовать этот атрибут в главной таблице к навигационному свойству Orders или использовать Fluent API, как показано в примере ниже:

Обратите внимание, что при использовании Fluent API в вызове метода WithRequired() мы не передаем параметр делегата с выбором навигационного свойства из модели, т.к. используем одностороннюю связь и у нас отсутствует навигационное свойство в классе Order.

Набор данных, содержащий связанные таблицы данных, использует DataRelation объекты для представления связи «родители-потомки» между таблицами и для возвращения связанных записей друг от друга. При добавлении связанных таблиц в наборы данных с помощью мастера настройки источника или Конструктор наборов данных создает и настраивает DataRelation объект.

DataRelationОбъект выполняет две функции:

Он может сделать доступными записи, связанные с записью, с которой вы работаете. Он предоставляет дочерние записи в родительской записи ( GetChildRows ) и родительской записи при работе с дочерней записью ( GetParentRow ).

Он может применять ограничения для ссылочной целостности, например удалять связанные дочерние записи при удалении родительской записи.

Важно понимать разницу между истинным соединением и функцией DataRelation объекта. В случае истинного объединения записи берутся из родительской и дочерней таблиц и помещаются в один плоский набор записей. При использовании DataRelation объекта новый набор записей не создается. Вместо этого DataRelation отслеживает связь между таблицами и сохраняет синхронизацию родительских и дочерних записей.

Объекты и ограничения DataRelation

DataRelationОбъект также используется для создания и принудительного применения следующих ограничений.

Ограничение UNIQUE, которое гарантирует, что столбец в таблице не содержит дубликатов.

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

Ограничения, указываемые в DataRelation объекте, реализуются путем автоматического создания соответствующих объектов или настройки свойств. При создании ограничения внешнего ключа с помощью DataRelation объекта экземпляры ForeignKeyConstraint класса добавляются в DataRelation ChildKeyConstraint свойство объекта.

Ограничение UNIQUE реализуется либо путем простого присвоения Unique свойству столбца данных значения true или путем добавления экземпляра UniqueConstraint класса к DataRelation ParentKeyConstraint свойству объекта. Сведения о приостановке ограничений в наборе данных см. в разделе Отключение ограничений при заполнении набора данных.

Правила ссылочной целостности

В рамках ограничения внешнего ключа можно указать правила ссылочной целостности, применяемые в трех точках:

При обновлении родительской записи

При удалении родительской записи

Когда изменение принято или отклонено

Правила, которые можно сделать, указаны в Rule перечислении и перечислены в следующей таблице.

Правило ограничения внешнего ключа Действие
Cascade Изменения (обновление или удаление), внесенные в родительскую запись, также вносятся в связанные записи в дочерней таблице.
SetNull Дочерние записи не удаляются, но внешний ключ в дочерних записях имеет значение DBNull . С помощью этого параметра дочерние записи можно оставить как потерянные, т. е. они не имеют связи с родительскими записями. Примечание. Использование этого правила может привести к недопустимым данным в дочерней таблице.
SetDefault Внешнему ключу в связанных дочерних записях присваивается значение по умолчанию (как установлено DefaultValue свойством столбца).
None В связанные дочерние записи не вносятся изменения. При использовании этого параметра дочерние записи могут содержать ссылки на недопустимые родительские записи.

Дополнительные сведения об обновлениях в таблицах наборов данных см. в разделе Сохранение данных в базе данных.

Отношения только для ограничений

При создании DataRelation объекта можно указать, что отношение должно использоваться только для принудительного применения ограничений, то есть оно также не будет использоваться для доступа к связанным записям. Этот параметр можно использовать для создания набора данных, который является немного более эффективным и содержит меньше методов, чем один с возможностью связанных записей. Однако доступ к связанным записям будет невозможен. Например, отношение только с ограничением не позволяет удалить родительскую запись, которая по-прежнему имеет дочерние записи, и вы не сможете получить доступ к дочерним записям через родительский элемент.

Создание связи данных вручную в конструктор наборов данных

при создании таблиц данных с помощью средств проектирования данных в Visual Studio связи создаются автоматически, если данные могут быть собраны из источника данных. При добавлении вручную таблиц данных из вкладки набор данных панели элементов может потребоваться создать связь вручную. Сведения о создании DataRelation объектов программным путем см. в разделе Добавление связейданных.

Связи между таблицами данных отображаются в виде линий в Конструктор наборов данных с глифом "один ко многим". По умолчанию имя связи не отображается в области конструктора.

Отображаемые на компьютере имена или расположения некоторых элементов пользовательского интерфейса Visual Studio могут отличаться от указанных в этой статье. Возможно, вы используете другой выпуск Visual Studio или другие параметры среды. Дополнительные сведения см. в разделе Персонализация среды IDE.

Создание связи между двумя таблицами данных

Откройте свой набор данных в Конструкторе наборов данных. Дополнительные сведения см. в разделе Пошаговое руководство. Создание набора данных в конструктор наборов данных.

Перетащите объект связи с панели элементов набора данных на дочернюю таблицу данных в связи.

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

Выберите родительскую таблицу из поля Родительская таблица . Родительская таблица содержит записи на стороне «один» связи «один ко многим».

Убедитесь, что в поле дочерняя таблица отображается правильная дочерняя таблица. Дочерняя таблица содержит записи на стороне «многие» связи «один ко многим».

Введите имя связи в поле имя или оставьте имя по умолчанию на основе выбранных таблиц. Это имя фактического DataRelation объекта в коде.

Выберите столбцы, которые объединяют таблицы в списках Ключевые столбцы и Внешние ключевые столбцы .

Выберите, следует ли создать связь, ограничение или и то, и другое.

Установите или снимите флажок Вложенная связь . При выборе этого параметра свойству присваивается значение Nested true , после чего дочерние строки отношения вкладываются в родительский столбец, когда эти строки записываются в виде XML-данных или синхронизируются с XmlDataDocument . Дополнительные сведения см. в разделе вложенность связейданных.

Задайте правила, которые должны применяться при внесении изменений в записи в этих таблицах. Дополнительные сведения см. в разделе Rule.

Отображение имени отношения в конструктор наборов данных

Откройте свой набор данных в Конструкторе наборов данных. Дополнительные сведения см. в разделе Пошаговое руководство. Создание набора данных в конструктор наборов данных.

В меню данные выберите команду Показать связи меток , чтобы отобразить имя связи. Очистите эту команду, чтобы скрыть имя связи.

В данной теме показано, как создавать связь (отношение) между таблицами по некоторому полю. Данная тема базируется на знаниях предыдущих тем:

Содержание

Поиск на других ресурсах:

Условие задачи

В базе данных заданы две таблицы с именами Source и Emission. Таблица Source определяет источник загрязненных выбросов. Таблица Emission определяет время выбросов и число загрязненных выбросов, которое было сформировано источником.

Структура таблиц следующая.

Название поля Тип данных Комментарий
ID_Source int Ключевое поле, уникальное поле (счетчик), первичный ключ
Name char[50] Название, строка символов
Address char[100] Адрес, строка символов

Название поля Тип данных Комментарий
ID_Emission int Ключевое поле, уникальное поле (счетчик)
ID_Source int Внешний ключ, значение Source.ID_Source
count float Количество выбросов
Text char[100] Комментарий
date datetime Дата и время выбросов

Выполнение

1. Запуск Microsoft Visual Studio

Запустить систему визуальной разработки приложений Microsoft Visual Studio .

2. Создание/подключение базы данных

После подключения окно Server Explorer будет иметь вид, как показано на рисунке 1.

Visual Studio Server Explorer база данных

3. Поля ID_Source и ID_Emission

Следует отметить, что поля ID_Source и ID_Emission есть уникальными счетчиками. Такие поля используются в базах данных для обеспечения уникальности каждой записи таблицы.

Поле ID_Source таблицы Source есть первичным ключом.

Подробный пример создания уникального поля, которое есть счетчиком, описывается в статье:

4. Установление связи между таблицами

По умолчанию, система MS Visual Studio запрещает сохранение изменений, которые требуют повторного создания таблиц. Чтобы разрешить вносить изменения в таблицы нужно настроить (снять выделение) опцию

Для этого нужно выполнить следующую последовательность шагов:

После выполненных действий можно создавать связь между таблицами.

Visual Studio опция изменения база данных

4.2. Установление первичного ключа ( Primary Key ) в таблице Source

Как видно из структуры таблиц (рисунок 1) общим для таблиц есть поле ID_Source. Связь между таблицами будет осуществляться по этому полю.

Чтобы установить первичный ключ нужно выполнить такие действия:

Visual Studio команда таблица определение

Visual Studio первичный ключ таблица

Рис. 4. Установление первичного ключа в таблице Source

Visual Studio поле таблица первичный ключ

Рис. 5. Поле ID_Source в таблице Source после установления первичного ключа

В таблице Emission не обязательно устанавливать первичный ключ.

4.3. Создание связи между таблицами по полю ID_Source

Visual Studio команда диаграмма добавить

Рис. 6. Команда добавления новой диаграммы

SQL Server таблица диаграмма

После выполненных действий окно диаграммы будет иметь вид, как показано на рисунке 8. Пока что таблицы не имеют связи между собой.

База данных SQL Server таблица

Рис. 8. Таблицы Source и Emission

Чтобы начать устанавливать отношение между таблицами, нужно сделать клик на поле ID_Source таблицы Source, а потом (не отпуская кнопку мышки) перетянуть его на поле Source таблицы Emission .

В результате последовательно откроются два окна: Tables and Columns (рисунок 9) и Foreign Key Relationship (рисунок 10), в которых нужно оставить все как есть и подтвердить свой выбор на кнопке OK .

Visual Studio связь таблица

Рис. 9. Окно настройки параметров связи (отношения) между таблицами

Visual Studio отношение таблица

Рис. 10. Настройка свойств соединения FK_Emission_Source

4.4. Диаграмма связи

После создания связи окно диаграммы будет иметь вид, как показано на рисунке 11.

SQL Server отношение таблица рисунок

Рис. 11. Вид диаграммы после установки отношения (связи) между таблицами Source и Emission

Как видно из рисунка, конец соединения (отношения), что примыкает к таблице Source имеет вид ключа. А конец соединения, которое примыкает к таблице Emission имеет вид знака бесконечность ∞ .

Это означает, что в таблице Source числовое значение в поле ID_Source может встречаться только один раз. А в таблице Emission числовое значение ID_Source может повторяться (бесконечное количество раз). Таким образом можно представить любое множество уникальных объектов, которые имеют свойство повторяться в некоторой предметной области.

После закрытия диаграммы ее нужно сохранить под некоторым именем, например Diagram1 (рисунок 12). Система выдаст соответствующее окно уточнения.

SQL Server диаграмма имя

Рис. 12. Задание имени для диаграммы

SQL Server таблица база данных

Рис. 13. Окно сохранения таблиц в базе данных в связи с изменениями

Visual Studio команда отношение таблица

Рис. 14. Команда редактирования связей (отношений) между таблицами

5. Программное управление данными

После создания связи (отношения) между таблицами можно создавать проект, который будет управлять данными в таблицах. Но это уже совсем другая тема.

Описание: image473

Связи между таблицами можно легко определить прямо внутри диаграммы: надо просто перетащить столбец первичного ключа из одной таблицы в столбец внешнего ключа другой таблицы. При этом автоматически появятся два диалоговых окна: Foreign Key Relationships и Tables and Columns (это те же самые диалоговые окна, которые используются для созда­ния внешних ключей в конструкторе Table Designer). На рис. 18.8 показано создание обыч­ной связи "один-ко-многим" между таблицей категорий и таблицей заказов. Таблица заказов имеет столбец идентификатора категории, который будет внешним ключом для первичного ключа таблицы категорий.

После фиксации назначений столбцов вы завершаете создание связи при помощи изменения (при необходимости) свойств самой связи в диалоговом окне Foreign Key Relationships (рис. 18.9).

Связи на диаграмме изображаются в виде линии между двумя таблицами. Линия обозначает направление связи ключиком на стороне первичного ключа и символом бесконечности на стороне внешнего ключа (сторона "многие"). На рис. 18.10 показана связь между таблицей категорий заказов и таблицей самих заказов (в таком виде, как она выглядела бы в визуаль­ном конструкторе Database Diagram Designer).

elected RelM icnship:

Editing piupertrcs for new relationship. The Tabies And Cclumru Sptt ficati зп property needs to be Mled in before the new relationship will be accepted

Che;V E i sting Date On Creatrc Yes E Tables And Columns Specical

E Dstebase Designer

Enforce Foi Repncaticri

Enforce Foreign Key Constraint Yes Ш INSERT And UPDATE Spet .fiLat E Identity

.Name V FffProductProduclC alegory

Описание: image474

Descr.ptton

По умолчанию связи поддерживают ссылочную целостность. То есть они предотвра­щают любое действие (вставку, обновление, удаление), которое могло бы привести к несовпадению ключей двух связанных таблиц. Сюда входят: вставка такого значения внешнего ключа, которое не существует как первичный ключ связанной таблицы; из­менение такого значения первичного ключа, на которое есть ссылка как на значение внешнего ключа и т. д.

Вы можете управлять ссылочной целостностью при помощи настройки значения En­force Foreign Key Constraint, которое имеется в диалоговом окне Foreign Key Rela­tionship. Связи, которые не обеспечивают ссылочной целостности, изображаются в визуальном конструкторе Diagram Designer как линии с каемкой (а не сплошные). Не­обходимо также отметить, что Diagram Designer будет показывать только те связи, которые были явным образом определены (так, как это было описано в предыдущих

разделах). Если дать внешним и первичным ключам аналогичные названия, то это не приведет к автоматическому созданию связи.

Кроме связи "один-ко-многим" в визуальном конструкторе Database Diagram Designer вы можете создать связи "один-к-одному", "многие-ко-многим" и рефлексивные.

Цель урока: Изучить основные принципы работы с базой данных. Краткое описание реляционной модели баз данных. Работа с базой данных (создание таблиц, связей в VS 2012). Команды INSERT, UPDATE, DELETE, SELECT. Использование LinqToSql и Linq. Создание репозитария, IRepository, SqlRepository.

Что такое БД

Реляционная база данных — база данных, основанная на реляционной модели данных. Реляционность – это отношения (связи) от англ. relation.

Таблицы
  • у таблицы есть имя (уникальное)
  • нет двух одинаковых строк
  • столбцы имеют разные наименования (нет двух одинаковых столбцов)
  • порядок строк в таблице произвольный (т.е. не надо учитывать порядок строк, если не задана сортировка)
  • Имя столбца
  • Тип данных для этого столбца
Связи
  • Первичный ключ – это набор столбцов (атрибутов) таблицы, однозначно определяющих уникальность строки. Обычно это одно поле, называется ID. Оно является автоикрементным, т.е. при попытке добавления записи, там автоматически вставляется 1, 2, 3, 4… n+1, где n – это значение последнего добавленного ID.
  • Внешний ключ – это набор столбцов (атрибутов) таблицы, которые однозначно определяют уникальность строки в другой таблице. Опять же это обычно одно поле, названное [Имя таблицы]ID. Но не является автоинкрементным.
  • Прописана связь между первичным ключом и внешним ключом.
  • Один-к-одному. Т.е. одной строке в таблице соответствует одна строка в другой таблице. Это редко используется, но используется. Например, в одной таблице данные о пользователе, а в другой — дополнительные данные о том же пользователе. Такой вариант необходим, чтобы манипулировать, по необходимости, меньшим количеством данных.
  • Один-ко-многим. Одной строк в таблице A соответствует одна или несколько строк в таблице B. Но одной строке в таблице B соответствует только одна строка в таблице A. В этом случае в таблице B существует внешний ключ, который однозначно определяет запись в таблице A.
  • Многие-ко-многим. Одной строке в таблице А соответствует одна или несколько строк в таблице В, что истинно и в обратном. В данном случае создается дополнительная таблица со своим первичным ключом, и двумя внешними ключами к таблице A и B.
Создание простой схемы в БД

Создадим БД в VS 2012:


Назовем её LessonProject, и добавим 3 таблицы Role User и UserRole.
Создадим таблицу Role:



Для строковых значений используем тип nvarchar(n), где n – максимальная длина строки, обычно используется от 50 до 500. Для больших текстовых строк используется nvarchar(MAX).

Устанавливаем первичный ключ:


Задаем для ID автоинкремент:


Подобным образом создаем таблицу User:

Поле Тип поля
ID int
Email nvarchar(150)
Password nvarchar(50)
AddedDate datetime
ActivatedDate datetime (null)
ActivatedLink nvarchar(50)
LastVisitDate datetime (null)
AvatarPath nvarchar(150) (null)

Создаем таблицу UserRole:

Поле Тип поля
ID int
UserID int
RoleID int


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


В свойствах INSERT And UPDATE Specification выставляем On Update/On Delete свойства Cascade:


Это необходимо для того, чтобы при изменении/удалении столбца из таблицы Role все связанные с этой строкой строки таблицы UserRole должны быть изменены или удалены.

Аналогичную связь мы устанавливаем с таблицей User.

Таким образом, таблицы Role и User имеют отношения многие ко многим через таблицу UserRole. Т.е. у одного пользователя может быть больше одной роли, и одна и та же роль может быть у нескольких пользователей.

SELECT, INSERT, UPDATE, DELETE.

В реляционных базах данных используется язык запросов SQL.

Есть 4 основные команды для манипулирования данными — SELECT, INSERT, UPDATE, DELETE

SELECT – для выбора данных и таблиц.
Пример:

INSERT — Добавление строк в таблицу
Пример:

UPDATE – изменение значений в таблице
Пример:

DELETE – удаление строк из таблицы
Пример:

LinqToSQL и Linq.

Создадим проект LessonProject.Model для работы с БД типа ClassLibrary.
Добавляем LINQ to SQL Classes тип, называем LessonProejctDb.dbml


Открываем объект, выделяем все таблицы и мышкой переносим на холст:


  • классы, готовые к использованию в работе с БД
  • визуальное отображение таблиц и связей

Добавим несколько данных в таблицу Role и User:

1 admin Админ
2 customer Заказчик

ID UserID RoleID
1 1 1
2 1 2
3 2 2

Создадим консольный проект Lesson3 и подключим LessonProject.Model. Добавим сборку System.Configuration и System.Data.Linq. Проинициализируем context и выведем данные о ролях:


Для добавления строки в Role делаем так:

Для удаления строки в Role делаем так:

Для изменения данных делаем так:

Для манипуляции данных используется язык запросов Linq. Мы рассмотрим только некоторые основные функции Linq. Linq применяется для типов реализующий интерфейс IQueryable<>

    .Where() – основная функция фильтрации. Возвращает тип IQueryable. Условие внутри должно возвращать булево значение (bool).

Создание репозитория IRepository, SqlRepository.

Собственно с БД мы уже можем работать, только теперь нужно отделить модель данных от конкретной реализации, т.е. наши контроллеры про context и System.Data.Linq вообще не должны ничего знать.

Для этого создадим интерфейс IRepository, где будет дан доступ к данным, а также выведены методы для создания, изменения и удаления этих данных.


Реализацию назовем SqlRepository. Так как мы с данным контекстом SqlRepository не хотим особо связывать, то добавим Ninject модуль в проект LessonProject.Model:


Создадим класс SqlRepository:

Прежде, чем реализовать доступ ко всем таблицам, создание, удаление и изменение, подумаем о том, что файл этот будет выглядеть громадным и неуклюжим. Таким кодом будет управлять тяжело физически. Так что сделаем отдельную папку SqlRepository и SqlRepository класс сделаем partial, а в папке создадим реализации интерфейса IRepository, разбитые по каждой таблице. Назовем файл Role:

Небольшой проект содержит от 10 до 40 таблиц, большой проект от 40, и всё это хотелось бы как-то автоматизировать. Создадим несколько сниппетов, для IRepository и для SqlRepository. Сниппеты – это уже готовые шаблоны кода, которые вызываются с помощью intelliSence, и позволяют быстро создавать код.

Сниппеты

Для IRepository таблиц, создадим table.snippet:


Для SqlRepository создадим сниппет sqlTable.snippet:

Для того, чтобы добавить code-snippet. откроем TOOLS -> Code Snippet Manager… (Ctrl-K, B). В окне нажимаем Import и импортируем оба сниппета в My Code snippet:


Finish, OK.
Используем для таблиц User и UserRole.



Осталось прописать только поля для Update [имя таблицы], но это уже меньше работы.

Proxy

Как видим, классы, которые мы используем, являются partial, поэтому их можно дополнить. Создадим, подобно SqlRepository, папку Proxy, где будем размещать partial классы. Например, для класса User создадим метод, который автоматически генерирует строку, требуемую для активации пользователя:

Добавим строку доступа к БД в web.Config:

Проинициализируем работу с БД в Ninject:

Применяем InRequestScope(). Т.е. каждый запрос будет использовать отдельный объект SqlRepository. Это позволит избежать коллизий при исполнении.Объявляем IRepository в контроллере:

Читайте также: