80 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Реализация ООП-наследования в классах, работающих с SQL и MS Entity Framework

Настройка привязки модели к базе данных

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

Использование DbSet является не единственным способом, с помощью которого связывателю модели можно указать на сущностный класс. Вообще существует три разных способа:

Использовать типизированное свойство DbSet в классе контекста.

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

Можно использовать настройки Fluent API.

Допустим у нас имеется следующая простая модель:

Если вы использовали эту модель ранее, удалите ссылки на сущностные объекты DbSet в классе контекста. Чтобы указать связывателю модели, что класс Customer должен отображаться на таблицу в базе данных, можно использовать свойство Configurations объекта DbModelBuilder, как показано в примере ниже:

Методу Add() передается объект EntityTypeConfiguration, типизированный нужным классом модели. Если вы помните, при рассмотрении подхода Code-First мы использовали унаследованные классы конфигурации от EntityTypeConfiguration. Экземпляры этих классов также можно передавать методу Add() объекта конфигурации.

Установка классов и свойств модели без привязки к таблице

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

Что делать, если нам нужно указать Code-First, что класс Order не должен отображаться в базе данных? Для этих целей в аннотациях данных используется специальный атрибут NotMapped, который применяется к классу модели. В Fluent API это реализовано с помощью метода Ignore() класса DbModelBuilder. В примере ниже показано, как использовать эти средства:

Если вы запустите этот пример, то в базе данных будет создана только таблица Customer. Свойство Customer.Orders фактически больше не является навигационным, т.к. класс Order больше не является объектом базы данных.

Entity Framework также позволяет не отображать конкретные свойства класса модели – зачастую, в классах требуется использовать дополнительные свойства, которые не должны быть отражены на столбцы таблицы базы данных. Для этого используются также, атрибут NotMapped и метод Ignore(), только они указываются на уровне свойства, а не на уровне класса:

Привязка свойств и их доступность

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

Скалярные свойства

Скалярные свойства будут отображаться на столбцы только в том случае, если их тип поддерживается в модели EDM (Entity Data Model), иначе говоря, если этот тип можно будет преобразовать в SQL-совместимый тип данных. Допустимыми типами EDM являются: Binary, Boolean, Byte, DateTime, DateTimeOffset, Time, Decimal, Double, Guid, Int16, Int32, Int64, SByte, Single и String. Скалярные свойства, которые не могут быть отображены на тип EDM игнорируются (например, перечисления и целые числа без знака – ulong, long и т.д.)

Модификаторы доступа и поддержка чтения/записи для свойств

Напомню, в C# поддержка к записи для свойств определяется оператором set, к чтению – оператором get. Ранее в каждой модели данных мы использовали автоматически определяемые свойства. Также, каждое свойство помечается модификатором доступа: public, protected, internal или private. Code-First использует следующие соглашения в плане доступности свойств и поддержки чтения/записи:

Все открытые свойства (модификатор public) будут автоматически отображены на столбец таблицы в базе данных.

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

Закрытые свойства (помеченные модификаторами private, internal или protected) автоматически не отображаются на таблицу, но это поведение можно изменить используя Fluent API.

Для конфигурации закрытых свойств вы должны быть в состоянии получить доступ к свойству в том месте, где вы выполняете конфигурацию. Например, если у вас есть класс Customer со свойством LastName, помеченным модификатором internal и он находится в той же сборке что и класс контекста данных, у вас будет доступ к этому свойству из метода OnModelCreating():

Однако, если классы Customer и SampleContext (наш класс контекста из примеров) находятся в разных сборках, настраивать закрытое свойство таким образом нельзя. Эту проблему можно решить, если вы определите класс конфигурации (унаследованный от EntityTypeConfiguration ) в той же сборке, где находится класс Customer, добавите ссылку на эту сборку в сборке, где находится класс контекста и в методе OnModelCreating() укажите объекту DbModelBuilder ссылку на эту конфигурацию.

Таким же способом можно определить закрытые и защищенные свойства (модификаторы private и protected). Важно запомнить, чтобы этот подход работал, класс конфигурации должен быть вложен в класс модели, к которому он применяется. Ниже показан пример для класса Customer, в котором мы определяем закрытое свойство LastName и класс конфигурации, вложенный в класс Customer:

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

Теперь сослаться на эту конфигурации можно в классе контекста, используя Fluent API:

Наследование в классах модели

Entity Framework поддерживает различные иерархии наследования в модели. Ранее, при обсуждении возможностей Entity Framework мы не обращали внимание на возможность наследования классов модели, поэтому у вас может возникнуть вопрос о том, как Code-First работает с такими моделями.

Отображение Table Per Hierarchy (TPH)

