Nota
L'accés a aquesta pàgina requereix autorització. Podeu provar d'iniciar la sessió o de canviar els directoris.
L'accés a aquesta pàgina requereix autorització. Podeu provar de canviar els directoris.
Cuando se trabaja con Entity Framework Code First, el comportamiento predeterminado es asignar las clases POCO a las tablas mediante un conjunto de convenciones integradas en EF. A veces, sin embargo, no puede o no desea seguir esas convenciones y necesita asignar entidades a algo distinto de lo que dictan las convenciones.
Hay dos formas principales de configurar EF para que usen algo distinto de las convenciones, es decir, anotaciones o API fluidas de EFs. Las anotaciones solo cubren un subconjunto de la funcionalidad de la API fluida, por lo que hay escenarios de mapeo que no se pueden lograr mediante anotaciones. Este artículo está diseñado para demostrar cómo usar la API fluida para configurar las propiedades.
Para obtener acceso a la API fluida del código primero, se reemplaza el método OnModelCreating en dbContext derivado. Los ejemplos siguientes están diseñados para mostrar cómo realizar varias tareas con la API fluida y le permiten copiar el código y personalizarlo para adaptarlo a su modelo, si desea ver el modelo que se pueden usar con as-is, se proporciona al final de este artículo.
Configuración global del modelo
Esquema predeterminado (EF6 en adelante)
A partir de EF6, puede usar el método HasDefaultSchema en DbModelBuilder para especificar el esquema de base de datos que se va a usar para todas las tablas, procedimientos almacenados, etc. Esta configuración predeterminada se invalidará para todos los objetos para los que configure explícitamente un esquema diferente.
modelBuilder.HasDefaultSchema("sales");
Convenciones personalizadas (EF6 en adelante)
A partir de EF6, puede crear sus propias convenciones para complementar las incluidas en Code First. Para obtener más información, consulte Convenciones de Custom Code First.
Asignación de propiedades
El método Property se usa para configurar atributos para cada propiedad que pertenezca a una entidad o a un tipo complejo. El método Property se usa para obtener un objeto de configuración para una propiedad determinada. Las opciones del objeto de configuración son específicas del tipo que se está configurando; IsUnicode solo está disponible en las propiedades de cadena, por ejemplo.
Configuración de una clave principal
La convención de Entity Framework para las claves principales es:
- La clase define una propiedad cuyo nombre es "ID" o "Id".
- o un nombre de clase seguido de "ID" o "Id".
Para establecer explícitamente una propiedad como una clave principal, puede usar el método HasKey. En el ejemplo siguiente, el método HasKey se usa para configurar la clave principal InstructorID en el tipo OfficeAssignment.
modelBuilder.Entity<OfficeAssignment>().HasKey(t => t.InstructorID);
Configuración de una clave principal compuesta
En el ejemplo siguiente se configuran las propiedades DepartmentID y Name para que sean la clave principal compuesta del tipo Department.
modelBuilder.Entity<Department>().HasKey(t => new { t.DepartmentID, t.Name });
Desactivación de la identidad de las claves principales numéricas
En el ejemplo siguiente se establece la propiedad DepartmentID en System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None para indicar que la base de datos no generará el valor.
modelBuilder.Entity<Department>().Property(t => t.DepartmentID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
Especificar la longitud máxima en una propiedad
En el ejemplo siguiente, la propiedad Name no debe tener más de 50 caracteres. Si hace que el valor tenga más de 50 caracteres, obtendrá una excepción DbEntityValidationException . Si Code First crea una base de datos a partir de este modelo, también establecerá la longitud máxima de la columna Nombre en 50 caracteres.
modelBuilder.Entity<Department>().Property(t => t.Name).HasMaxLength(50);
Configuración de la propiedad que se va a requerir
En el ejemplo siguiente, se requiere la propiedad Name. Si no especifica el Nombre, obtendrá una excepción de tipo DbEntityValidationException. Si Code First crea una base de datos a partir de este modelo, la columna que se usa para almacenar esta propiedad normalmente no acepta valores NULL.
Nota:
En algunos casos, es posible que no se pueda establecer la columna de la base de datos como no aceptante de valores NULL, aunque la propiedad sea requerida. Por ejemplo, cuando se utiliza una estrategia de herencia TPH, los datos de varios tipos se almacenan en una sola tabla. Si un tipo derivado incluye una propiedad necesaria, la columna no se puede hacer que no admita valores NULL, ya que no todos los tipos de la jerarquía tendrán esta propiedad.
modelBuilder.Entity<Department>().Property(t => t.Name).IsRequired();
Configuración de un índice en una o varias propiedades
Nota:
SOLO EF6.1 y versiones posteriores : el atributo Index se introdujo en Entity Framework 6.1. Si usa una versión anterior, la información de esta sección no se aplica.
La creación de índices no es compatible de forma nativa con fluent API, pero puede usar la compatibilidad con IndexAttribute a través de fluent API. Los atributos de índice se procesan mediante la inclusión de una anotación de modelo en el modelo, que luego se transforma en un índice en la base de datos más adelante en el flujo de trabajo. Puede agregar manualmente estas mismas anotaciones mediante fluent API.
La manera más fácil de hacerlo es crear una instancia de IndexAttribute que contenga toda la configuración del nuevo índice. A continuación, puede crear una instancia de IndexAnnotation , que es un tipo específico de EF que convertirá la configuración de IndexAttribute en una anotación de modelo que se puede almacenar en el modelo de EF. A continuación, se pueden pasar al método HasColumnAnnotation del Fluent API, especificando el nombre Index para la anotación.
modelBuilder
.Entity<Department>()
.Property(t => t.Name)
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));
Para obtener una lista completa de la configuración disponible en IndexAttribute, consulte la sección Índice de anotaciones de datos de Code First. Esto incluye personalizar el nombre del índice, crear índices únicos y crear índices de varias columnas.
Puede especificar varias anotaciones de índice en una sola propiedad pasando una matriz de IndexAttribute al constructor de IndexAnnotation.
modelBuilder
.Entity<Department>()
.Property(t => t.Name)
.HasColumnAnnotation(
"Index",
new IndexAnnotation(new[]
{
new IndexAttribute("Index1"),
new IndexAttribute("Index2") { IsUnique = true }
})));
Especificar no asignar una propiedad CLR a una columna de la base de datos
En el ejemplo siguiente se muestra cómo especificar que una propiedad de un tipo CLR no está asignada a una columna de la base de datos.
modelBuilder.Entity<Department>().Ignore(t => t.Budget);
Asignación de una propiedad CLR a una columna específica de la base de datos
En el ejemplo siguiente se asigna la propiedad NAME CLR a la columna de base de datos DepartmentName.
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.HasColumnName("DepartmentName");
Cambiar el nombre de una clave externa que no está definida en el modelo
Si decide no definir una clave externa en un tipo CLR, pero desea especificar el nombre que debe tener en la base de datos, haga lo siguiente:
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(t => t.Courses)
.Map(m => m.MapKey("ChangedDepartmentID"));
Configuración de si una propiedad String admite contenido Unicode
De forma predeterminada, las cadenas son Unicode (nvarchar en SQL Server). Puede usar el método IsUnicode para especificar que una cadena debe ser de tipo varchar.
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.IsUnicode(false);
Configuración del tipo de datos de una columna de base de datos
El método HasColumnType permite el mapeo a diferentes representaciones del mismo tipo básico. El uso de este método no permite realizar ninguna conversión de los datos en tiempo de ejecución. Tenga en cuenta que IsUnicode es la forma preferida de establecer columnas en varchar, ya que es independiente de la base de datos.
modelBuilder.Entity<Department>()
.Property(p => p.Name)
.HasColumnType("varchar");
Configuración de propiedades en un tipo complejo
Hay dos maneras de configurar propiedades escalares en un tipo complejo.
Puede llamar a Property en ComplexTypeConfiguration.
modelBuilder.ComplexType<Details>()
.Property(t => t.Location)
.HasMaxLength(20);
También puede usar la notación de puntos para tener acceso a una propiedad de un tipo complejo.
modelBuilder.Entity<OnsiteCourse>()
.Property(t => t.Details.Location)
.HasMaxLength(20);
Configuración de una propiedad para usar como token de simultaneidad optimista
Para especificar que una propiedad de una entidad representa un token de simultaneidad, puede usar el atributo ConcurrencyCheck o el método IsConcurrencyToken.
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.IsConcurrencyToken();
También puede usar el método IsRowVersion para configurar la propiedad para que sea una versión de fila en la base de datos. Al establecer la propiedad como una versión de fila, se configura automáticamente para que sea un token de concurrencia optimista.
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.IsRowVersion();
Asignación de tipos
Especificar que una clase es un tipo complejo
Por convención, un tipo que no tiene ninguna clave principal especificada se trata como un tipo complejo. Hay algunos escenarios en los que Code First no detectará un tipo complejo (por ejemplo, si tiene una propiedad denominada ID, pero no significa que sea una clave principal). En tales casos, usaría la API fluida para especificar explícitamente que un tipo es un tipo complejo.
modelBuilder.ComplexType<Details>();
Especificar no mapear un tipo de entidad CLR a una tabla en la base de datos
En el ejemplo siguiente se muestra cómo excluir un tipo CLR de ser asignado a una tabla de la base de datos.
modelBuilder.Ignore<OnlineCourse>();
Asignación de un tipo de entidad a una tabla específica de la base de datos
Todas las propiedades de Department se asignarán a columnas de una tabla denominada t_ Department.
modelBuilder.Entity<Department>()
.ToTable("t_Department");
También puede especificar el nombre del esquema de la siguiente manera:
modelBuilder.Entity<Department>()
.ToTable("t_Department", "school");
Asignación de la herencia Tabla-Por-Jerarquía (TPH)
En el escenario de mapeo de TPH, todos los tipos de una jerarquía de herencia se mapean a una sola tabla. Se usa una columna descriptiva para identificar el tipo de cada fila. Al crear el modelo con Code First, TPH es la estrategia predeterminada para los tipos que participan en la jerarquía de herencia. De forma predeterminada, la columna discriminadora se agrega a la tabla con el nombre "Discriminador" y el nombre de tipo CLR de cada tipo de la jerarquía se usa para los valores discriminadores. Puede modificar el comportamiento predeterminado mediante la API fluida.
modelBuilder.Entity<Course>()
.Map<Course>(m => m.Requires("Type").HasValue("Course"))
.Map<OnsiteCourse>(m => m.Requires("Type").HasValue("OnsiteCourse"));
Asignación de la herencia de tabla por tipo (TPT)
En el escenario de mapeo de TPT, todos los tipos se asignan a tablas individuales. Las propiedades que pertenecen únicamente a un tipo base o a un tipo derivado se almacenan en una tabla que se asigna a ese tipo. Las tablas que se asignan a tipos derivados también almacenan una clave externa que combina la tabla derivada con la tabla base.
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");
Asignación de la herencia de la clase Tabla-Por-Concreto (TPC)
En el escenario de mapeo de TPC, todos los tipos que no son abstractos en la jerarquía se mapean a tablas individuales. Las tablas que se asignan a las clases derivadas no tienen ninguna relación con la tabla que se asigna a la clase base de la base de datos. Todas las propiedades de una clase, incluidas las propiedades heredadas, se asignan a columnas de la tabla correspondiente.
Llame al método MapInheritedProperties para configurar cada tipo derivado. MapInheritedProperties vuelve a asignar todas las propiedades que se heredaron de la clase base a nuevas columnas de la tabla para la clase derivada.
Nota:
Tenga en cuenta que, dado que las tablas que participan en la jerarquía de herencia de TPC no comparten una clave principal, habrá claves de entidad duplicadas al insertar en tablas asignadas a subclases si tiene valores generados por la base de datos con la misma inicialización de identidad. Para solucionar este problema, puede especificar un valor de inicialización diferente para cada tabla o desactivar la identidad en la propiedad de clave principal. Identity es el valor predeterminado para las propiedades de clave entera al trabajar con Code First.
modelBuilder.Entity<Course>()
.Property(c => c.CourseID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<OnsiteCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("OnsiteCourse");
});
modelBuilder.Entity<OnlineCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("OnlineCourse");
});
Asignación de propiedades de un tipo de entidad a varias tablas en la base de datos (división de entidades)
La división de entidades permite distribuir las propiedades de un tipo de entidad entre varias tablas. En el ejemplo siguiente, la entidad Departamento se divide en dos tablas: Departamento y DetallesDelDepartamento. La división de entidades usa varias llamadas al método Map para asignar un subconjunto de propiedades a una tabla específica.
modelBuilder.Entity<Department>()
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Name });
m.ToTable("Department");
})
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Administrator, t.StartDate, t.Budget });
m.ToTable("DepartmentDetails");
});
Asignación de varios tipos de entidad a una tabla en la base de datos (división de tablas)
En el ejemplo siguiente se asignan dos tipos de entidad que comparten una clave principal a una tabla.
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");
Asignación de un tipo de entidad para insertar, actualizar o eliminar procedimientos almacenados (EF6 en adelante)
A partir de EF6, puede asignar una entidad para usar procedimientos almacenados para insertar actualizaciones y eliminar. Para obtener más información, consulte Code First Insert/Update/Delete Stored Procedures.
Modelo usado en ejemplos
El siguiente modelo de Code First se usa para los ejemplos de esta página.
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;
public class SchoolEntities : DbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
public class Department
{
public Department()
{
this.Courses = new HashSet<Course>();
}
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public System.DateTime StartDate { get; set; }
public int? Administrator { get; set; }
// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}
public class Course
{
public Course()
{
this.Instructors = new HashSet<Instructor>();
}
// Primary key
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}
public partial class OnlineCourse : Course
{
public string URL { get; set; }
}
public partial class OnsiteCourse : Course
{
public OnsiteCourse()
{
Details = new Details();
}
public Details Details { get; set; }
}
public class Details
{
public System.DateTime Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}
public class Instructor
{
public Instructor()
{
this.Courses = new List<Course>();
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
public class OfficeAssignment
{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }
public string Location { get; set; }
// When Entity Framework sees Timestamp attribute
// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }
// Navigation property
public virtual Instructor Instructor { get; set; }
}