Изучаем EDM и сгенерированные дизайнером классы
Откроем нашу модель дизайнером и произведем небольшие изменения:
Концептуальная модель состоит из EntityContainer, множества определений EntityType и определений Association.
EntityContainer хранит опиcания EntitySet'ов и AssociationSet'ов.
Каждый элемент EntityType определяет сущность концептуальной модели. Элемент Key определяет совокупность свойств, являющихся первичным ключом. Элементы Property задают скалярные свойства сущности. А элементы NavigationProperty служат для связи сущностей.
Вооружившись знаниями (пока поверхностыми... но в одной из следующих статей мы это исправим) об элементах концептуальной модели, рассмотрим классы, сгенерированные дизайнером.
Подписи к ассоциации говорят, что "одному Address соответствует множество Person, а одному Person соответствует 0 либо 1 Address".
При создании модели лучше сразу переименовать навигационные свойства таким образом, чтобы они отображали "множественность".
- Выделим сущность Persons и в окне Properties выставим: Name в Person, Entity Set Name в Persons
- В сущности Person в разделе Navigation Properties выделим Addresses и в окне Properties зададим свойству Name значение Address
- Выделим сущность Addresses и в окне Properties выставим: Name в Address, Entity Set Name в Addresses
- Сохраним модель и закроем ее.
Полный разбор концептуальной модели в мои планы на эту статью не входит: сделаю акцент на том, что имеет отношение к сегодняшней теме.
<edmx:ConceptualModels>
<Schema Namespace="FirstModelModel" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
<EntityContainer Name="FistModel">
<EntitySet Name="Addresses" EntityType="FirstModelModel.Address" />
<EntitySet Name="Persons" EntityType="FirstModelModel.Person" />
<AssociationSet Name="FK_Persons_Addresses" Association="FirstModelModel.FK_Persons_Addresses">
<End Role="Addresses" EntitySet="Addresses" />
<End Role="Persons" EntitySet="Persons" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Address">
<Key>
<PropertyRef Name="AddressId" />
</Key>
<Property Name="AddressId" Type="Int32" Nullable="false" />
<Property Name="City" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Street" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="PostalCode" Type="String" Nullable="false" MaxLength="6" Unicode="true" FixedLength="false" />
<Property Name="Apartment" Type="String" MaxLength="6" Unicode="true" FixedLength="false" />
<NavigationProperty Name="Persons" Relationship="FirstModelModel.FK_Persons_Addresses" FromRole="Addresses" ToRole="Persons" />
</EntityType>
<EntityType Name="Person">
<Key>
<PropertyRef Name="PersonId" />
</Key>
<Property Name="PersonId" Type="Int32" Nullable="false" />
<Property Name="Name" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="Surname" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
<NavigationProperty Name="Address" Relationship="FirstModelModel.FK_Persons_Addresses" FromRole="Persons" ToRole="Addresses" />
</EntityType>
<Association Name="FK_Persons_Addresses">
<End Role="Addresses" Type="FirstModelModel.Address" Multiplicity="0..1" />
<End Role="Persons" Type="FirstModelModel.Person" Multiplicity="*" />
</Association>
</Schema>
</edmx:ConceptualModels>
Концептуальная модель состоит из EntityContainer, множества определений EntityType и определений Association.
EntityContainer хранит опиcания EntitySet'ов и AssociationSet'ов.
Каждый элемент EntityType определяет сущность концептуальной модели. Элемент Key определяет совокупность свойств, являющихся первичным ключом. Элементы Property задают скалярные свойства сущности. А элементы NavigationProperty служат для связи сущностей.
Вооружившись знаниями (пока поверхностыми... но в одной из следующих статей мы это исправим) об элементах концептуальной модели, рассмотрим классы, сгенерированные дизайнером.
Классы-сущности
Каждый EntityType преобразуется в класс-сущность (приведен упрощенный вариант кода):Если элементы Property просто конвертируются в C#-свойства (с добавлением partial-методов для валидации), то с NavigationProperty все несколько сложнее. Возможны два варианта:
public partial class Person : EntityObject
{
private int _PersonId;
partial void OnPersonIdChanging(int value);
partial void OnPersonIdChanged();
public int PersonId
{
get
{
return this._PersonId;
}
set
{
this.OnPersonIdChanging(value);
this.ReportPropertyChanging("PersonId");
this._PersonId = StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("PersonId");
this.OnPersonIdChanged();
}
}
//...
//Остальные скалярные свойства
public Address Address
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference("FirstModelModel.FK_Persons_Addresses", "Addresses").Value;
}
set
{
((IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference("FirstModelModel.FK_Persons_Addresses", "Addresses").Value = value;
}
}
public EntityReference AddressReference
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.GetRelatedReference("FirstModelModel.FK_Persons_Addresses", "Addresses");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedReference("FirstModelModel.FK_Persons_Addresses", "Addresses", value);
}
}
}
}
public partial class Address : EntityObject
{
private int _AddressId;
partial void OnAddressIdChanging(int value);
partial void OnAddressIdChanged();
public int AddressId
{
get
{
return this._AddressId;
}
set
{
this.OnAddressIdChanging(value);
this.ReportPropertyChanging("AddressId");
this._AddressId = StructuralObject.SetValidValue(value);
this.ReportPropertyChanged("AddressId");
this.OnAddressIdChanged();
}
}
//...
//Остальные скалярные свойства
public EntityCollection Persons
{
get
{
return ((IEntityWithRelationships)(this)).RelationshipManager.GetRelatedCollection("FirstModelModel.FK_Persons_Addresses", "Persons");
}
set
{
if ((value != null))
{
((IEntityWithRelationships)(this)).RelationshipManager.InitializeRelatedCollection("FirstModelModel.FK_Persons_Addresses", "Persons", value);
}
}
}
}
- Если навигационному свойству соответствует 0..1 объект (как, например, в случае Person.Address), для него дизайнер сгенерирует два свойства: первое - с типом данных связанной сущности (Address) и с именем соответствующего NavigationProperty (Address), а второе - с типом EntityReference и с добавлением к имени постфикса "Reference" (AddressReference). Первое свойство - это непосредственно связанный с сущностью объект (адрес данной личности), второе - вспомогательное свойство, содержащее функционал, который пригодится при более изощренном использовании EF.
- Если навигационному свойству соответствует множество объектов (как, например, в случае Address.Persons), для него будет сгенерировано одно свойство типа EntityCollection с именем соответствующего NavigationProperty (Persons). В этом случае вспомогательного свойства не требуется: весь необходимый функционал встроен в EntityCollection.
Подписи к ассоциации говорят, что "одному Address соответствует множество Person, а одному Person соответствует 0 либо 1 Address".
При создании модели лучше сразу переименовать навигационные свойства таким образом, чтобы они отображали "множественность".
Наследник ObjectContext
Заканчивая разговор о классах, сгенерированных дизайнером, рассмотрим класс FirstModel, который является наследником ObjectContext.
Как было отмечено в прошлой статье, ObjectContext является центральным классом Object Services. Однако на практике используется не сам ObjectContext, а его наследник, генерируемый дизайнером, т. к. в нем есть ряд свойств и методов, упрощающих работу:
Как было отмечено в прошлой статье, ObjectContext является центральным классом Object Services. Однако на практике используется не сам ObjectContext, а его наследник, генерируемый дизайнером, т. к. в нем есть ряд свойств и методов, упрощающих работу:
Как мы выяснили в начале статьи, в EntityContainer хранятся определения EntitySet (в том числе). Так вот для каждого EntitySet в FirstModel генерируется:
public partial class FistModel : ObjectContext
{
private ObjectQuery _Addresses;
public ObjectQuery Addresses
{
get
{
if ((this._Addresses == null))
{
this._Addresses = base.CreateQuery("[Addresses]");
}
return this._Addresses;
}
}
private ObjectQuery _Persons;
public ObjectQuery Persons
{
get
{
if ((this._Persons == null))
{
this._Persons = base.CreateQuery("[Persons]");
}
return this._Persons;
}
}
public void AddToAddresses(Address address)
{
base.AddObject("Addresses", address);
}
public void AddToPersons(Person person)
{
base.AddObject("Persons", person);
}
}
- Свойство типа ObjectQuery
для обращения к этому набору (Addresses, Persons) - Метод для добавления элементов в этот набор (AddToAddresses, AddToPersons)
Заключение
Неожиданно введение затянулось. Ну что ж... к самим запросам перейдем в следующей статье, которая выйдет совсем скоро.
9 комментариев:
Ждем продолжение!
Четвертая статья уже давно написана. А если Вы о пятой, то она появится в течение двух недель.
Я после использование всем ORM систем понял, что они значительно разгуржают сервер и он практически не трудиться. Хотесь бы видеть какое-то универсальное стредство от MS, которое позволяло бы использовать преимущетсво РБД, но получать данные в объектном виде. Я вот все равно не пойму, у меня есть скажем уже рабочае база данных - так часто в реальной жизни случается, и многое не разрабатываеться с нуля, и я бы могу результаты запроса привезти в объектной модели и вот здесь начинается ручная свистопляска... и никак, вызывает DataReader приобразовываешь результаты в объекты и потом с ними работаешь, не совсем удобно...
>>Я после использование всем ORM систем понял
Всех-всех-всех? :)
>>они значительно разгуржают сервер и он практически не трудиться
Сервер СУБД трудится ровно столько же. И вообще не вижу связи между ORM и нагруженностью сервера.
>>и я бы могу результаты запроса привезти в объектной модели и вот здесь начинается ручная свистопляска... и никак, вызывает DataReader
Зачем DataReader? Сущности замапили - и вперед. DataReader, если и нужен, то для очень специфичных вещей.
>>Зачем DataReader? Сущности замапили - и вперед. DataReader, если и нужен, то для очень специфичных вещей.
Ну почему - давай-те представим ситуаций, к которой структура БД уже пределеная и использование динамических запросов запрещенно - типичная систуация для финансовых приложение.
Т.е. у нас есть хранимая процедура, скажем для вашего примера, которая возвращает Имя пользователя и его адресс один result-set. Как в данном случае можно воспользоваться EF или объектами ORM? Создать отдельный элемент для каждого result-set вроде нет смысла.
Самый оптимальный вариант, это делать мапинг - при помощи того же самого DataReader - на типичный объекты ORM? В таком случае, где выгодность ORM.
Второй вопрос, как перенести сложную логику выборки данных в SQL-сервер? Это тоже не всегда возможно.
Для задач типа Persons --> Address это легко :))) А возьмите корпоративный БД, в которой 700 таблиц и милионы запросов в секунду. С динамически генеруемым SQL он просто загнется.
Еще вариант, как указать на определенной использование индекса - HINT. Никак..
и т.д.
Ааа, вот вы о чем.
Ну да, любой ORM теряет от жесткой привязки к хранимым процедурам. Хотя операции вставки, удаления и обновления мы можем замапить для сущности напрямую. А считывать при помощи явного вызова хранимой процедуры через EF (в этом случае сущности тоже мапятся).
А высокая производительность хранимых процедур по сравнению с динамическими запросами - это миф. В современных РСУБД план выпонения динамических запросов кэшируется практически так же хорошо, как и хранимок.
Хорошо написано, спасибо.
Для пущего счастья стоит добавить ссылки на все другие части во все эти части (надеюць, что понятно выразился :).
Очень хочется 5-ю статью, которая должна была выйти в течение 2-х недель в 2009 году :) Она есть?
Я ждал этого комментария больше года :) Да, цикл заглох, несмотря на мое обещание написать продолжение.
Хотя заглох он на 7-й статье, а не на пятой. И почему вы пишете комментарий к 3-й статье? :)
Кроме того, я еще немного писал о EF уже вне рамок цикла. Все статьи, касающиеся EF, можно просмотреть по соответствующему тегу: http://alexidsa.blogspot.com/search/label/Entity%20Framework
Отправить комментарий