Отображение “таблица на иерархию” (Table Per Hierarchy – TPH) используется в Code-First по умолчанию и означает, что иерархия унаследованных классов отображается на одну таблицу в базе данных. Чтобы увидеть это отображение в действие, давайте изменим модель и укажем класс User, унаследованный от Customer, добавляющий два новых свойства:

Для такой модели Entity Framework сгенерирует одну таблицу Customers, имеющую следующую структуру:

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

Если вы используете для вставки данных класс User, как показано в примере ниже, то Code-First вставит в столбец Discriminator значение “User”:

Фактически, с помощью этого столбца Entity Framework отслеживает, какой класс в иерархии наследования использовался для изменения/вставки данных в таблицу.

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

В этой настройки для указания имени столбца дискриминатора используется метод Requires() объекта конфигурации EntityMappingConfiguration. Мы явно указали имя CustomerType для столбца дискриминатора. С помощью метода HasValue() можно установить значение, которое будет генерироваться для каждого класса. Теперь, если вы вставите запись в таблицу Customers с помощью класса User, столбец CustomerType будет хранить значение “Добавлено из User”.

Можно также изменить тип столбца дискриминатора. Например, если вам известно, что в иерархии классов модели используются всего два класса (как, в нашем примере), то можно определить столбец IsUser типа bool (в SQL тип BIT), который будет указывать, добавлена ли строка с помощью Customer, либо с помощью User:

Структура таблицы Customers с использованием нового столбца IsUser показана на рисунке ниже:

Отображение Table Per Type (TPT)

В то время, как отображение TPH хранит одну таблицу для иерархии наследованных типов, отображение “таблица на тип” (Table Per Type – TPT) позволяет создать таблицу для каждого типа, при этом в таблицах, отображающих наследуемые классы хранится внешний ключ, ссылающийся на их родительский класс (таблицу). Если ваша схема базы данных должна использовать отдельные таблицы для иерархии классов, вы должны явно сконфигурировать производные. Для этого вам нужно просто указать имя таблицы для производного типа. Вы можете сделать это с помощью аннотаций данных или Fluent API:

В результате запуска этого примера, Code-First создаст две таблицы: Customers и User. На рисунке ниже показана их структура:

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

Когда вы добавляете новую запись с помощью класса User, Code-First сначала создаст новую запись в таблице Customers, а затем вставит новую запись в таблицу User, ссылающуюся на созданную запись в таблице Customers.

Отображение Table Per Concrete Type (TPC)

Отображение “таблица на конкретный тип” (Table Per Concrete Type – TPC) похоже на отображение TPT тем, что при таком отображении для каждого класса в иерархии наследования создается таблица. Но при этом таблицы, созданные на основе этой иерархии никак не связаны, и в каждой таблице отображается весь набор свойств из родительских классов. Такое отображение бывает полезно когда вы хотите расширить старую таблицу при этом не изменяя ее и не удаляя, а просто создав новую таблицу со старыми и новыми столбцами.

Читать еще:  Лицензионные программы бесплатно и легально с ключом активации.

Вы можете настроить отображение TPC с использованием Fluent API (аннотации данных для этого отображения не поддерживаются). Давайте изменим отображение для наших классов Customer и User. Для настройки TPC используется вспомогательный метод MapInheritedProperties(), который доступен только на объекте EntityMappingConfiguration, т.е. в вызове метода Map(). Ниже показан соответствующий пример:

Обратите внимание, что вы должны явно включить отображение имени таблицы с помощью метода ToTable() для базовой таблицы. С отображением TPT это не требуется, но с TPC это является обязательным. Метод MapInheritedProperties() указывает Code-First, что он должен отобразить все свойства, унаследованные от базового класса к новым колонкам в таблице, созданной на основе производного типа. Структура таблиц Customer и User выглядит следующим образом:

Абстрактные классы модели

Все создаваемые ранее классы модели не были абстрактными, это означает, что в коде мы могли создавать объекты этих классов. В C# поддерживается возможность создания абстрактных классов, экземпляры которых нельзя создавать в коде. Возникает вопрос, как работает Entity Framework с такими классами модели?

Давайте изменим нашу модель классов Customer-User, и укажем, что класс Customer должен являться абстрактным, а также добавим новый класс Client, который наследуется от Customer:

Теперь вы не можете создавать экземпляры класса Customer, поэтому удалите создание этих объектов, если вы использовали их ранее где-то в приложении. Также удалите настройки TPC в методе OnModelCreating(), в результате чего Code-First будет использовать по умолчанию подход для отображения TPH – все поля из производных классов будут содержаться в таблице Customers.

Если вы запустите этот пример, то увидите, что структура таблицы Customers идентична той, что у нас получалась при рассмотрении TPH, за тем лишь исключением, что были добавлены поля из класса Client.

