Entity Framework 4.1 Database First не добавляет первичный ключ в сгенерированный класс DbContext T4
Я только начинаю с Entity Framework 4.1, пробую режим "база данных сначала". Когда EF генерирует класс Model с помощью "ADO.Net DbContext Generator", не должен ли он идентифицировать первичный ключ для класса с атрибутом [Key]? Без этого он кажется несовместимым с T4 MVCScaffolding.
Вот подробности:
Используя графический интерфейс Entity Data Model Designer, я добавил простую таблицу "страна" в модель из моей существующей базы данных. GUI правильно идентифицирует одно целочисленное поле ключа идентификации с именем "PK" в качестве моего первичного ключа. (Увы! Я новый пользователь, поэтому я не могу добавить снимок экрана. Вместо этого я включил CSDL.) Однако, когда EF генерирует код с помощью "ADO.Net DbContext Generator", он не идентифицирует PK поле в качестве ключевого поля в сгенерированном классе (см. фрагмент кода ниже).
CSDL для таблицы "страна":
<edmx:ConceptualModels>
<Schema Namespace="EpiDataModel" Alias="Self" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2008/09/edm">
<EntityContainer Name="EpiModelEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="countries" EntityType="EpiDataModel.country" />
</EntityContainer>
<EntityType Name="country">
<Key>
<PropertyRef Name="PK" />
</Key>
<Property Name="PK" Type="Int32" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Name="Abbreviation" Type="String" Nullable="false" MaxLength="200" Unicode="false" FixedLength="false" />
<Property Name="Name" Type="String" MaxLength="1024" Unicode="false" FixedLength="false" />
<Property Name="Description" Type="String" MaxLength="1024" Unicode="false" FixedLength="false" />
<Property Name="Sequence" Type="Int32" />
</EntityType>
</Schema>
</edmx:ConceptualModels>
Вот автоматически сгенерированный код:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace MvcApplication1.Areas.Epi.Models
{
public partial class country
{
public int PK { get; set; }
public string Abbreviation { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Sequence { get; set; }
}
}
Это вызывает проблему, когда я пытаюсь построить контроллер с помощью шаблона MVCScaffolding T4. Я получаю сообщение об ошибке "Свойства не являются первичными ключами". Команда и вывод из консоли диспетчера пакетов NuGet приведены ниже:
PM> scaffold controller MvcApplication1.Areas.Epi.Models.country -Area Epi -NoChildItems -DbContextType MvcApplication1.Areas.Epi.Models.EpiModelEntities -Force
Scaffolding countriesController...
Get-PrimaryKey : Cannot find primary key property for type 'MvcApplication1.Areas.Epi.Models.country'. No properties appear to be primary keys.
At C:\work\EPI\EPIC_MVC3\sandbox\MvcApplication1\packages\MvcScaffolding.1.0.6\tools\Controller\MvcScaffolding.Controller.ps1:74 char:29
+ $primaryKey = Get-PrimaryKey <<<< $foundModelType.FullName -Project $Project -ErrorIfNotFound
+ CategoryInfo : NotSpecified: (:) [Get-PrimaryKey], Exception
+ FullyQualifiedErrorId : T4Scaffolding.Cmdlets.GetPrimaryKeyCmdlet
Однако если я вручную изменю сгенерированный класс, добавив в поле атрибут [Key], то точно такая же команда скаффолдинга, показанная выше, будет работать нормально:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; // manually added
namespace MvcApplication1.Areas.Epi.Models
{
public partial class country
{
[Key] // manually added
public int PK { get; set; }
public string Abbreviation { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Sequence { get; set; }
}
}
Так почему же EF Database First и T4 MVCScaffolding не играют вместе? И даже без проблемы со скаффолдингом, разве классы EF не должны знать, каковы ключевые поля?
2 ответа
Шаблоны T4 не используют аннотации данных, потому что классы, созданные из шаблонов, не нуждаются в них. EF также не нуждается в них, потому что отображение определено в файлах XML, а не в коде. Если вам нужны аннотации данных, вы должны либо:
- Модифицируйте шаблон T4, чтобы использовать их (это требует понимания модели метаданных EF)
- Не используйте шаблоны и сначала используйте код
- Используйте классы друзей, чтобы вручную добавлять аннотации данных, и надеяться, что леса распознают их
Если кто-то захочет сделать это, я найду несколько хороших интересных шаблонов на github Джеймса Мэннингса. Эти шаблоны обладают большей функциональностью, но кое-что из этого я извлек:
1) Заменить использование в верхней части Entity.tt с
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
<#
if (efHost.EntityFrameworkVersion >= new Version(4, 4))
{
WriteLine("using System.ComponentModel.DataAnnotations.Schema;");
}
#>
2) Затем найдите эту строку (которая распечатывает свойства)
<#= Accessibility.ForProperty(property) #> <#= typeUsage #> <#= code.Escape(property) #> { get; set; }
3) и добавьте этот шаблон кода
var attributes = new List<string>();
var isPartOfPrimaryKey = efHost.EntityType.KeyMembers.Contains(property);
var primaryKeyHasMultipleColumns = efHost.EntityType.KeyMembers.Count > 1;
if (isPartOfPrimaryKey)
{
if (primaryKeyHasMultipleColumns)
{
var columnNumber = efHost.EntityType.KeyMembers.IndexOf(property);
attributes.Add(String.Format("[Key, Column(Order = {0})]", columnNumber));
}
else
{
attributes.Add("[Key]");
}
}
PushIndent(new string(' ', 8));
foreach (var attribute in attributes)
{
WriteLine(attribute);
}
ClearIndent();