понедельник, января 21, 2008

Entity Framework - ComplexType

Если вы воспользуетесь визардом создания Entity Data Model (EDM) в Visual Studio 2008 и натравите его на свою базу данных, то в результате получите модель наподобие этой:


В полученной модели мы видим сущности и связи между ними. Сущности содержат скалярные (Scalar properties) и навигационные (Navigation properties) свойства. Навигационные свойства соответствуют концам (Ends) связей и предствляют собой ссылки на другие сущности или коллекции сущностей, это зависит от мощности соответствующего конца связи. Скалярные свойства могут иметь простой (Simple type) или комплексный (Complex type) тип. Простые типы в EDM, это строки, массив байт (binary), Boolean, DateTime, Guid, Decimal, а также стандартный набор числовых типов (знаковые и беззнаковые целые различной размерности, и числа с плавающей точкой). Для некоторых типов при помощи атрибутов определяются фасеты, призванные обеспечить правильное соответствие типов при маппировании (например, для строк можно определить фиксированную или переменную длину). Кстати, плохая новость, перечисления (enum) не входят в перечень поддерживаемых типов! Проблема довольно легко решается. Поскольку все генерируемые дата-классы EF представляют собой partial классы, довольно просто создать свойство обертку с типом enum над соответствующим скалярным свойством. Поддержку enum обещают… в следующей версии EF.

А теперь вспомним о том, что я сказал немного выше. Скалярные свойства сущностей могут иметь комплексный тип. Не пытайтесь создать или найти ComplexType на диаграмме EDM. ComplexTypes не поддерживаются инструментами Visual Studio. Рассмотрим вот такую сущность Person:


Четыре ее свойства (Site, StreeAddress, Region, PostalCode) предназначены для представления адреса человека. Вероятно, удобнее было бы работать с объектом типа Address для представления этих данных. Для этих целей как раз и используется ComplexType. Для того, чтобы создать ComplexType нам придется закрыть красивое, но увы, бесполезное в нашем случае окно дизайнера, и открыть заново edmx файл модели командой «Open with…» в Xml редакторе студии. Кстати, должен вам заметить, что создавать и редактировать свойства сущностей гораздо удобнее именно в Xml редакторе, интелисенс по Xsd схеме очень помогает. А вот редактировать ассоциации лучше в дизайнере, поскольку в этом случае необходимо вносить согласованные изменения в различные части edmx схемы.

ComplexType описывается в разделе ConceptualModels, там же где и EntityType. Опишем комплексный тип CAddress следующим образом:


<ComplexType Name="CAddress" >
<Property Name="Sity" Type="String" Nullable="true" MaxLength="50" />
<Property Name="StreetAddress" Type="String" Nullable="true" MaxLength="50" />
<Property Name="Region" Type="String" Nullable="true" MaxLength="50" />
<Property Name="PostalCode" Type="String" Nullable="true" MaxLength="6" FixedLength="true" />
</ComplexType>



А в EntityType Person заменим четыре свойства на одно Address типа CAddress:


<EntityType Name="Person">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="Int32" Nullable="false" />
<Property Name="FirstName" Type="String" Nullable="false" MaxLength="50" />
<Property Name="LastName" Type="String" Nullable="false" MaxLength="50" />
<Property Name="BirthDate" Type="DateTime" Nullable="false" />
<Property Name="Address" Type="Self.CAddress" Nullable="false" />
</EntityType>



Как видно, объявление свойства комплексного типа ничем не отличается от прочих. Просто указан тип свойства «Self.CAddress», здесь Self это алиас заданный для концептуальной модели. Атрибут Nullable="false" в нашем случае обязателен, это одно из ограничений. Теперь надо поправить маппинг для сущности Person:


<EntityTypeMapping TypeName="IsTypeOf(TestModel.Person)">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="BirthDate" ColumnName="BirthDate" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ComplexProperty Name="Address" TypeName="TestModel.CAddress" >
<ScalarProperty Name="Sity" ColumnName="Sity" />
<ScalarProperty Name="PostalCode" ColumnName="PostalCode" />
<ScalarProperty Name="Region" ColumnName="Region" />
<ScalarProperty Name="StreetAddress" ColumnName="StreetAddress" />
</ComplexProperty>
<ScalarProperty Name="Id" ColumnName="Id" />
</MappingFragment>
</EntityTypeMapping>



Здесь вместо ScalarProperty используется элемент ComplexProperty а в атрибуте TypeName указывается уже не «Self.CAddress», а «TestModel.CAddress», где «TestModel» это имя namespace заданное в концептуальной схеме. Не запутайтесь!
Теперь вместо четырех свойств в сущности Person у нас есть одно - Address типа CAddress. Можно снова открыть модель в дизайнере, ничего не сломается (во всяком случае, в beta 3), но тип CAddress вы не увидите и свойство Address лучше не редактировать в дизайнере (это же касается и маппинга).
Можно заметить, что объявление ComplexType и EntityType очень похожи. Почему бы нам не объявить для представления адреса EntityType вместо ComplexType? Между ними есть одно очень важное отличие. ComplexType не может быть самостоятельно замаплен на объкты в БД, он мапится только в составе других сущностей. Т.о. если бы для представления адреса в БД у нас использовалась отдельная таблица, нам следовало бы использовать EntityType и Navigation Property в сущности Person для отражения связи Person – Address.
В EF существует ряд ограничений на использование ComplexType:
  • Свойства комплексных типов не должны иметь значение null, иначе при сохранении изменений получим исключение

  • Нельзя наследовать complexType от другого ComplexType

  • Когда изменяется одно из всойств в ComplexType, при сохранении ComplexType обновляется целиком

  • При реализации класса для ComplexType разработчики настоятельно советуют наследовать его от ComplexObject :)

2 комментария:

Анонимный комментирует...

А с VS 2008 Express EF не стыкуется? Установил EF beta3. В студии ничего не появилось. Открываю пример из февральского MSDN Magazine, открыват модель как XML.

Sergey Rozovik комментирует...

Я работаю с Visual Studio Team System 2008. Для установки EF beta 3 на студию требуется накатить патч KB945282.
C VS Express не пробовал.