Фактически поведение Code-First при использовании абстрактных классов идентично поведению для обычных классов, но при этом меняется способ взаимодействия с таблицей Customers. Для вставки данных в эту таблицу вы можете использовать объекты унаследованных классов Client и User. Если вы реализуете отображение TPC, очевидно, что при использовании абстрактного класса вы потеряете доступ к вставке/изменению данных в таблице Customers.

Практическая разница между подходами к наследованию в Entity Framework при разработке

Здравствуйте ,изучая материалы по Entity Framework прочитал о возможностях и разных подходах при реализации наследования в данном фреймворке . И меня конечно же заинтересовал такой вопрос :

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

Какие же пункты я буду освещать при формировании ответа :

Теория – здесь я буду рассказывать какие собственно есть подходы и как они отличаются в плане реализации

Практика – здесь я буду показывать саму реализацию кода модели классов + покажу как будет выглядеть таблица/таблицы баз данных при разных подходах

Характеристическое сравнение – в заключительном участке мы с вами выясним какой же подход ,в какой категории и почему именно этому подходу дано предпочтение .

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

Ответы (1 шт):

Итак ,начнем пожалуй .

Теоретическая часть :

Всего в Entity Framework есть всего 3 подхода :

  1. TPH (Table Per Hierarchy – Таблица на одну иерархию классов)
  2. TPT (Table Per Type – Таблица на тип)
  3. TPC (Table Per Class(Concrete Type) – Таблица на каждый отдельный тип/класс)

1) TPH (Table Per Hierarchy – Таблица на одну иерархию классов) – При использовании данного подхода – для одной иерархии классов используется одна таблица.

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

2) TPT (Table Per Type – Таблица на тип) – данный подход предполагает сохранение в общей таблице только тех свойств, которые общие для всех классом-наследников, то есть которые определены в базовом классе.

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

3) TPC (Table Per Class(Concrete Type) – Таблица на каждый отдельный тип/класс) – предполагает создание для каждой модели по отдельной таблицы. Столбцы в каждой таблице создаются по всем свойствам, в том числе и унаследованным.

Здесь мы с теоретической частью пожалуй закончим – перейдем до практики:

Практическая часть:

Я считаю ,что надо выяснить одну деталь сразу :

Суть и предназначение у этих подходов одна – отобразить зависимость класса-наследника от класса-родителя и разность заключается в том ,что я буду показывать какой именно подход будет оптимальней с разных характеристических сторон(ну и само собою – отличия при их практической реализации). Спасибо за внимание!

1) Подход TPH :

Итак у нас есть базовая мини-иерархия двух классов :

Здесь класс Smartphone наследуется от Phone , определяя одно свойство в дополнение к унаследованным.

И при работе будет создана такая таблица:

Кроме всех свойств классов Phone и Smartphone здесь также появляется еще один столбец – Discriminator . Он имеет тип nvarchar и имеет длину в 128 символов. Данный столбец и будет определять относится строка к типу Phone или Smartphone .

Работа в программе :

Прошу вас обратить внимание на одну деталь : при выводе данных из Phones также будет идти вывод экземпляра Smartphone вместе с остальными экземплярами ,поскольку здесь SmartPhone и есть объектом Phone .

2) Подход TPT :

Чтобы применить подход, возьмем из предыдущего примера систему классов и добавим к классу Smartphone атрибут [Table] :

Все остальное остается также, как и при подходе TPH. Но теперь база данных будет содержать следующие таблицы:

Таблица для смартфонов содержит только одно поле OS , а также ключ Id для связи с таблицей Phones .

Применение моделей будет аналогично подходу TPH(пункт выше):

3) Подход TPC :

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

Во-первых, обратите внимание, что у класса Phone в качестве типа ключа используется не int , а Guid . Это поможет нам избежать некоторых проблем с ключами. Хотя также можно было бы использовать int с ручной установкой Id при создании объекта.

Во-вторых, при настройке сопоставления моделей и таблиц у каждой модели вызывается метод MapInheritedProperties() , который указывает Entity Framework -у, что в таблицу для данной модели надо включить также наследуемые свойства, а не только те, которые определены непосредственно в этой модели.

При генерации базы данных у нас будут созданы две таблицы с полным набором столбцов:

Несмотря на то, что объект Smartphone никак не связан с таблицей Phones , при извлечении данных он также будет находится в наборе db.Phones , потому что наследование все равно будет действовать.

Переходим к третьей части – Характеристическое сравнение :

Вот тут уже действительно интересно: В зависимости от ваших требований и требований заказчика – здесь попросту нет ‘лучшего’ решения . Но:

