7

Estoy empezando con Entity Framework 4.1, probando el modo "base de datos primero". Cuando EF genera una clase de modelo con el "ADO.Net DbContext Generator", ¿no debería identificar la clave primaria para la clase con un atributo [Key]? Sin esto, parece incompatible con el T4 MVCScaffolding.Entity Framework 4.1 La base de datos primero no agrega una clave principal a la clase generada DbContext T4

Aquí están los detalles:

utilizando la GUI de Entity Data Model Designer, he añadido una sencilla tabla de "país" con el modelo de mi base de datos existente. La GUI identifica correctamente un único campo clave de identidad entero llamado "PK" como mi clave principal. (¡Ay! Soy un nuevo usuario, así que no puedo agregar una captura de pantalla. He incluido el CSDL en su lugar a continuación). Sin embargo, cuando EF genera código usando el "ADO.Net DbContext Generator", no identifica el PK campo como el campo clave en la clase generada (ver fragmento de código a continuación).

El CSDL para la tabla de "país":

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

Aquí está el código autogenerado:

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

Esto causa un problema cuando trato de andamio un controlador utilizando la plantilla MVCScaffolding T4. Me sale un error "No parece que las propiedades sean claves principales". El comando y la salida de la consola NuGet Administrador de paquetes es el siguiente:

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 

Sin embargo, si cambio manualmente la clase generada para agregar un atributo [clave] para el campo, entonces el comando exactamente el mismo andamio se muestra más arriba funciona bien :

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

¿Por qué no EF Database First y el T4 MVCScaffolding funcionan bien juntos? E incluso sin el problema del andamiaje, ¿las clases EF no necesitan saber cuál es el campo clave?

Respuesta

5

T4 Templates no utiliza anotaciones de datos porque las clases generadas a partir de plantillas no las necesitan. EF tampoco los necesita porque el mapeo se define en archivos XML que no están en el código. Si necesita anotaciones de datos debe:

plantilla
  • Modificar T4 usarlos (esto requiere la comprensión del modelo de metadatos EF)
  • No utilizar plantillas y use el código primer lugar
  • buddy classes Uso de forma manual añadir anotaciones de datos y esperar que los andamios los reconocerá
+0

Gracias Ladislav. La modificación de las plantillas T4 va más allá de lo que quiero intentar en este momento. Intenté clases de compañeros (siguiendo http://stackoverflow.com/questions/4915957/using-system-componentmodel-dataannotations-with-entity-framework-4-0/), y aunque pude compilar y construir el proyecto sin errores, el paquete MVCScaffolding todavía no funcionaba. He agregado esto como un tema de discusión en el proyecto MVCScaffolding CodePlex (http://mvcscaffolding.codeplex.com/discussions/284993). Code First no es una opción porque estoy mapeando a un gran DB existente. –

+0

¿No hay alguna versión modificada de la plantilla T4 disponible? – Saber

2

Si alguien quiere hacer esto, he encontrado algunas buenas plantillas interesantes en james mannings github esas plantillas ja Tiene más funcionalidad, pero el bit que saqué de estos fue:
1) Reemplace los usos en la parte superior de la entidad.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) A continuación, busque esta línea (que imprime las propiedades)

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

3) y anteponer el código de plantilla

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