我刚刚开始使用实体框架4.1,尝试"数据库优先"模式。当EF使用"ADO"生成一个Model类时。Net DbContext生成器,"它不应该用[key]属性标识类的主键吗?如果没有这个,它似乎与T4 mvc脚手架不兼容。
详情如下:
使用实体数据模型设计器GUI,我从现有数据库中向模型添加了一个简单的"国家"表。GUI正确地将一个名为"PK"的整数标识键字段标识为主键。(唉!我是新用户,所以不能添加截图。我在下面包含了CSDL。)但是,当EF使用"ADO"生成代码时。. Net DbContext Generator",它不会将PK字段识别为生成类中的关键字段(参见下面的代码摘录)。
"country"表的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; }
}
}
当我尝试使用mvcs脚手架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:workEPIEPIC_MVC3sandboxMvcApplication1packagesMvcScaffolding.1.0.6toolsControllerMvcScaffolding.Controller.ps1:74 char:29
+ $primaryKey = Get-PrimaryKey <<<< $foundModelType.FullName -Project $Project -ErrorIfNotFound
+ CategoryInfo : NotSpecified: (:) [Get-PrimaryKey], Exception
+ FullyQualifiedErrorId : T4Scaffolding.Cmdlets.GetPrimaryKeyCmdlet
但是,如果我手动更改生成的类以向字段添加[Key]属性,那么上面显示的完全相同的scaffolding命令可以正常工作:
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数据库第一和T4 mvc脚手架一起玩得不好呢?即使没有脚手架问题,EF类不需要知道关键字段是什么吗?
T4 Templates不使用数据注释,因为从模板生成的类不需要它们。EF也不需要它们,因为映射是在XML文件中定义的,而不是在代码中。如果需要数据注释,则必须:
- 修改T4模板来使用它们(这需要理解EF元数据模型)
- 不要使用模板,先用代码代替
- 使用好友类手动添加数据注释,并希望scaffolding能够识别它们
如果有人想这样做,我发现了一些很有趣的模板James manning github这些模板有更多的功能,但我从中提取的部分是:
1)替换实体顶部的用法。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();