Движок Entity Framework хоть и поддерживает подход TPC , но для его адекватной работы нам нужно метод OnModelCreating() переопределять , дабы фреймворк понял, что мы подвязываем два класса между собою наследственными связями – чтобы можно было нормально работать с результатом запросов используя этот подход в приложении .

Значит ли это то ,что в большинстве случаев нам будет придеться использовать 2-а первых подхода – TPH и TPT ,чтобы не тратить время на изощрения с TPC и относительную скорость работы приложения при получении запросов?

Давайте выясним!

Итак ,здесь мы использовать формат “критерий – условный победитель – почему” :

  1. Скорость исполнения(работы)TPH ->Таблица на одну иерархию классов в общем имеет лучшую скорость хотя бы потому ,что на не надо делать запросы JOIN поскольку все данные в одной таблице . Такое решение становиться даже более очевидным ,когда у нас наследственная иерархия делается “шире” и “глубже”.
  2. ГибкостьTPT ->Таблица на тип более гибкая потому,что решает проблему редактирования и обновлений колонок дочерней-таблицы при этом не изменяя родительскую таблицу.
  3. ЭстетичностьTPT -> это уже чисто субъективное мнение ,но как по-мне ,то TPT выглядит для мене более объектно-ориентированным подходом.
  4. Использование памятиTPT -> если у вас иерархия наследования имеет очень много различных типов , то использование TPT позволит использовать данные ,которые имеют много незаполненных полей , тем более ,если структура баз данных решает проблему множества пустых полей ,то эта проблема вряд ли скажется на производительности работы запросов.

Как мы с вами можем заметить ,если вы или ваш заказчик знает на какой критерий делать упор – то вы можете сделать очевидный выбор исходя из информации выше

Единственный нюанс состоит в том ,что в 90% случаев ставка идет на Производительность,поэтому в большинстве случаев использование TPH(Таблица на одну иерархию) для оптимизации работы запросов – будет оптимальней

Надеюсь ,я этой статьей смог внести немного ясности в вопрос – если у вас есть пожелания ,то комментарии всегда открыты для вас – Спасибо за внимание,комрады!

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

Введение в ООП с примерами на C#. Часть вторая. Все, что нужно знать о наследовании

    Переводы , 15 июля 2015 в 1:28

Вступление

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

Давайте сразу тезисно опишем, что такое наследование:

  • Это механизм создания нового класса на основе уже существующего старого.
  • Старый класс называется «родительским», «предком» («super class»).
  • Новый класс называется «дочерним», «наследником» («sub class»).
  • Наследование нужно для повторного использования кода, которое облегчает следование принципу DRY (Don’t Repeat Yourself — Не повторяйся).
  • Дочерний класс содержит методы и переменные родительского.

Рассмотрим наследование в действии

Создайте консольное приложение и назовите его InheritanceAndPolymorphism . Добавьте два класса, с названиями ClassA и ClassB , как показано ниже:

Как вы можете видеть, класс A пуст, а в B мы добавили два метода и переменную x со значением 100.

Теперь в главном методе Program.cs напишите следующее:

Разумеется, этот код вызовет ошибку:

Error: ‘InheritanceAndPolymorphism.ClassA’ does not contain a definition for ‘Display1’ and no extension method ‘Display1’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassA’ could be found (are you missing a using directive or an assembly reference?)

Очевидно, причина в том, что в классе А нет метода, который мы вызываем. Однако он есть у класса B. Было бы здорово, если бы мы могли получить доступ ко всему коду в B из A!

Теперь измените описание первого класса на следующее:

Теперь после выполнения программы мы получим:

Т.е. теперь ClassA наследует публичные методы из ClassB , это то же самое, если бы мы скопировали весь код из B в A. Всё, что объект класса B может делать, может и объект класса A. ClassA — дочерний класс, а ClassB — родительский.

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

Ивент перенесён на 26 – 27 сентября , Новосибирск, беcплатно

Теперь давайте представим, что ClassA тоже имеет метод Display1 :

Что будет, если мы запустим код теперь? Каким будет вывод? И будет ли вывод вообще или выйдет ошибка компиляции? Давайте проверим.

Однако мы также получим предупреждение:

Warning: ‘InheritanceAndPolymorphism.ClassA.Display1()’ hides inherited member ‘InheritanceAndPolymorphism.ClassB.Display1()’. Use the new keyword if hiding was intended.

Что нужно запомнить: ничто не может помешать создать в дочернем классе такой же метод, как и в родительском.

Когда мы вызываем a.Display1() , C# сначала ищет Display1() в ClassA , а только потом в ClassB . Поскольку в A такой метод есть, вызывается именно он.

Что нужно запомнить: методы дочерних классов имеют приоритет при выполнении.

