Изучаем 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)
Заключение
Неожиданно введение затянулось. Ну что ж... к самим запросам перейдем в следующей статье, которая выйдет совсем скоро.


