Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
Es posible definir un constructor con parámetros y hacer que EF Core llame a este constructor al crear una instancia de la entidad. Los parámetros del constructor se pueden enlazar a propiedades mapeadas o a varios tipos de servicios para facilitar comportamientos como la carga diferida.
Nota:
Actualmente, toda la vinculación de constructores se realiza por convención. La configuración de constructores específicos que se van a usar está planeada para una versión futura.
Enlace a las propiedades mapeadas
Considere un modelo típico de blog/publicación:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Cuando EF Core crea instancias de estos tipos, como para los resultados de una consulta, primero llamará al constructor sin parámetros predeterminado y, a continuación, establecerá cada propiedad en el valor de la base de datos. Sin embargo, si EF Core encuentra un constructor parametrizado cuyos nombres de parámetros y tipos coinciden con los de las propiedades mapeadas, llamará a este constructor con valores correspondientes para dichas propiedades y no establecerá cada propiedad explícitamente. Por ejemplo:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Cosas que tener en cuenta:
- No todas las propiedades deben tener parámetros de constructor. Por ejemplo, la propiedad Post.Content no está establecida por ningún parámetro de constructor, por lo que EF Core lo establecerá después de llamar al constructor de la manera normal.
- Los tipos y nombres de los parámetros deben coincidir con los tipos y nombres de las propiedades, salvo que las propiedades pueden usar notación Pascal (con mayúsculas iniciales en cada palabra) mientras que los parámetros utilizan notación camel (con minúscula inicial y mayúsculas internas).
- EF Core no puede establecer propiedades de navegación (como Blog o Entradas anteriores) mediante un constructor.
- El constructor puede ser público, privado o tener cualquier otra accesibilidad. Sin embargo, los servidores proxy de carga diferida requieren que el constructor sea accesible desde la clase de proxy heredada. Normalmente esto significa hacer que sea público o protegido.
Propiedades de solo lectura
Una vez que las propiedades se establecen a través del constructor, puede tener sentido que algunas de ellas sean de solo lectura. EF Core admite esto, pero hay algunas cosas a tener en cuenta.
- Las propiedades sin métodos set no se asocian por convención. (Al hacerlo, tiende a asignar propiedades que no se deben asignar, como las propiedades calculadas).
- El uso de valores de clave generados automáticamente requiere una propiedad de clave que sea de lectura y escritura, ya que el generador de claves debe establecer el valor de clave al insertar nuevas entidades.
Una manera fácil de evitar estas cosas es usar establecedores privados. Por ejemplo:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}
public int Id { get; private set; }
public string Name { get; private set; }
public string Author { get; private set; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Content { get; set; }
public DateTime PostedOn { get; private set; }
public Blog Blog { get; set; }
}
EF Core ve una propiedad con un establecedor privado como lectura y escritura, lo que significa que todas las propiedades se asignan como antes y la clave todavía se puede almacenar.
Una alternativa al uso de modificadores privados es hacer que las propiedades sean verdaderamente de solo lectura y agregar un mapeo más explícito en OnModelCreating. Del mismo modo, algunas propiedades se pueden quitar completamente y reemplazar solo por campos. Por ejemplo, considere estos tipos de entidad:
public class Blog
{
private int _id;
public Blog(string name, string author)
{
Name = name;
Author = author;
}
public string Name { get; }
public string Author { get; }
public ICollection<Post> Posts { get; } = new List<Post>();
}
public class Post
{
private int _id;
public Post(string title, DateTime postedOn)
{
Title = title;
PostedOn = postedOn;
}
public string Title { get; }
public string Content { get; set; }
public DateTime PostedOn { get; }
public Blog Blog { get; set; }
}
Y esta configuración en OnModelCreating:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Author);
b.Property(e => e.Name);
});
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Aspectos a considerar:
- La clave "property" es ahora un campo. No es un
readonlycampo para que se puedan usar claves generadas por el almacén. - Las otras propiedades son propiedades de solo lectura establecidas solo en el constructor.
- Si EF solo establece el valor de clave principal o lee de la base de datos, no es necesario incluirlo en el constructor. Esto deja la clave "propiedad" como un campo simple y deja claro que no debe establecerse explícitamente al crear nuevos blogs o publicaciones.
Nota:
Este código dará como resultado la advertencia del compilador "169" que indica que el campo nunca se usa. Esto se puede omitir porque, en realidad, EF Core usa el campo de forma extralinguista.
Inserción de servicios
EF Core también puede insertar "servicios" en el constructor de un tipo de entidad. Por ejemplo, se puede insertar lo siguiente:
-
DbContext- la instancia de contexto actual, que también puede ser tipificado como el tipo derivado DbContext. -
ILazyLoader- el servicio de carga diferida: consulte la documentación de carga diferida para obtener más detalles. -
Action<object, string>- un delegado de carga diferida: consulte la documentación de carga diferida para obtener más detalles. -
IEntityType: los metadatos de EF Core asociados a este tipo de entidad
Nota:
Actualmente, solo se pueden insertar servicios conocidos por EF Core. La compatibilidad con la inserción de servicios de aplicaciones se está considerando para una versión futura.
Por ejemplo, se puede usar dbContext insertado para acceder de forma selectiva a la base de datos para obtener información sobre las entidades relacionadas sin cargarlas todas. En el ejemplo siguiente, se usa para obtener el número de entradas de un blog sin cargar las entradas:
public class Blog
{
public Blog()
{
}
private Blog(BloggingContext context)
{
Context = context;
}
private BloggingContext Context { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public ICollection<Post> Posts { get; set; }
public int PostsCount
=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == EF.Property<int?>(p, "BlogId"))
?? 0;
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }
public Blog Blog { get; set; }
}
Algunas cosas que hay que tener en cuenta sobre esto:
- El constructor es privado, ya que solo EF Core lo invoca y hay otro constructor público para uso más general.
- El código que usa el servicio inyectado (es decir, el contexto) es defensivo frente a que este sea
nullpara manejar casos en los que EF Core no crea la instancia. - Dado que el servicio se almacena en una propiedad de lectura y escritura, se restablecerá cuando la entidad se adjunte a una nueva instancia de contexto.
Advertencia
Inyectar el DbContext de esta manera a menudo se considera un antipatrón, ya que acopla directamente los tipos de entidad a EF Core. Tenga en cuenta cuidadosamente todas las opciones antes de usar la inyección de servicio como esta.