Такая возможность нам даётся для того, чтобы мы могли изменить поведение методов предка, если оно нас не устраивает. Однако мы всё равно можем вызывать методы родительского класса следующим образом:

В таком случае вывод будет:

ClassA Display1
ClassB Display1

Что нужно запомнить: ключевое слово base может быть использовано для обращения к методам класса-предка.

Что же, вверх по иерархии мы обращаться можем. Давайте попробуем сделать наоборот:

Error: ‘InheritanceAndPolymorphism.ClassB’ does not contain a definition for ‘Display2’ and no extension method ‘Display2’ accepting a first argument of type ‘InheritanceAndPolymorphism.ClassB’ could be found (are you missing a using directive or an assembly reference?)

Что нужно запомнить: наследование не работает в обратном направлении.

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

Что нужно запомнить: кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё.

Если класс С, будет унаследован от класса B, который, в свою очередь, будет унаследован от класса A, то класс C унаследует члены как от класса B, так и от класса A. Это транзитивное свойство наследования. Потомок перенимает все члены родителей и не может исключить какие-либо. Он может «спрятать» их, создав свой метод с тем же именем. Конечно, это никак не повлияет на родительский класс, просто в дочернем метод не будет виден.

Члены класса могут быть двух типов — статический, который принадлежит именно классу, или обычный, который доступен только из реализаций класса (его объектов). Чтобы сделать член статическим мы должны использовать ключевое слово static .

Если мы не наследуем класс ни от какого другого, подразумевается, что мы наследуем его от класса object . Это — родитель всех классов, и он единственный не унаследован ни от чего. Таким образом, такой код:

Автоматически воспринимается C# так:

Таким образом, по свойству транзитивности, ClassA также является наследником object .

Теперь ещё один момент. Если мы захотим сделать так:

То у нас это не получится:

‘InheritanceAndPolymorphism.ClassW’ cannot derive from special class ‘System.ValueType’
‘InheritanceAndPolymorphism.ClassX’ cannot derive from special class ‘System.Enum’
‘InheritanceAndPolymorphism.ClassY’ cannot derive from special class ‘System.Delegate’
‘InheritanceAndPolymorphism.ClassZ’ cannot derive from special class ‘System.Array’

Заметили словосочетание «special class»? Такие классы нельзя расширять.

Что нужно запомнить: ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.

Выше мы описали три класса: ClassW , ClassX и ClassY , который наследуется от первых двух. Теперь попробуем это скомпилировать:

Compile time Error: Class ‘InheritanceAndPolymorphism.ClassY’ cannot have multiple base classes: ‘InheritanceAndPolymorphism.ClassW’ and ‘ClassX’.

Что ещё нужно запомнить: класс может иметь только одного родителя, множественное наследование в C# не поддерживается (оно поддерживается у интерфейсов, но в этой статье мы о них речи не ведём).

Если мы попробуем обойти это правило таким образом:

То это не пройдёт:

Error: Circular base class dependency involving ‘InheritanceAndPolymorphism.ClassX’ and ‘InheritanceAndPolymorphism.ClassW’.

Что нужно запомнить: классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), что, в общем-то, логично.

Операции с объектами

Здесь мы пытаемся приравнять объект от разных классов друг к другу.

Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’

Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassA’ to ‘InheritanceAndPolymorphism.ClassB’

Однако у нас это плохо получается. Даже несмотря на то, что они имеют одинаковые поля с одинаковыми значениями. Даже если бы эти поля имели одинаковые названия. C# работает с типами очень чётко — вы не можете приравнять два объекта от двух независимых классов. Однако, если бы класс A наследовался от B:

…мы бы продвинулсь немногим дальше:

Error: Cannot implicitly convert type ‘InheritanceAndPolymorphism.ClassB’ to ‘InheritanceAndPolymorphism.ClassA’. An explicit conversion exists (are you missing a cast?)

Как я уже говорил, C# подходит к вопросам типов очень дотошно. Класс A унаследован от B, значит, имеет все его поля и методы — при назначении переменной типа B объекта типа A проблем не возникает. Однако вы уже знаете, что в обратную сторону это не работает — в классе B нет полей и методов, которые могут быть в A.

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

Здесь нам наконец-то представляется шанс обмануть правило:

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

Итак, наш последний блок кода:

Error: Cannot implicitly convert type ‘int’ to ‘char’. An explicit conversion exists (are you missing a cast?)

Что нужно запомнить: можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).

Заключение

В этой части мы рассмотрели наследование. Мы попробовали запускать разные варианты кода, чтобы возможно глубже понять суть этого принципа. *этот текст будет изменён после перевода следующей статьи* In my next article, we’ll be discussing about run time polymorphism. Inheritance plays a very important role in run time polymorphism.

