Propriétés cachées et propriétés de l’indexeur

Les propriétés cachées sont des propriétés qui ne sont pas définies dans votre classe d’entité .NET, mais qui sont définies pour ce type d’entité dans le modèle EF Core. La valeur et l’état de ces propriétés sont conservés uniquement dans le suivi des modifications. Les propriétés cachées sont utiles lorsqu’il existe des données dans la base de données qui ne doivent pas être exposées sur les types d’entités mappés.

Les propriétés de l’indexeur sont des propriétés de type d’entité, qui sont sauvegardées par un indexeur dans la classe d’entité .NET. Elles sont accessibles à l’aide de l’indexeur sur les instances de classe .NET. Cela vous permet également d’ajouter des propriétés supplémentaires au type d’entité sans modifier la classe CLR.

Propriétés cachées de clé étrangère

Les propriétés cachées sont souvent utilisées pour les propriétés de clé étrangère, où elles sont ajoutées au modèle par convention lorsqu’aucune propriété de clé étrangère n’a été trouvée par convention ou configurée explicitement. La relation est représentée par les propriétés de navigation, mais dans la base de données, elle est appliquée par une contrainte de clé étrangère et la valeur de la colonne de clé étrangère est stockée dans la propriété cachée correspondante.

La propriété sera nommée <navigation property name><principal key property name> (la navigation sur l’entité dépendante, qui pointe vers l’entité principale, est utilisée pour l’appellation). Si le nom de la propriété de clé principale commence par le nom de la propriété de navigation, le nom sera simplement <principal key property name>. S’il n’existe aucune propriété de navigation sur l’entité dépendante, le nom de type principal concaténé avec le nom de propriété de clé primaire ou alternative est utilisé à sa place <principal type name><principal key property name>.

Par exemple, la liste de codes suivante entraîne l’introduction d’une propriété cachée BlogId dans l’entité 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; }
}

Configuration des propriétés cachées

Vous pouvez utiliser l’API Fluent pour configurer les propriétés cachées. Une fois que vous avez appelé la surcharge de chaîne de Property<TProperty>(String), vous pouvez enchaîner l’un des appels de configuration comme vous le feriez pour d’autres propriétés. Dans l’exemple suivant, étant donné que Blog n’a aucune propriété CLR nommée LastUpdated, une propriété cachée est créée :

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 le nom fourni à la méthode Property correspond au nom d’une propriété existante (une propriété cachée ou une propriété définie sur la classe d’entité), le code configure cette propriété existante plutôt que d’introduire une nouvelle propriété cachée.

Accès aux propriétés cachées

Les valeurs de propriété cachée peuvent être obtenues et modifiées via l’API ChangeTracker :

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

Les propriétés cachées peuvent être référencées dans les requêtes LINQ via la méthode statique EF.Property :

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

Les propriétés cachées ne sont pas accessibles après une requête sans suivi, car les entités retournées ne sont pas suivies par le suivi des modifications.

Configuration des propriétés de l’indexeur

Vous pouvez utiliser l’API Fluent pour configurer les propriétés de l’indexeur. Une fois que vous avez appelé la méthode IndexerProperty, vous pouvez enchaîner l’un des appels de configuration comme vous le feriez pour d’autres propriétés. Dans l’exemple suivant, Blog a un indexeur défini et il sera utilisé pour créer une propriété d’indexeur.

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 le nom fourni à la méthode IndexerProperty correspond au nom d’une propriété d’indexeur existante, le code configure cette propriété existante. Si le type d’entité a une propriété, qui est soutenue par une propriété sur la classe d’entité, une exception est levée, car les propriétés de l’indexeur doivent uniquement être accessibles via l’indexeur.

Les propriétés de l’indexeur peuvent être référencées dans les requêtes LINQ via la méthode statique EF.Property, comme indiqué ci-dessus ou à l’aide de la propriété d’indexeur CLR.

Types d’entités conteneur de propriétés

Les types d’entités qui contiennent uniquement des propriétés d’indexeur sont appelés types d’entités conteneur de propriétés. Ces types d’entités n’ont pas de propriétés cachées, et EF crée des propriétés d’indexeur à la place. Actuellement, seul Dictionary<string, object> est pris en charge comme type d’entité conteneur de propriétés. Il doit être configuré en tant que type d’entité de type partagé avec un nom unique et la propriété DbSet correspondante doit être implémentée à l’aide d’un appel 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");
            });
    }
}

Les types d’entités conteneur de propriétés peuvent être utilisés partout où un type d’entité normal est utilisé, y compris en tant que type d’entité détenu. Toutefois, ils ont certaines limites :