Структурна анотация - хубава. За първи път чувам за тази функция, но работи. Току що го пробвах. Ще се опитам да го обясня малко.
Структурните анотации са просто случаен xml, добавен към EDMX файл. EDMX файлът всъщност е просто XML, който има 4 части - CSDL, MSL, SSDL и част, свързана с елементите за позициониране в дизайнера.
- CSDL описва обекти и асоциации между обекти (дефинирани в дизайнера)
- SSDL описва таблици и релации
- MSL описва съпоставяне между CSDL и SSDL
Ако започнете първо с модел (искате да генерирате база данни от вашия модел), имате само CSDL част и както SSDL, така и MSL ще бъдат генерирани от някакъв автоматичен процес (T4 шаблони, изпълнявани в работен поток), след като SSDL бъде създаден, друг T4 шаблон ще генерира SQL скрипт за създаване на база данни.
Структурната анотация, описана в нишката на свързания форум на MSDN, е намек. Ще поставите структурна анотация в CSDL частта на EDMX (трябва да отворите EDMX като XML - щракнете върху файла в Solution Explorer и изберете Отваряне с). Моят тестов CSDL описва един потребителски обект с три свойства (обектът се вижда на екранната снимка по-късно в отговора):
<!-- CSDL content -->
<edmx:ConceptualModels>
<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
xmlns:custom="http://tempuri.org/custom"
Namespace="Model" Alias="Self" >
<EntityContainer Name="ModelContainer" annotation:LazyLoadingEnabled="true">
<EntitySet Name="UsersSet" EntityType="Model.User" />
</EntityContainer>
<EntityType Name="User">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Login" Nullable="false" />
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
</edmx:ConceptualModels>
Добавих персонализирана дефиниция на пространство от имена в Schema
елемент:xmlns:custom="http://tempuri.org/custom"
и дефинира персонализирана структурна анотация за CreatedAt
свойство:
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
Името на пространството от имена или елемента, използвани за структурна анотация, не са важни - зависи от вас какви имена ще използвате. Единственото важно нещо е edmx:CopyToSSDL="true"
атрибут. Този атрибут се разпознава от T4 шаблон, използван за създаване на SSDL, и той просто взема този елемент и го поставя в SSDL. Генерираният SSDL изглежда така:
<Schema Namespace="Model.Store" Alias="Self"
Provider="System.Data.SqlClient" ProviderManifestToken="2008"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="ModelStoreContainer">
<EntitySet Name="UsersSet" EntityType="Model.Store.UsersSet" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="UsersSet">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Login" Type="nvarchar(max)" Nullable="false" />
<Property Name="CreatedAt" Type="datetime" Nullable="false">
<custom:SqlType xmlns:custom="http://tempuri.org/custom">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
Единствената точка беше преместването на структурната анотация към SSDL. Всички анотации са достъпни в метаданни чрез някаква колекция от стойности на имена. Сега трябва да модифицирате T4 шаблон, отговорен за генерирането на SQL скрипт, за да разпознаете тази анотация и да използвате стойността, дефинирана в анотацията, вместо типа, дефиниран в свойството. Можете да намерите шаблона в:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen\SSDLToSQL10.tt
Копирайте файла на шаблона на ново място (за да не променяте оригиналния) и заменете създаването на таблица по подразбиране с това:
-- Creating table '<#=tableName#>'
CREATE TABLE <# if (!IsSQLCE) {#>[<#=schemaName#>].<#}#>[<#=tableName#>] (
<#
for (int p = 0; p < entitySet.ElementType.Properties.Count; p++)
{
EdmProperty prop = entitySet.ElementType.Properties[p];
#>
[<#=Id(prop.Name)#>] <#
if (prop.MetadataProperties.Contains("http://tempuri.org/custom:SqlType"))
{
MetadataProperty annotationProperty = prop.MetadataProperties["http://tempuri.org/custom:SqlType"];
XElement e = XElement.Parse(annotationProperty.Value.ToString());
string value = e.Value.Trim();
#>
<#=value#> <# } else { #> <#=prop.ToStoreType()#> <# } #> <#=WriteIdentity(prop, targetVersion)#> <#=WriteNullable(prop.Nullable)#><#=(p < entitySet.ElementType.Properties.Count - 1) ? "," : ""#>
<#
}
#>
);
GO
Сега последната точка е промяна на шаблона, използван за генериране на SQL скрипт. Отворете EDMX файла в дизайнера и отидете на свойствата на модела (просто щракнете някъде в дизайнера, докато имате отворен прозорец със свойства). Променете шаблона за генериране на DDL към шаблона, който сте променили.
Стартирайте Генериране на база данни от модел и ще създаде SQL скрипт, съдържащ:
-- Creating table 'UsersSet'
CREATE TABLE [dbo].[UsersSet] (
[Id] int IDENTITY(1,1) NOT NULL,
[Login] nvarchar(max) NOT NULL,
[CreatedAt] Date NOT NULL
);
GO
Това е може би най-модерната и скрита функция на EDMX, която съм виждал досега. Анотациите заедно с персонализирани T4 шаблони могат да ви осигурят голям контрол върху генерирането на клас и SQL. Мога да си представя да използвам това, за да дефинирам например индекси на бази данни или уникални ключове, когато първо използвам модел или да добавя избирателно някои потребителски атрибути към генерирани POCO класове.
Причината, поради която това е толкова скрито, е, че няма поддръжка на инструменти във VS извън кутията, за да се използва това.