Entity Framework 4.1 Database First не добавляет первичный ключ в сгенерированный класс DbContext T4

Я только начинаю работать с Entity Framework 4.1, пробуя режим "сначала база данных". Когда EF создает класс модели с помощью «ADO.Net DbContext Generator», не должен ли он идентифицировать первичный ключ для класса с помощью атрибута [Key]? Без этого он кажется несовместимым с T4 MVCScaffolding.

Вот подробности:

Используя графический интерфейс Entity Data Model Designer, я добавил в модель простую таблицу «country» из моей существующей базы данных. Графический интерфейс пользователя правильно идентифицирует одно поле ключа целочисленной идентификации с именем «PK» в качестве моего первичного ключа. (Увы! Я новый пользователь, поэтому не могу добавить снимок экрана. Вместо этого ниже я включил CSDL.) Однако, когда EF генерирует код с помощью «ADO.Net DbContext Generator», он не идентифицирует ПК. поле в качестве ключевого поля в сгенерированном классе (см. фрагмент кода ниже).

CSDL для таблицы "country":

<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).
  • Не используйте шаблоны и сначала используйте код
  • Используйте классы друзей, чтобы вручную добавлять аннотации к данным. и надеюсь, что строительные леса их узнают
person Ladislav Mrnka    schedule 07.12.2011
comment
Спасибо Ладислав. Изменение шаблонов T4 выходит за рамки того, что я хочу сделать прямо сейчас. Я попробовал классы друзей (после stackoverflow.com/questions/4915957/), и хотя мне удалось скомпилировать и собрать проект без ошибок, пакет MVCScaffolding по-прежнему не работал. Я добавил это как тему для обсуждения в рамках проекта MVCScaffolding CodePlex (mvcscaffolding.codeplex.com/discussions/284993 ). Code First не подходит, потому что я сопоставляю большую существующую БД. - person Martin_W; 05.01.2012
comment
Нет ли доступной модифицированной версии шаблона T4? - person Saber; 02.10.2012

Если кто-то действительно хочет это сделать, я нашел несколько хороших интересных шаблонов на james mannings 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();
person Mike Walsh    schedule 20.12.2013