This is my first post on stack so sorry for any inconvenience or my bad English.
I'm having a problem with Entity Framework SaveChanges() adding new User record twice into the database table.
I've created a UserFormViewModel for posting data based on user inputs from modal window form.
This is my UserFormViewModel:
public int Id { get; set; }
[Required]
[StringLength(50)]
public string Username { get; set; }
[Required]
[StringLength(20)]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[StringLength(50)]
public string Name { get; set; }
[Required]
[StringLength(50)]
public string Surname { get; set; }
[Required]
[StringLength(255)]
[EmailAddress]
public string Email { get; set; }
public byte UserRoleId { get; set; }
public Address Address { get; set; }
public IEnumerable<UserRole> UserRoles { get; set; }
All inputs from the view are mapped correctly to AddOrEdit action method parameter, so problem starts when saving the data into the database via SaveChanges() method. It's adding record twice into the table.
- I've tried adding
Userseparately and it adds thatUsertwice. - Then I've tried adding the
Addressseparately and it addsAddresstwice. - When saving
UserandAddressin onedbContextinstance or in two separateddbContextinstances,UserandAddressare again added twice.
Also tried database first approach but results are the same.
I'm assuming it has to do with relationships between the tables but can't point to the right cause.
In Address model class there are standard properties for street address:
Id, StreetName, StreetNumber, Zip, City
and two foreign keys:
public int? UserId { get; set; }
public int? ShopId { get; set; }
public virtual User User { get; set; }
public virtual Shop Shop { get; set; }
So User and Shop share Address table for their addresses.
Another way around when User and Shop are set to reference Address table all works fine.
But I want to understand why this is not working and in the end to make Address be able to cascade on User or Shop delete instead of using LINQ to remove Address.
Here is the action method AddOrEdit for a User and its Address:
private readonly FurnitureStoreDbContext _context;
public UsersController()
{
_context = new FurnitureStoreDbContext();
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
}
[HttpPost]
public ActionResult AddOrEdit(UserFormViewModel userForm)
{
if (userForm.Id == 0)
{
var newUser = new User
{
Username = userForm.Username,
Password = userForm.Password,
Name = userForm.Name,
Surname = userForm.Surname,
Email = userForm.Email,
UserRoleId = userForm.UserRoleId
};
_context.tblUsers.Add(newUser);
_context.SaveChanges();
var newAddress = new Address
{
StreetName = userForm.Address.StreetName,
StreetNumber = userForm.Address.StreetNumber,
ZipCode = userForm.Address.ZipCode,
City = userForm.Address.City,
UserId = newUser.Id
};
_context.tblStreetAddresses.Add(newAddress);
_context.SaveChanges();
return Json(new { success = true, message = "Saved" }, JsonRequestBehavior.AllowGet);
}
else
{
var updatedUser = new User
{
Username = userForm.Username,
Password = userForm.Password,
Name = userForm.Name,
Surname = userForm.Surname,
Email = userForm.Email,
UserRoleId = userForm.UserRoleId
};
var updatedAddress = userForm.Address;
_context.Entry(updatedUser).State = EntityState.Modified;
_context.SaveChanges();
_context.Entry(updatedAddress).State = EntityState.Modified;
_context.SaveChanges();
return Json(new { success = true, message = "Updated" }, JsonRequestBehavior.AllowGet);
}
}
Here is a debug log for adding only User - without adding Address.
Violation of UNIQUE KEY constraint 'UQ__Users__536C85E479927847' exception is happening because I've set Username field to be unique and context is trying to add User record twice.
Step into: Stepping over property 'FurnitureStore.Models.User.get_Addresses'. To step into properties or operators, go to Tools->Options->Debugging and uncheck 'Step over properties and operators (Managed only)'.
Step into: Stepping over property 'FurnitureStore.Models.User.get_Addresses'. To step into properties or operators, go to Tools->Options->Debugging and uncheck 'Step over properties and operators (Managed only)'.
Opened connection at 3/27/2021 8:55:09 PM +01:00
Opened connection at 3/27/2021 8:55:09 PM +01:00
Started transaction at 3/27/2021 8:55:09 PM +01:00
Started transaction at 3/27/2021 8:55:09 PM +01:00
INSERT [dbo].[Users]([Username], [Password], [Name], [Surname], [Email], [UserRoleId])
VALUES (@0, @1, @2, @3, @4, @5)
SELECT [Id]
FROM [dbo].[Users]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()INSERT [dbo].[Users]([Username], [Password], [Name], [Surname], [Email], [UserRoleId])
VALUES (@0, @1, @2, @3, @4, @5)
SELECT [Id]
FROM [dbo].[Users]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'Username 1' (Type = String, Size = 50)
-- @0: 'Username 1' (Type = String, Size = 50)
-- @1: 'q1w2e3r4' (Type = String, Size = 20)
-- @2: 'Name 1' (Type = String, Size = 50)
-- @3: 'Surname 1' (Type = String, Size = 50)
-- @4: '[email protected]' (Type = String, Size = 255)
-- @5: '2' (Type = Byte, Size = 1)
-- Executing at 3/27/2021 8:55:10 PM +01:00
-- @1: 'q1w2e3r4' (Type = String, Size = 20)
-- @2: 'Name 1' (Type = String, Size = 50)
-- @3: 'Surname 1' (Type = String, Size = 50)
-- @4: '[email protected]' (Type = String, Size = 255)
-- Completed in 7 ms with result: SqlDataReader
-- @5: '2' (Type = Byte, Size = 1)
-- Executing at 3/27/2021 8:55:10 PM +01:00
Committed transaction at 3/27/2021 8:55:10 PM +01:00
-- Failed in 26 ms with error: Violation of UNIQUE KEY constraint 'UQ__Users__536C85E479927847'. Cannot insert duplicate key in object 'dbo.Users'. The duplicate key value is (Username 1).
The statement has been terminated.
Closed connection at 3/27/2021 8:55:10 PM +01:00
Closed connection at 3/27/2021 8:55:10 PM +01:00
Exception thrown: 'System.Data.Entity.Infrastructure.DbUpdateException' in EntityFramework.dll
An exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll but was not handled in user code
An error occurred while updating the entries. See the inner exception for details.
The thread 0x42cc has exited with code 0 (0x0).
Any help is appreciated.
Thanks :)
updatedUserdoesn't have anIdvalue set (while the database table clearly has the field). That should cause trouble updating, but no a re-insert, the way you coded it.