Вот что вы должны были запомнить за сегодня:

  • как сын получается похожим на отца, наследует его черты, так и дочерний класс имеет параметры родительского;
  • ничто не может помешать создать в дочернем классе такой же метод, как и в родительском;
  • методы дочерних классов имеют приоритет при выполнении;
  • ключевое слово base может быть использовано для обращения к методам класса-предка;
  • наследование не работает в обратном направлении;
  • кроме конструкторов и деструкторов, дочерний класс получает от родителя абсолютно всё;
  • ваши классы не могут быть унаследованы от встроенных классов вроде System.ValueType , System.Enum , System.Delegate , System.Array и т.д.;
  • класс может иметь только одного родителя, множественное наследование классов в C# не поддерживается;
  • классы не могут наследоваться циклически (1-й от 2-го, 2-й от 3-го 3-й от 1-го), это невозможно чисто логически;
  • вы можете назначить переменной родительского типа объект дочернего, но не наоборот;
  • можно конвертировать char в int . Нельзя конвертировать int в char (причина в том, что диапазон целого числа больше, чем символа).

Напоминаем вам, что в первой статье этой серии вы можете прочитать о полиморфизме. Продолжайте учиться программировать с нами!

Связываем ASP.NET Core MVC с MS SQL Server (Entity Framework Core)

