7

Sono appena iniziato con Entity Framework 4.1, provando la modalità "database first". Quando EF genera una classe Model con il "Generatore DbContext di ADO.Net", non dovrebbe identificare la chiave primaria per la classe con un attributo [Chiave]? In caso contrario, appare incompatibile con il T4 MVCScaffolding.Entity Framework 4.1 Database First non aggiunge una chiave primaria alla classe generata da DbContext T4

Ecco i dettagli:

utilizzando la GUI Designer Entity Data Model, ho aggiunto un semplice "country" tabella per il modello dal mio database esistente. La GUI identifica correttamente un singolo campo della chiave di identità intero chiamato "PK" come chiave primaria. (Ahimè, sono un nuovo utente, quindi non posso aggiungere uno screenshot. Ho incluso il CSDL invece di seguito.) Tuttavia, quando EF genera codice usando "ADO.Net DbContext Generator", non identifica il PK campo come campo chiave nella classe generata (vedere l'estratto del codice di seguito).

Il CSDL per la tabella "paese":

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

Ecco il codice generato automaticamente:

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

Ciò causa un problema quando cerco di patibolo un controller utilizzando il modello MVCScaffolding T4. Viene visualizzato un messaggio di errore "Nessuna proprietà sembra essere la chiave principale". Il comando e uscita dalla Console NuGet Gestione pacchetti è inferiore:

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 

Tuttavia, se cambio manualmente la classe generata per aggiungere un attributo [Key] per il campo, quindi l'esatto comando stesso ponteggio mostrato sopra funziona bene :

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

Allora, perché non sono EF database primo e l'T4 MVCScaffolding giocando bello insieme? E anche senza il problema dello scaffolding, le classi EF non devono sapere quali sono i campi chiave?

risposta

5

I modelli T4 non utilizzano annotazioni di dati perché le classi generate dai modelli non ne hanno bisogno. Anche EF non ne ha bisogno perché la mappatura è definita in file XML non in codice. Se avete bisogno di annotazioni di dati è necessario:

modello
  • Modifica T4 usarli (questo richiede comprensione del modello di metadati EF)
  • Non usare modelli e utilizzare il codice prima invece
  • Usa buddy classes manualmente aggiungere annotazioni di dati e la speranza che impalcature riconoscerà loro
+0

Grazie Ladislav. La modifica dei modelli T4 è al di là di ciò che voglio tentare in questo momento. Ho provato le classi buddy (seguendo http://stackoverflow.com/questions/4915957/using-system-componentmodel-dataannotations-with-entity-framework-4-0/) e sebbene potessi compilare e creare il progetto senza errori, il pacchetto MVCScaffolding non funzionava ancora. Ho aggiunto questo come argomento di discussione nel progetto CodePlex MVCScaffolding (http://mvcscaffolding.codeplex.com/discussions/284993). Code First non è un'opzione perché sto mappando su un grande DB esistente. –

+0

Non è disponibile alcuna versione modificata del modello T4? – Saber

2

Se qualcuno vuole fare questo, ho trovato alcuni buoni modelli interessanti sulla james mannings github quei modelli ettari C'è più funzionalità, ma il pezzo che ho estratto è stato:
1) Sostituire gli usi nella parte superiore di Entity.TT con

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

2) Poi trovare questa linea (che stampa le proprietà)

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

3) e anteporre questo codice modello

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