Entity Framework 4.1 Database First does not add a primary key to the derived DbContext T4 class

I'm just getting started with Entity Framework 4.1, checking the "database first" mode. When EF generates a Model class with "ADO.Net DbContext Generator", should it not identify the primary key for the class with the [Key] attribute? Without it, it seems incompatible with the T4 MVCScaffolding.

Here are the details:

Using the Designer Data Entity Data Modeler GUI, I added a simple country table to my model from my existing database. The GUI correctly identifies a single integer identity key field with the name "PK" as my primary key. (Alas, I am a new user, so I can’t add a screenshot. I turned on CSDL instead.) However, when EF generates code using the “ADO.Net DbContext Generator”, it does not identify the PC field as the key field in the generated class ( see code excerpt below).

CSDL for country table:

<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> 

Here's the autogenerated code:

 //------------------------------------------------------------------------------ // <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; } } } 

This causes a problem when I try to tint a controller using the MVCScaffolding T4 template. I get the error "No properties are primary keys." Command and output from the NuGet Package Manager console:

 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 

However, if I manually modified the generated class to add the [Key] attribute to the field, then the exact same scaffolding command shown above works fine:

 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; } } } 

So why aren't EF Database First and T4 MVCScaffolding playing well together? And even without problems with forests, should EF classes not know what a key field is?

+7
source share
2 answers

T4 templates do not use data annotations because classes created from templates do not need them. They don’t need EF either, because mapping is defined in XML files, not in code. If you need data annotations, you must either:

  • Modify the T4 template to use them (this requires an understanding of the EF metadata model)
  • Do not use patterns and use code first
  • Use buddy classes to manually add data annotations and hope scaffolding recognize them.
+5
source

If someone really wants to do this, I found some interesting interesting templates james mannings github These templates have more functionality, but the bit that I pulled from them was:
1) Replace the entries at the top of Entity.tt with

 using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; <# if (efHost.EntityFrameworkVersion >= new Version(4, 4)) { WriteLine("using System.ComponentModel.DataAnnotations.Schema;"); } #> 

2) Then find this line (which returns the properties)

  <#= Accessibility.ForProperty(property) #> <#= typeUsage #> <#= code.Escape(property) #> { get; set; } 

3) and add this template code

  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(); 
+2
source

All Articles