Comparteix a través de


Propiedades de "shadow" e "indexer"

Las propiedades shadow son propiedades que no están definidas en la clase de entidad de .NET, pero que se definen para ese tipo de entidad en el modelo de EF Core. El valor y el estado de estas propiedades se mantienen exclusivamente en el Seguimiento de Cambios. Las propiedades shadow son útiles cuando hay datos en la base de datos que no deben exponerse en los tipos de entidad asignados.

Las propiedades del indexador son propiedades de tipo de entidad, respaldadas por un indexador en la clase de entidad de .NET. Se puede tener acceso a ellos mediante el indexador en las instancias de clase de .NET. También permite agregar propiedades adicionales al tipo de entidad sin cambiar la clase CLR.

Propiedades sombrías de clave externa

Las propiedades de sombra se usan con más frecuencia para las propiedades de clave externa, donde se agregan al modelo por convención cuando no se ha encontrado ninguna propiedad de clave externa por convención o configurada explícitamente. La relación se representa mediante propiedades de navegación, pero en la base de datos se aplica mediante una restricción de clave externa, y el valor de la columna de clave externa se almacena en la propiedad oculta correspondiente.

La propiedad será nombrada <navigation property name><principal key property name> (el elemento de navegación en la entidad dependiente, que apunta a la entidad principal, se usa para la asignación de nombre). Si el nombre de la propiedad de clave principal comienza por el nombre de la propiedad de navegación, el nombre será simplemente <principal key property name>. Si no hay ninguna propiedad de navegación en la entidad dependiente, el nombre del tipo principal concatenado con el nombre de propiedad de clave principal o alternativa se utiliza en su lugar <principal type name><principal key property name>.

Por ejemplo, la siguiente lista de código provocará la introducción de una propiedad sombra en la entidad Post.

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

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

    // Since there is no CLR property which holds the foreign
    // key for this relationship, a shadow property is created.
    public Blog Blog { get; set; }
}

Configuración de propiedades de sombra

Puede usar Fluent API para configurar las propiedades de sombra. Una vez que haya llamado a la sobrecarga de cadena de Property<TProperty>(String), puede encadenar cualquiera de las llamadas de configuración que haría para otras propiedades. En el ejemplo siguiente, dado que Blog no tiene ninguna propiedad CLR denominada LastUpdated, se crea una propiedad shadow:

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property<DateTime>("LastUpdated");
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Si el nombre proporcionado al Property método coincide con el nombre de una propiedad existente (una propiedad shadow o una definida en la clase de entidad), el código configurará esa propiedad existente en lugar de introducir una nueva propiedad de sombra.

Acceso a las propiedades de sombra

Los valores de propiedad Shadow se pueden obtener y cambiar a través de la ChangeTracker API:

context.Entry(myBlog).Property("LastUpdated").CurrentValue = DateTime.Now;

Se puede hacer referencia a las propiedades de sombra en las consultas LINQ mediante el EF.Property método estático:

var blogs = context.Blogs
    .OrderBy(b => EF.Property<DateTime>(b, "LastUpdated"));

No se puede tener acceso a las propiedades de sombra después de una consulta sin seguimiento, ya que las entidades devueltas no son rastreadas por el rastreador de cambios.

Configuración de propiedades del indexador

Puede usar fluent API para configurar las propiedades del indexador. Una vez que haya llamado al método IndexerProperty, puede encadenar cualquiera de las llamadas de configuración que haría para otras propiedades. En el ejemplo siguiente, Blog tiene definido un indexador y se usará para crear una propiedad de indexador.

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>().IndexerProperty<DateTime>("LastUpdated");
    }
}

public class Blog
{
    private readonly Dictionary<string, object> _data = new Dictionary<string, object>();
    public int BlogId { get; set; }

    public object this[string key]
    {
        get => _data[key];
        set => _data[key] = value;
    }
}

Si el nombre proporcionado al IndexerProperty método coincide con el nombre de una propiedad de indexador existente, el código configurará esa propiedad existente. Si el tipo de entidad tiene una propiedad, respaldada por una propiedad en la clase de entidad, se produce una excepción, ya que solo se debe tener acceso a las propiedades del indexador a través del indexador.

Se puede hacer referencia a las propiedades del indexador en consultas LINQ a través del EF.Property método estático como se muestra anteriormente o mediante la propiedad del indexador CLR.

Tipos de entidad de bolsa de propiedades

Los tipos de entidad que contienen solo propiedades del indexador se conocen como tipos de entidad de contenedor de propiedades. Estos tipos de entidad no tienen propiedades de sombra y EF crea propiedades del indexador en su lugar. Actualmente solo Dictionary<string, object> se admite como un tipo de entidad de bolsa de propiedades. Debe configurarse como un tipo de entidad de tipo compartido con un nombre único y la propiedad correspondiente DbSet debe implementarse mediante una Set llamada.

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}

Los tipos de entidad de contenedor de propiedades se pueden usar siempre que se use un tipo de entidad normal, incluso como un tipo de entidad poseída. Sin embargo, tienen ciertas limitaciones: