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; }
}
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);