В этой статье поговорим о том, как связать ASP.NET Core MVC приложение с базой данных на MS SQL Server, используя технологию Entity Framework Core.

  • Данный материал актуален для версии .NET Core 2.2

    Предположим, что имеется действующее ASP.NET Core MVC приложение, готовое к запуску. Это может быть или ваш собственный проект, или пустое приложение, созданное из шаблона. Также имеется готовый настроенный MS SQL Server. Перед нами стоит задача – настроить связь между приложением и сервером, то есть чтобы все данные приложения хранились в некоторой базе данных.

    Во-первых, определим доменную модель, то есть что мы будем хранить в базе данных. Например, на нашем сайте пользователи смогут публиковать и редактировать статьи. Создадим подобный класс:

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

    В описанном выше классе важно следующее:

    • наш класс наследуется от класса IdentityDbContext , тем самым мы добавляем в наше приложение функциональность технологии ASP.NET Identity (система аутентификации и авторизации пользователей).
    • свойство типа DbSet позволяет соотнести объекты в приложении с соответствующими записями в таблице базы данных. Все LINQ-запросы к этому свойству будут транслироваться в SQL-запросы.
    • в переопределенном методе OnModelCreating() мы добавляем одну новую статью. Таким образом, при создании базы данных в таблицу Articles сразу будет добавлена первая запись. Данный подход иногда очень удобен, когда требуется заполнить базу данных тестовыми данными.

    Далее переключаемся в класс Startup.cs, в метод ConfigureServices(). В этом месте непосредственно указывается вся конфигурация для нашего приложения, например, переадресация, кэш, сессия, маршрутизация и т.д. Нас в данном примере интересует настройки для контекста базы данных.

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

    • источник данных – локальный SQL-сервер.
    • название базы данных – Articles.
    • Persist Security Info=False – запрещаем получение важных данных из строки подключения после открытия соединения.
    • MultipleActiveResultSets=True – также разрешаем возможность выполнения нескольких пакетов по одному соединению (MARS).
    • Trusted_Connection=True – даем возможность при соединении использовать режим Windows-аутентификации.

    * в вашем проекте строка подключения конечно же может отличаться.

    К данному моменту мы определили доменную модель, создали пользовательский контекст базы данных, и также настроили его для работы. Все готово для создания первой миграции. Для работы с миграциями и вообще Entity Framework можно использовать либо командное окно в Visual Studio Package Manager Console, либо стандартный PowerShell. В данном примере воспользуемся первым вариантом.

    Добавим новую первую миграцию с помощью команды:
    add-migration _initial

    Далее применим созданную миграцию и обновим базу данных. В нашем примере база данных еще не существует, и она будет создана. Применяем команду в Package Manager Console:
    update-database

    На данном этапе связь между веб-приложением и сервером баз данных установлена. Чтобы было удобнее работать со статьями и совершать над ними стандартные CRUD-операции (create, read, update, delete), создадим класс-репозиторий.

    Благодаря классу-репозиторию мы скрываем детали работы контекста базы данных. Теперь все манипуляции со статьями будут проходить только через репозиторий. Также зарегистрируем репозиторий как сервис в классе Startup.cs в методе ConfigureServices(), чтобы была возможность использовать его в других классах.

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

    Последнее что остается – это создать соответствующие представления. Далее представлена простейшая HTML-разметка.

    Проектирование баз данных и работа с ними Веб-приложений. LINQ, ADO.NET Entities, DDD

    10.1.3.1.2. Сопоставление объектов и данных

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

    В существующих решениях была предпринята попытка устранить этот разрыв, часто называемый “несоответствием типов данных” ( impedance mismatch ), путем сопоставления с реляционными таблицами и столбцами только объектно-ориентированных классов и свойств. Вместо данного традиционного подхода в Entity Framework реляционные таблицы, столбцы и ограничения внешнего ключа логических моделей преобразуются в сущности и связи концептуальных моделей. Это позволяет достичь большей гибкости при определении объектов и оптимизации логической модели. С помощью инструментов Entity Data Model формируются расширяемые классы данных, основанные на концептуальной модели. Эти классы являются разделяемыми классами, которые могут быть расширены с помощью дополнительных членов, добавленных разработчиком. Классы, сформированные для определенной концептуальной модели, являются производными от базовых классов, предоставляющих службы объектов для материализации сущностей в виде объектов, а также для отслеживания и сохранения изменений. Разработчики могут использовать эти три класса для работы с сущностями и связями как с объектами, связанными свойствами навигации.

    10.1.3.1.3. Компоненты платформы Entity Framework

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

    • Модель Entity Data Model ( EDM ) служит центром приложения Entity Framework. Она задает схему проекта, которая используется для построения программируемых классов, используемых кодом приложения.
    • Компонент Object Services позволяет программистам работать с классами CLR, созданными из концептуальной модели. Он также обеспечивает инфраструктурную поддержку для приложения Entity Framework, предоставляя такие службы, как управление состоянием, отслеживание изменений, разрешение идентификаторов, загрузка и переход по связям, распространение изменений объектов в модификации базы данных, а также поддержку запросов для Entity SQL.
    • Компонент LINQ to Entities обеспечивает поддержку LINQ при запросах к сущностям. Компонент LINQ to Entities позволяет разработчикам писать запросы к базе данных на одном из поддерживаемых языков программирования .NET Framework, например Visual Basic или Visual C#.
    • Язык Entity SQL подобен языку SQL и не зависит от типа хранилища. Он предназначен для создания запросов к сложным графам объектов, основанных на модели EDM , а также для управления ими.
    • Поставщик EntityClient расширяет модель поставщика ADO.NET путем доступа к данным в терминах сущностей и связей концептуальной модели. Он выполняет запросы, которые используют язык Entity SQL. Entity SQL предоставляет базовый язык запросов, который позволяет поставщику EntityClient связываться с базой данных.
    • Компонент метаданных ADO.NET управляет метаданными для всей платформы Entity Framework как во время разработки, так и во время выполнения. Все метаданные, связанные с моделями и сопоставлениями, доступны через интерфейсы метаданных, которые не зависят от механизма, используемого для хранения метаданных. Текущий механизм хранения использует файлы, которые основаны на трех диалектах XML: языке CSDL, языке SSDL и языке MSL.
    • Приложение Entity Framework включает изменяющийся набор средств, которые создают сопоставления и разделяемые классы, представляющие сущности концептуальной модели.
    • Приложение Entity Framework включает обновленный поставщик данных SqlClient, который поддерживает канонические деревья команд.

    На рис. 10.13 показаны связи различных доступных пользователям интерфейсов программирования в приложении Entity Framework [10, 11]. Стрелка вниз обозначает запрос к источнику данных, а стрелка вверх – возвращаемые данные. Службы объектов создают каноническое дерево команд, которое представляет работу LINQ to Entities или Entity SQL с концептуальной моделью. Поставщик EntityClient преобразует это каноническое дерево команд, основанное на модели EDM , в новое каноническое дерево команд, содержащее эквивалентные операции для источника данных.

    10.1.3.1.4. Средства работы с моделью EDM

    Наряду со средой выполнения Entity Framework, .NET Framework 3.5 с пакетом обновления 1 включает генератор моделей EDM (EdmGen.exe). Программа командной строки соединяется с источником данных и формирует модель EDM на основе сопоставления типа “один к одному” между сущностями и таблицами. В этой программе используется также файл концептуальной модели (с расширением CSDL) для формирования файла уровня объектов, содержащего классы, которые представляют типы сущностей и контекст ObjectContext.

    Visual Studio 2008 включает в себя обширный набор поддерживаемых инструментов для создания и обслуживания модели EDM в приложении Visual Studio. Конструктор Entity Data Model поддерживает создание усовершенствованных сценариев сопоставления (таких как наследование типа “одна таблица на тип” и “одна таблица на иерархию”), а также разделение сущностей, которые сопоставлены с несколькими таблицами.

    10.1.3.2. Создание сущностной модели данных

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

    C помощью мастера EDM , который предоставляет список объектов, которые возможно смоделировать (рис. 10.14 ), включим все таблицы в модель, тем самым произведя взаимно-однозначное сопоставление таблиц сущностям [12].

    Источник: Обзор ADO.NET Entity Framework [12]

    Помимо создания файлов CSDL, SSDL и MSL для всех таблиц в Northwind , мастер также создаст набор классов, основываясь на CSDL, представляющий модель. Часть этих классов показана на рис. 10.15 в окне Class View.

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

    Источник: Обзор ADO.NET Entity Framework [12]

    10.1.3.3. Разбор CSDL

    Метаданные, содержащиеся в CSDL-файле, содержат списки сущностей, представленных элементами EntityType, и связей, представленных элементами Association , относящихся к типу AssociationType . Далее показан фрагмент CSDL-файла, определяющий EntityType :

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

    В следующем фрагменте CSDL определятся AssociationType между Customer и относящимися к нему Orders :

    Элементы End в AssociationType указывают участвующих в ассоциации. В этом примере сущность Customers ассоциирована с сущностью Orders . Также сущность Customers может быть связана с любым числом сущностей Orders , что определяется атрибутом Multiplicity .

    В то время как элементы EntityType и AssociationType определяют типы сущностей области и отношения между ними, элементы EntitySet и AssociationSet определяют их области применения. Все “наборы”, которые должны быть сгруппированы вместе, содержатся внутри элемента EntityContainer .

    Следующий фрагмент CSDL демонстрирует EntityContainer и часть его содержимого:

    Этот фрагмент включает наборы EntitySet для типов EntityType Customers и Orders : Здесь же объявляется ассоциация AssociationSet FK_Orders_Customers . Таким образом, этот фрагмент определяет сущности Customers и Orders , а также связь между ними.

    Entity Framework 4 & Частичное Наследование Классов

    У меня есть простая модель EF с тестовой сущностью, и я хочу использовать разделяемые классы для добавления проверки, подобной этой:

    Но я получаю эту ошибку:

    Частичные объявления ‘Model.Test’ не должны указывать различные базовые классы.

    Я понимаю ошибку, но как я могу использовать модели EF4 и по-прежнему иметь доступ ко всем моим бизнес-проверкам?

    BusinessObject имеет все проверки и прочее, так что если я могу заставить их работать счастливо вместе, я все сделал. Надеюсь, кто-то может помочь.

    1 Ответ

    При текущей настройке (с использованием по умолчанию EF EntityObjects) невозможно получить объекты, наследуемые от BusinessObject . Однако вместо наследования можно использовать композицию, что означает, что каждый разделяемый класс будет иметь поле типа BusinessObject.

    Если вы хотите придерживаться вашего наследства, то вы должны использовать EF 4 поко ми лицами, а затем POCOs наследовать от BusinessObject .

    Похожие вопросы:

    Проблема В прошлом наша команда разработчиков решила создать новое корпоративное приложение и решила использовать Linq to SQL начиная с Entity Framework sucked. (Это не мое мнение!) Они использовали.

    Я решил преобразовать свой DAL в Entity Framework 4.0. У меня уже есть моя огромная база данных, а также много бизнес-классов. Как я могу сопоставить таблицы и поля с моими классами?

    Каковы преимущества/недостатки каждого метода? Я знаю, что читал где-то в книге или на этом сайте, почему использование наследования таблиц является дерьмовым для Entity Framework 4. Например.

    Интересно, можно ли сделать наследование таблицы postgres в entity framework 7-вместо добавления столбцов сделайте real postgres с базовой таблицей и таблицей, которые наследуют базовую таблицу.

    Я работаю над проектом с MVC 4 и Entity Framework. Я создал модель сущности с автогенерированными классами из базы данных. Но, я хочу разные имена и методы для классов в моделях. Например, был.

    Скажем, у меня очень простая структура классов, больше для удобства, чем что-либо. class A < [Column(someValue)] public int someValue> [Table(tableB)] class B< [Column(somethingElse)].

    Я пишу приложение WPF, используя caliburn.micro для работы с базой данных. Я использую entity framework с подходом database-first (поскольку у меня уже есть БД) для создания классов. Можно ли.

    Как создать диаграмму классов из диаграммы Edmx в ADO.NET Entity FrameWork 4? Я использую Visual Studio 2010.

    У меня есть существующая база данных. Я надеялся, что есть способ генерировать файлы классов из этой базы данных. Тем не менее, я, кажется, вижу много создания базы данных из файлов класса.

    Я работаю над проектом, и я использую Entity Framework 4 в качестве моего ORM. Я реализую POCO классов. Каждый пример, который я вижу с EF 4 и POCOs реализует все свойства с общедоступными.

    голоса
    Рейтинг статьи
  • Ссылка на основную публикацию
    Статьи c упоминанием слов: