Compartir a través de


Propiedades reemplazadas y de indizador

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

Las propiedades de indizador son propiedades de tipo de entidad, respaldadas por un indizador en la clase de entidad .NET. Se puede acceder a ellas mediante el indizador en las instancias de clase de .NET. Esto también permite agregar propiedades adicionales al tipo de entidad sin cambiar la clase CLR.

Propiedades reemplazadas de clave externa

Las propiedades reemplazadas se usan normalmente como 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 reemplazada correspondiente.

La propiedad se llamará <navigation property name><principal key property name> (la navegación en la entidad dependiente, que apunta a la entidad principal, se usa para la denominación). Si el nombre de la propiedad de clave principal comienza con 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, se usa en su lugar el nombre del tipo de entidad de seguridad concatenado con el nombre de la propiedad de clave principal o alternativa <principal type name><principal key property name>.

Por ejemplo, la siguiente lista de código dará lugar a que una propiedad reemplazada BlogId se introduzca 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; }
}

Configurar propiedades reemplazadas

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

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 método Property coincide con el nombre de una propiedad existente (una propiedad reemplazada o una definida en la clase de entidad), el código configurará esa propiedad existente en lugar de introducir una nueva propiedad reemplazada.

Acceder a las propiedades reemplazadas

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

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

Se puede hacer referencia a las propiedades reemplazadas en consultas LINQ a través del método estático EF.Property:

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

No se puede tener acceso a las propiedades reemplazadas tras una consulta sin seguimiento, ya que el rastreador de cambios no realiza un seguimiento de las entidades devueltas.

Configurar propiedades de indizador

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

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 método IndexerProperty coincide con el nombre de una propiedad de indizador 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 porque solo se debe tener acceso a las propiedades del indizador a través del indizador.

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

Tipos de entidad de contenedor de propiedades

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

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 cuando se use un tipo de entidad normal, incluso como un tipo de entidad en propiedad. Sin embargo, tienen ciertas limitaciones: