Tipos de entidad sin llaveKeyless Entity Types

Nota

Esta característica se agregó en EF Core 2,1 bajo el nombre de los tipos de consulta.This feature was added in EF Core 2.1 under the name of query types. En EF Core 3,0 se cambió el nombre del concepto a tipos de entidad sin entrada.In EF Core 3.0 the concept was renamed to keyless entity types.

Además de los tipos de entidad normales, un modelo de EF Core puede contener _tipos de entidad_sin clave, que se pueden usar para realizar consultas de base de datos con datos que no contengan valores de clave.In addition to regular entity types, an EF Core model can contain keyless entity types, which can be used to carry out database queries against data that doesn't contain key values.

Características de tipos de entidad sin llaveKeyless entity types characteristics

Los tipos de entidad sin llave admiten muchas de las mismas capacidades de asignación que los tipos de entidad normales, como las propiedades de navegación y asignación de herencia.Keyless entity types support many of the same mapping capabilities as regular entity types, like inheritance mapping and navigation properties. En almacenes relacionales, pueden configurar los objetos de base de datos de destino y las columnas a través de métodos de la API fluidos o las anotaciones de datos.On relational stores, they can configure the target database objects and columns via fluent API methods or data annotations.

Sin embargo, son diferentes de los tipos de entidad normales en que:However, they are different from regular entity types in that they:

  • No se puede definir una clave.Cannot have a key defined.
  • Nunca se realiza un seguimiento de los cambios en DbContext y, por lo tanto, nunca se insertan, actualizan o eliminan en la base de datos.Are never tracked for changes in the DbContext and therefore are never inserted, updated or deleted on the database.
  • Nunca se detectan por convención.Are never discovered by convention.
  • Solo admite un subconjunto de capacidades de asignación de navegación, en concreto:Only support a subset of navigation mapping capabilities, specifically:
    • Nunca pueden actuar como el extremo principal de una relación.They may never act as the principal end of a relationship.
    • Puede que no tengan navegaciones a entidades propiedadThey may not have navigations to owned entities
    • Solo pueden contener propiedades de navegación de referencia que apunten a entidades normales.They can only contain reference navigation properties pointing to regular entities.
    • Las entidades no pueden contener propiedades de navegación a tipos de entidad sin llave.Entities cannot contain navigation properties to keyless entity types.
  • Debe configurarse con .HasNoKey() llamada al método.Need to be configured with .HasNoKey() method call.
  • Se puede asignar a una consulta de definición.May be mapped to a defining query. Una consulta de definición es una consulta declarada en el modelo que actúa como origen de datos para un tipo de entidad sin entrada.A defining query is a query declared in the model that acts as a data source for a keyless entity type.

Escenarios de usoUsage scenarios

Algunos de los escenarios de uso principales de los tipos de entidad sin llave son:Some of the main usage scenarios for keyless entity types are:

  • Actúa como el tipo de valor devuelto para las consultas SQL sin procesar.Serving as the return type for raw SQL queries.
  • Asignación a vistas de base de datos que no contienen una clave principal.Mapping to database views that do not contain a primary key.
  • Asignación de tablas que no tiene definida una clave principal.Mapping to tables that do not have a primary key defined.
  • Asignación de las consultas definidas en el modelo.Mapping to queries defined in the model.

Asignación de objetos de base de datosMapping to database objects

La asignación de un tipo de entidad sin llave a un objeto de base de datos se consigue mediante el ToTable o ToView API fluida.Mapping a keyless entity type to a database object is achieved using the ToTable or ToView fluent API. Desde la perspectiva de EF Core, el objeto de base de datos especificado en este método es un vista, lo que significa que se trata como un origen de consulta de solo lectura y no puede ser el destino de la actualización, insertar o eliminar operaciones.From the perspective of EF Core, the database object specified in this method is a view, meaning that it is treated as a read-only query source and cannot be the target of update, insert or delete operations. Sin embargo, esto no significa que el objeto de base de datos sea realmente necesario para ser una vista de base de datos.However, this does not mean that the database object is actually required to be a database view. Como alternativa, puede tratarse de una tabla de base de datos que se tratará como de solo lectura.It can alternatively be a database table that will be treated as read-only. Por el contrario, en el caso de los tipos de entidad normales, EF Core supone que un objeto de base de datos especificado en el método ToTable se puede tratar como una tabla, lo que significa que se puede usar como origen de la consulta, pero también como destino de las operaciones de actualización, eliminación e inserción.Conversely, for regular entity types, EF Core assumes that a database object specified in the ToTable method can be treated as a table, meaning that it can be used as a query source but also targeted by update, delete and insert operations. De hecho, puede especificar el nombre de una vista de base de datos en ToTable y todo debería funcionar bien siempre que la vista está configurada para ser actualizables en la base de datos.In fact, you can specify the name of a database view in ToTable and everything should work fine as long as the view is configured to be updatable on the database.

Nota

ToView supone que el objeto ya existe en la base de datos y que no lo crearán las migraciones.ToView assumes that the object already exists in the database and it won't be created by migrations.

EjemploExample

En el ejemplo siguiente se muestra cómo utilizar los tipos de entidad sin entrada para consultar una vista de base de datos.The following example shows how to use keyless entity types to query a database view.

Sugerencia

Puede ver un ejemplo de este artículo en GitHub.You can view this article's sample on GitHub.

En primer lugar, definimos un modelo sencillo de Blog y Post:First, we define a simple Blog and Post model:

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
}

A continuación, definimos una vista de base de datos simple que nos permitirá consultar el número de entradas vinculadas a cada blog:Next, we define a simple database view that will allow us to query the number of posts associated with each blog:

db.Database.ExecuteSqlRaw(
    @"CREATE VIEW View_BlogPostCounts AS 
        SELECT b.Name, Count(p.PostId) as PostCount 
        FROM Blogs b
        JOIN Posts p on p.BlogId = b.BlogId
        GROUP BY b.Name");

A continuación, definimos una clase para contener el resultado de la vista de base de datos:Next, we define a class to hold the result from the database view:

public class BlogPostsCount
{
    public string BlogName { get; set; }
    public int PostCount { get; set; }
}

A continuación, configuraremos el tipo de entidad sin llave en OnModelCreating con la API de HasNoKey.Next, we configure the keyless entity type in OnModelCreating using the HasNoKey API. Usamos la API de configuración fluida para configurar la asignación para el tipo de entidad sin llave:We use fluent configuration API to configure the mapping for the keyless entity type:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<BlogPostsCount>(eb =>
        {
            eb.HasNoKey();
            eb.ToView("View_BlogPostCounts");
            eb.Property(v => v.BlogName).HasColumnName("Name");
        });
}

A continuación, configuramos el DbContext para incluir el DbSet<T>:Next, we configure the DbContext to include the DbSet<T>:

public DbSet<BlogPostsCount> BlogPostCounts { get; set; }

Por último, podemos consultar la vista de base de datos de la manera estándar:Finally, we can query the database view in the standard way:

var postCounts = db.BlogPostCounts.ToList();

foreach (var postCount in postCounts)
{
    Console.WriteLine($"{postCount.BlogName} has {postCount.PostCount} posts.");
    Console.WriteLine();
}

Sugerencia

Nota también hemos definido una propiedad de consulta de nivel de contexto (DbSet) para que actúe como raíz para las consultas en este tipo.Note we have also defined a context level query property (DbSet) to act as a root for queries against this type.