web-dev-qa-db-fra.com

Seed entité avec propriété possédée

J'essaie d'amorcer une entité utilisateur dans ma base de données. L'entité User a une propriété owend EmailPermissions.

Lorsque j'exécute la commande

les migrations dotnet ef ajoutent Initial;

Je reçois l'erreur

L'entité de départ pour le type d'entité "Utilisateur" ne peut pas être ajoutée car elle a le jeu de navigation "EmailPermissions". Pour amorcer des relations, vous devez ajouter l'amorce d'entité associée à 'EmailPermissions' et spécifier les valeurs de clé étrangère {'UserId'}.

mais comme EmailPermissions est une entité possédée, je ne lui ai pas donné de propriété explicite UserId, ce qui signifie que je ne peux pas l'amorcer séparément dans la base de données.

l'entité

public sealed class User : IdentityUser
{
    public User()
    {
        EmailPermissions = new EmailPermissions();
    }

    /* [..] */

    public string TemporaryEmailBeforeChange { get; set; }
    public bool IsEmailAwaitingUpdate { get; set; }
    public EmailPermissions EmailPermissions { get; set; }
    public ICollection<Registeration> Registerations { get; set; }

    /* [..] */

}

[Owned]
public class EmailPermissions
{
    /* [..] */

    public bool Newsletter { get; set; }
    public bool PromotionalOffers { get; set; }
    public bool PrestationReminders { get; set; }
    public bool PrestationOffers { get; set; }
}

L'appel de semis

private void SeedUser(ModelBuilder builder)
{
    builder.Entity<User>().HasData(
        new User
        {
            Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
            Email = "[email protected]",
            UserName = "[email protected]",
            PasswordHash = "AQAAAAEAACcQAAAAEIytBES+jqKH9jfuY3wzKyduDZruyHMGE6P+ODe1pSKM7BuGjd3AIe6RGRHrXidRsg==",
            SecurityStamp = "WR6VVAGISJYOZQ3W7LGB53EGNXCWB5MS",
            ConcurrencyStamp = "c470e139-5880-4002-8844-ed72ba7b4b80",
            EmailConfirmed = true
        });
}   

Si je supprime l'instanciation de la propriété EmailPermissions du constructeur, j'obtiens l'erreur suivante à la place

L'entité de type 'Utilisateur' partage la table 'AspNetUsers' avec des entités de type 'EmailPermissions', mais il n'y a aucune entité de ce type avec la même valeur de clé qui a été marquée comme 'Ajouté'.

Comment puis-je amorcer un utilisateur via le .HasData méthode quand il a une propriété possédée?

16
Mathieu VIALES

Actuellement, ces informations sont absentes de la documentation (suivies par # 710: documenter comment amorcer les types possédés ). Il est expliqué par l'équipe EF Core (avec un exemple) dans le thread # 12004: Problème d'amorçage contenant le type possédé :

Les types possédés doivent être amorcés avec un appel HasData après l'appel OwnsOne. De plus, étant donné que les types possédés par convention ont une clé primaire générée à l'état masqué et que les données de départ nécessitent la définition de clés, cela nécessite l'utilisation d'un type anonyme et la définition de la clé.

c'est essentiellement ce que le message d'exception vous dit.

En suivant les conseils, vous devez supprimer l'instanciation de la propriété EmailPermissions du constructeur et ajouter un code d'amorçage comme ceci:

builder.Entity<User>().OwnsOne(e => e.EmailPermissions).HasData(
    new
    {
        UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
        // other properties ...
    }
);

Assez ennuyeux et sujet aux erreurs en raison de la nécessité de connaître le nom du PK fantôme et l'utilisation d'un type anonyme. Comme le même membre l'a mentionné

Notez que cela deviendrait plus facile si les navigations étaient prises en charge pour l'amorçage, ce qui est suivi par # 10000: Amorçage des données: ajouter la prise en charge des navigations

22
Ivan Stoev

Merci à Ivan Stoev pour sa réponse. j'ajoute un peu plus de code à facile à imaginer. il s'agit du code de la base de la fonction de données de départ sur l'exemple.

  • Ajouter d'abord les données de l'utilisateur.
  • Après cela, ajoutez les données de l'objet possédé.
  • Les données de l'objet possédé doivent être anonymes car PK le demandera. Ce PK n'apparaîtra pas dans la base de données. Le nom doit être le nom de l'entité + l'ID

Exemple: l'entité XXX => PK sera XXXId

private void SeedUser(ModelBuilder builder)
{
    builder.Entity<User>(b =>
    {
        b.HasData(new User
        {
            Id = "37846734-172e-4149-8cec-6f43d1eb3f60",
            Email = "[email protected]",
            UserName = "[email protected]",
            // more properties of User
        });
        b.OwnsOne(e => e.EmailPermissions).HasData(new 
        {
                UserId = "37846734-172e-4149-8cec-6f43d1eb3f60",
                Newsletter = true,
                PromotionalOffers = true,
                PrestationReminders = true,
                PrestationOffers = true
        });
    });
}
2
Long Pham

Si vous souhaitez éviter d'utiliser un type anonyme pour spécifier les clés de propriété shadow, vous pouvez les déclarer explicitement dans votre classe de modèle et les configurer avec l'API Fluent en tant que clés. De cette façon, vous n'avez pas à deviner les noms de propriété et c'est moins sujet aux erreurs.

Si le nom fourni à la méthode Property correspond au nom d'une propriété existante (propriété shadow ou définie sur la classe d'entité), le code configurera cette propriété existante plutôt que d'introduire une nouvelle propriété shadow. Source

1
Shadow

J'ai eu le même problème, j'ai semé mes données au démarrage. Voici le lien vers le problème de github.

0
Mohsen

Dans mon scénario, je voulais que la propriété de type propriété soit initialisée automatiquement dans la classe parente:

public class User
{
  EmailPermissions _EmailPermissions;
  public EmailPermissions 
  {
    get => _EmailPermissions ??= new EmailPermissions();
    set => _EmailPermissions = value;
  }
}

Quand j'ai essayé d'ajouter des données sur les graines, j'ai eu cette méchante exception.

La solution consistait à passer le User comme type anonyme dans son appel HasData.

0
Shimmy