Search code examples
c#visual-studio-2017entity-framework-6sqlexception

EF6 SqlException: Violation of PRIMARY KEY constraint ... The duplicate key value is (0)


I know SQL, but I am very new to ORM and play in a sandbox using EntityFramework 6.2.0 in VS 2017. I thought that the framework should enable me to code more or less like I would use POCO objects, without having to deal with primary/foreign keys. And the changes are saved in the database as soon as I call in below code context.SaveChanges(); What do I do not (yet) understand with EF6 and its documentation?

I created two tables Person and WorkingHours depending on each other with a foreign key and a 1:n relationship.

  CREATE TABLE [dbo].[Person](
    [FirstName] [nvarchar](100) NOT NULL,
    [LastName] [nvarchar](100) NOT NULL,
    [BirthDate] [date] NULL,
    [Id] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_Person] 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]

CREATE TABLE [dbo].[WorkingHours](
  [Id] [int] NOT NULL,
  [PersonId] [int] NOT NULL,
  [Date] [date] NOT NULL,
  [Start] [time](7) NOT NULL,
  [Hours] [real] NOT NULL,
 CONSTRAINT [PK_WorkingHours] 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]

I created the model from the database.

I try to create two Person entries, each with a WorkingHours entry and saving it at once to the database. Here is the code:

     static void Main(string[] args)
    {
        using(var context = new EntityFrameworkTrialEntities2())
        {
             // Inserting entries
            var person1 = new Person() { FirstName = "Al", LastName = "Curie", BirthDate = new DateTime(1867, 11, 7) };
            var person2 = new Person() { FirstName = "Al", LastName = "Capone", BirthDate = new DateTime(1899, 1, 7) };
            context.Person.Add(person1);
            context.Person.Add(person2);

            var workingHour1 = new WorkingHour() { Date = new DateTime(1887, 8, 18), Start = new TimeSpan(6, 35, 33), Hours = 4.3F };
            var workingHour2 = new WorkingHour() { Date = new DateTime(1919, 9, 19), Start = new TimeSpan(10, 23, 56), Hours = 3.2F };
            person2.WorkingHours.Add(workingHour1); 
            person1.WorkingHours.Add(workingHour2); 

            context.WorkingHours.Add(workingHour1);
            context.WorkingHours.Add(workingHour2);
            context.SaveChanges(); // <-- place of exeception !!!!!!!!!!!!!!!!!!!!

            // (next step as EF6 trial: here the previously intentionally mixed relationships between person1/2 and workingHour2/1 shall be corrected)

But at calling context.SaveChanges(); I get this exception:

SqlException: Violation of PRIMARY KEY constraint 'PK_WorkingHours'. Cannot insert duplicate key in object 'dbo.WorkingHours'. The duplicate key value is (0). The statement has been terminated.**

I tried different manners with more context.SaveChanges() and Attach() and other orders, but I fail always with above exception. context.Configuration.ProxyCreationEnabled is set to true as default.

Here the model classes:

public partial class Person
{
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
    public Person()
    {
        this.WorkingHours = new HashSet<WorkingHour>();
    }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Nullable<System.DateTime> BirthDate { get; set; }
    public int Id { get; set; }

    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    public virtual ICollection<WorkingHour> WorkingHours { get; set; }
}


public partial class WorkingHour
{
    public int Id { get; set; }
    public int PersonId { get; set; }
    public System.DateTime Date { get; set; }
    public System.TimeSpan Start { get; set; }
    public float Hours { get; set; }

    public virtual Person Person { get; set; }
}

Solution

  • First problem is Primary Key of the WorkingHour is not the Identity Key that's why it is always being set to 0 (default value of int) for each WorkingHour and hence it is being duplicated.

    So make Id column of WorkingHour [Id] [int] IDENTITY(1,1) NOT NULL

    Second problem is in the following lines:

    person2.WorkingHours.Add(workingHour1); // adding with person
    person1.WorkingHours.Add(workingHour2); // adding with person
    
    context.WorkingHours.Add(workingHour1); // adding individually
    context.WorkingHours.Add(workingHour2); // adding individually
    

    Here you are adding a WorkingHour twice in the context. One with Person entity and other is individually. So remove the second sets and just use the first set as follows:

    person2.WorkingHours.Add(workingHour1); 
    person1.WorkingHours.Add(workingHour2);