IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Entity Framework Discussion :

EF6, code-first, liens DB


Sujet :

Entity Framework

  1. #1
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2010
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Novembre 2010
    Messages : 185
    Points : 167
    Points
    167
    Par défaut EF6, code-first, liens DB
    Bonjour,
    J'ai pris EF6 en code-first pour un nouveau projet mais je suis confronté à 2 problèmes en rapport aux liens entre les tables.

    J'ai besoin de faire un lien A-(0,1)---(1,1)-B et un lien C-(0,N)---(1,N)-D

    La justification du premier est dû au fait que A peut avoir B mais que B doit avoir A.
    Pour le second, j'avais initialement un lien C-(0,N)---(1,1)-D avec la clé de C dans D mais on m'a dit que parfois (rare mais parfois) D pouvait avoir 2 voir 3 C !!! Je veux donc changer mon modèle avec un lien en -(0,N)----(1,N)-.

    Voici le code fait réduit à l'essentiel :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
        public class A
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 1,1 to B
            /// </summary>
            public Guid BID { get; set; }
            public B B { get; set; }
        }
     
        public class B
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 0,1 to A
            /// </summary>
            public A A { get; set; }
        }
     
        public class C
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 0,N to list of criticities of D, wanted 1,N
            /// </summary>
            public virtual ICollection<D> D { get; set; }
        }
     
        public class D
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 0,N to list of C
            /// </summary>
            public virtual ICollection<C> C { get; set; }
        }
    Niveau SQL, voici ce que ça donne une fois généré :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    CREATE TABLE [dbo].[A](
    	[ID] [uniqueidentifier] NOT NULL,
    	[BID] [uniqueidentifier] NOT NULL,
     CONSTRAINT [PK_dbo.A] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[A] ADD  DEFAULT (newsequentialid()) FOR [ID]
    GO
     
    ALTER TABLE [dbo].[A]  WITH CHECK ADD  CONSTRAINT [FK_dbo.A_dbo.B_ID] FOREIGN KEY([ID])
    REFERENCES [dbo].[B] ([ID])
    GO
     
    ALTER TABLE [dbo].[A] CHECK CONSTRAINT [FK_dbo.A_dbo.B_ID]
    GO
     
    CREATE TABLE [dbo].[B](
    	[ID] [uniqueidentifier] NOT NULL,
     CONSTRAINT [PK_dbo.B] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[B] ADD  DEFAULT (newsequentialid()) FOR [ID]
    GO
     
    CREATE TABLE [dbo].[Defects](
    	[ID] [uniqueidentifier] NOT NULL,
    	[DefectGroupID] [uniqueidentifier] NOT NULL,
    	[Name] [nvarchar](50) NOT NULL,
     CONSTRAINT [PK_dbo.Defects] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[Defects] ADD  DEFAULT (newsequentialid()) FOR [ID]
    GO
     
    ALTER TABLE [dbo].[Defects]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Defects_dbo.DefectGroups_DefectGroupID] FOREIGN KEY([DefectGroupID])
    REFERENCES [dbo].[DefectGroups] ([ID])
    ON DELETE CASCADE
    GO
     
    ALTER TABLE [dbo].[Defects] CHECK CONSTRAINT [FK_dbo.Defects_dbo.DefectGroups_DefectGroupID]
    GO
     
    CREATE TABLE [dbo].[C](
    	[ID] [uniqueidentifier] NOT NULL,
     CONSTRAINT [PK_dbo.C] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[C] ADD  DEFAULT (newsequentialid()) FOR [ID]
    GO
     
    CREATE TABLE [dbo].[D](
    	[ID] [uniqueidentifier] NOT NULL,
     CONSTRAINT [PK_dbo.D] PRIMARY KEY CLUSTERED 
    (
    	[ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[D] ADD  DEFAULT (newsequentialid()) FOR [ID]
    GO
     
    CREATE TABLE [dbo].[CD](
    	[C_ID] [uniqueidentifier] NOT NULL,
    	[D_ID] [uniqueidentifier] NOT NULL,
     CONSTRAINT [PK_dbo.CD] PRIMARY KEY CLUSTERED 
    (
    	[C_ID] ASC,
    	[D_ID] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
     
    ALTER TABLE [dbo].[CD]  WITH CHECK ADD  CONSTRAINT [FK_dbo.CD_dbo.C_ID] FOREIGN KEY([DefectCriticity_ID])
    REFERENCES [dbo].[C] ([ID])
    ON DELETE CASCADE
    GO
     
    ALTER TABLE [dbo].[CD] CHECK CONSTRAINT [FK_dbo.CD_dbo.C_ID]
    GO
     
    ALTER TABLE [dbo].[CD]  WITH CHECK ADD  CONSTRAINT [FK_dbo.CD_dbo.D_ID] FOREIGN KEY([D_ID])
    REFERENCES [dbo].[D] ([ID])
    ON DELETE CASCADE
    GO
     
    ALTER TABLE [dbo].[CD] CHECK CONSTRAINT [FK_dbo.CD_dbo.D_ID]
    GO

    2 problèmes en résultent :
    1. Lien A-(0,1)---(1,1)-B => Un lien est automatiquement généré par EF6 entre A.BID et B.ID !!!
    2. Lien C-(0,N)----(1,N)-D => J'ai un lien C-(0,N)---(0,N)-D alors comment je garanti le 1 ?


    J'espère ne pas m'être trompé dans l'anonymisation de mes objets...

    Merci pour la lecture de mon message... Encore plus pour une réponse... Le nec plus ultra une réponse qui me guide... Utopiquement une réponse avec le code (on peut toujours rêver non)...

  2. #2
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2015
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Novembre 2015
    Messages : 19
    Points : 32
    Points
    32
    Par défaut
    Bonjour,


    Pour le premier le cas je pense que tu as inversé les liens entre les classes A et B.

    Moi j'aurai mis pour "A peut avoir B" le code suivant (Nullable GUID pour la propriété BID) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
     public class A
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            public Guid? BID { get; set; }
            public B B { get; set; }
        }
    Pour "B doit avoir A" le code suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public class B
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            public Guid AID { get; set; }
            public A A { get; set; }
        }
    Pour ton deuxième problème, c'est un peu plus complexe il faut passer par une troisième entité pour établir le lien entre C et D.
    Je ne suis pas sur que le lien C-(0,N)----(1,N)D soit possible (logiquement si D a au moins un lien vers C, le lien inverse va exister). Je pense que tu cherches plutôt à avoir C-(1,N)----(1,N)-D.
    Tu trouveras un exemple en suivant ce lien http://www.entityframeworktutorial.n...n-ef-core.aspx.

  3. #3
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2010
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Novembre 2010
    Messages : 185
    Points : 167
    Points
    167
    Par défaut
    Merci pour ton intervention.

    Concernant la relation A et B, j'avais fait ce code au début avec la même idée mais là il ne génère même pas la DB avec un beau message d'erreur et la DB ne se génère pas :
    System.InvalidOperationException*: 'Unable to determine the principal end of an association between the types 'A' and 'B'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.'
    Je vais donner un peu plus de précision sur mon model :
    A est une table d'adresse pour qu'elle soit toute gérée de la même façon
    B est une table de fabricant (Builder)

    Comme une adresse n'est pas seulement pour un fabricant mais pourrait être pour un client par exemple, une adresse peut exister sans avoir de fabricant, mais au plus elle ne peut avoir qu'un fabricant d'où le besoin d'avoir A-(0,1)---(1,1)-B

    D'après le site dont tu me communique le lien est en permanence ouvert chez moi, c'est ma référence aussi. (bon c'est du EFCore que tu me donnes et je suis sur EF6 mais ce n'est pas grave).

    Voici ce qui est dit, pour faire un lien (1,1)---(0,1)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class A
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 1,1 to B
            /// </summary>
            public virtual B B { get; set; }
        }
     
        public class B
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
     
            /// <summary>
            /// Link 0,1 to A
            /// </summary>
            [Required] // Identique à l'API modelBuilder.Entity<Address>().HasOptional(o => o.Builder).WithRequired(o => o.HumanAddress);
            public virtual A A { get; set; }
        }
    Là, la DB est générée, cela m'a choqué dans un premier temps mais après je l'ai compris donc accepté et validé ce que fait EF6 :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ALTER TABLE [dbo].[Builder]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Builder_dbo.Address_ID] FOREIGN KEY([ID])
    REFERENCES [dbo].[Address] ([ID])

    Mais au premier enregistrement que l'on cherche à mettre dans Builder, j'ai ce message d'erreur :
    'InvalidOperationException*: A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'.'
    Du coup je retire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    car ce n'est plus une identité et ça fonctionne... Mais...

    Maintenant je rentre dans l'architecture mise en place pour ce projet :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
        /// <summary>
        /// Toute entité doit être identifiable
        /// </summary>
        public abstract class Identifiable
        {
            [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
            public Guid ID { get; set; }
        }
     
        public class A : Identifiable
        {
            /// <summary>
            /// Link 1,1 to B
            /// </summary>
            public virtual B B { get; set; }
        }
     
        public class B : Identifiable
        {
            /// <summary>
            /// Link 0,1 to A
            /// </summary>
            [Required] // Identique à l'API modelBuilder.Entity<Address>().HasOptional(o => o.Builder).WithRequired(o => o.HumanAddress);
            public virtual A A { get; set; }
        }
    Comment faire pour retirer la clé primaire avec Fluent API ?

    En ce qui concerne C et D, ce n'est pas parce que j'ai créé une entité C qu'elle est forcément reliée à D par contre si je créé une entité D, elle a forcément un lien vers une entité C. Par contre dans EF6, il n'y a pas nécessaire de s'occuper de la table associative comme dans EFCore, voir ce lien.

    Merci d'avance pour vos idées

  4. #4
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2010
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Novembre 2010
    Messages : 185
    Points : 167
    Points
    167
    Par défaut
    Bon, déjà un problème de résolue, mon lien A-B est résolu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    modelBuilder.Entity<Builder>().Property(o => o.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
    J'ai renommer le champs dans la DB en ajoutant cette ligne afin de comprendre dans la base que c'est un lien vers la table Address
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    modelBuilder.Entity<Builder>().Property(o => o.ID).HasColumnName(typeof(Address).Name + "ID");
    Ca fonctionne, les données entrent !!!

    Il me reste le lien C-D à terminer...

    Quelqu'un pour lui tordre le coup aussi

  5. #5
    Nouveau membre du Club
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2015
    Messages
    19
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Novembre 2015
    Messages : 19
    Points : 32
    Points
    32
    Par défaut
    Je m'excuse j'ai lu trop vite, je pensais que tu étais sur EF core.

    La relation C-(0,N)----(1,N)-D me parait compliquer à mettre en place coté BDD car il faut vérifier que D a bien un lien C dans la table de correspondance avant même de l'insérer. D'ailleurs si un expert base de données passe par là et qui peut nous apporter ses lumières ça m’intéresse aussi .

    Sinon coté EF6, tu peux utiliser DbContext.ValidateEntity (appelé lors du savechanges ) et lever une exception du type DbEntityValidationException si le nombre d'éléments dans ICollection<C> est inférieur à 1.
    Voici la référence (c'est pas le même que le précédent ) https://docs.microsoft.com/fr-fr/ef/...validateentity

    J'espère que ça peut aider en attendant de trouver mieux.

  6. #6
    Membre habitué
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Novembre 2010
    Messages
    185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haut Rhin (Alsace)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Conseil

    Informations forums :
    Inscription : Novembre 2010
    Messages : 185
    Points : 167
    Points
    167
    Par défaut
    Pas de soucis pour EF Core que je connais aussi un petit peu, il y a beaucoup de similitude entre les 2 mais aussi beaucoup de différences

    Alors ça oui, ça m'aide bien car ça sécurise au moins un peu au niveau code. C'est déjà un bon premier point...

    Testé et validé.

    Merci IsmailChamssi, c'est la classe

    Après niveau DB, je ne vois qu'un trigger ou une procédure stockée pour faire le boulot... Je vais voir comment le faire pour que cela reste EF6 qui l'injecte... et pas un script SQL à passer en catimini après la création de la base de données...

    Mais si quelqu'un sait comment faire, n'hésitez pas

Discussions similaires

  1. [Débutant] C# - Asp.net EF6 - Migration automatique Code First - déploiement Azure
    Par Invité dans le forum Microsoft Azure
    Réponses: 3
    Dernier message: 21/07/2017, 17h19
  2. [Débutant] EF6 / Code First DatabaseGeneratedOption.Identity (membre privé ?)
    Par Nadinette dans le forum Entity Framework
    Réponses: 0
    Dernier message: 01/06/2016, 11h10
  3. [Débutant] MVC5 / EF6 Question d'architecture : Les Modèles Code First
    Par Nadinette dans le forum ASP.NET MVC
    Réponses: 7
    Dernier message: 11/03/2016, 20h07
  4. Code Vba lien hypertexte clignotement
    Par bnklm dans le forum Général VBA
    Réponses: 1
    Dernier message: 02/12/2006, 15h10

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo