【问题标题】:Entity Framework 4.1 Database First does not add a primary key to the DbContext T4 generated classEntity Framework 4.1 Database First 不向 DbContext T4 生成的类添加主键
【发布时间】:2011-12-07 18:07:05
【问题描述】:

我刚刚开始使用 Entity Framework 4.1,尝试“数据库优先”模式。当 EF 使用“ADO.Net DbContext Generator”生成模型类时,它不应该使用 [Key] 属性标识类的主键吗?没有这个,它似乎与 T4 MVCScaffolding 不兼容。

以下是详细信息:

使用实体数据模型设计器 GUI,我从现有数据库中向模型添加了一个简单的“国家”表。 GUI 正确地将名为“PK”的单个整数标识键字段识别为我的主键。 (唉!我是新用户,所以我无法添加屏幕截图。我在下面包含了 CSDL。)但是,当 EF 使用“ADO.Net DbContext Generator”生成代码时,它无法识别 PK field 作为生成类中的关键字段(参见下面的代码摘录)。

“国家”表的 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 类是否不需要知道关键字段是什么?

【问题讨论】:

    标签: entity-framework-4.1 primary-key dbcontext asp.net-mvc-scaffolding ef-database-first


    【解决方案1】:

    T4 模板不使用数据注释,因为从模板生成的类不需要它们。 EF 也不需要它们,因为映射是在 XML 文件中而不是在代码中定义的。如果您需要数据注释,您必须:

    • 修改 T4 模板以使用它们(这需要了解 EF 元数据模型)
    • 不要使用模板,而是先使用代码
    • 使用buddy classes手动添加数据注解,希望脚手架能够识别

    【讨论】:

    • 谢谢拉迪斯拉夫。修改 T4 模板超出了我现在想要尝试的范围。我尝试了 buddy classes(跟随stackoverflow.com/questions/4915957/…),虽然我可以编译和构建项目而没有错误,但 MVCScaffolding 包仍然不起作用。我已将此添加为 MVCScaffolding CodePlex 项目 (mvcscaffolding.codeplex.com/discussions/284993) 下的讨论主题。 Code First 不是一个选项,因为我正在映射到现有的大型数据库。
    • 没有修改版的T4模板吗?
    【解决方案2】:

    如果有人确实想这样做,我在 james mannings github 这些模板具有更多功能,但我从中提取的一点是:
    1) 将 Entity.tt 顶部的 usings 替换为

    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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-24
      • 2011-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多