Need a help to update the model
I have a model called ClubApplicationUser which is bridge between the Club and ApplicationUser which is extended model of Identity User model:
public class ClubApplicationUser
{
public Guid ClubID { get; set; }
public Club Club { get; set; }
public string Id { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public DateTime DateCreated { get; set; }
public string CreatedBy { get; set; }
public DateTime LastDateModified { get; set; }
public string LastModifiedBy { get; set; }
public DateTime? DateDeleted { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted { get; set; }
[Timestamp]
public byte RowVersion { get; set; }
[ForeignKey("CreatedBy")]
public ApplicationUser ClubApplicationCreatedUser { get; set; }
[ForeignKey("LastModifiedBy")]
public ApplicationUser ClubApplicationLastModifiedUser { get; set; }
}
and in the ApplicationDBContext - OnModelCreating we defined the relationships:
builder.Entity<ClubApplicationUser>()
.HasKey(bc => new { bc.ClubID, bc.Id });
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.Club)
.WithMany(b => b.ClubApplicationUsers)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.ApplicationUser)
.WithMany(c => c.ClubApplicationUsers)
.HasForeignKey(bc => bc.Id);
We have an issue where we could NOT update this and we have the error:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key and so cannot be modified or
marked as modified. To change the principal of an existing entity with
an identifying foreign key first delete the dependent and invoke
'SaveChanges' then associate the dependent with the new principal.
Here's the AssignClub.cs:
public class AssignClubUserModel : ClubNamePageModel
{
private readonly AthlosifyWebArchery.Data.ApplicationDbContext _context;
public AssignClubUserModel(AthlosifyWebArchery.Data.ApplicationDbContext context)
{
_context = context;
}
public class AssignClubUserViewModel<ApplicationUser>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedClubID { get; set; }
public byte RowVersion { get; set; }
}
[BindProperty]
public AssignClubUserViewModel<ApplicationUser> AssignClubUser { get; set; }
public SelectList ClubNameSL { get; set; }
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID,
RowVersion = t.RowVersion
}).SingleAsync();
AssignClubUser = user;
// Use strongly typed data rather than ViewData.
ClubNameSL = new SelectList(_context.Club, "ClubID", "Name");
//PopulateClubsDropDownList(_context);
return Page();
}
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!ModelState.IsValid)
{
return Page();
}
// 1st approach:
// Modify the bridge model directly
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
if (clubApplicationUserToUpdate == null)
{
return await HandleDeletedUser();
}
_context.Entry(clubApplicationUserToUpdate)
.Property("RowVersion").OriginalValue = AssignClubUser.RowVersion;
_context.Entry(clubApplicationUserToUpdate)
.Property("ClubID").OriginalValue = AssignClubUser.SelectedClubID;
await _context.SaveChangesAsync();
// 2nd approach:
// Soft -Delete and Add
// Did the soft-deleting and managed to add a new one BUT then die the roll back (adding the old one)
// Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
// Cannot insert duplicate key in object
// Due to duplicate key
/*var clubApplicatonUserToRemove = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
ClubApplicationUser clubApplicatonUserToAdd = new ClubApplicationUser();
clubApplicatonUserToAdd.Id = id.ToString();
clubApplicatonUserToAdd.ClubID = AssignClubUser.SelectedClubID;
//_context.Entry(clubApplicatonUserToRemove)
// .Property("RowVersion").OriginalValue = User.RowVersion;
if (clubApplicatonUserToRemove != null)
{
_context.ClubApplicationUser.Remove(clubApplicatonUserToRemove);
await _context.SaveChangesAsync();
_context.ClubApplicationUser.Add(clubApplicatonUserToAdd);
await _context.SaveChangesAsync();
}*/
return Page();
}
private async Task<IActionResult> HandleDeletedUser()
{
//ClubA deletedClubApplicationUser = new ApplicationUser();
//ModelState.AddModelError(string.Empty,
// "Unable to save. The user was deleted by another user.");
//ClubNameSL = new SelectList(_context.Roles, "Id", "Name", User.UserRoles.ElementAt(0).RoleId);
return Page();
}
private async Task setDbErrorMessage(ApplicationUser dbValues,
ApplicationUser clientValues, ApplicationDbContext context)
{
if (dbValues.FirstName != clientValues.FirstName)
{
ModelState.AddModelError("User.FirstName",
$"Current value: {dbValues.FirstName}");
}
if (dbValues.LastName != clientValues.LastName)
{
ModelState.AddModelError("User.LastName",
$"Current value: {dbValues.LastName}");
}
if (dbValues.Email != clientValues.Email)
{
ModelState.AddModelError("User.Email",
$"Current value: {dbValues.Email}");
}
ModelState.AddModelError(string.Empty,
"The record you attempted to edit "
+ "was modified by another user after you. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again.");
}
}
... and AssignClub.cshtml:
@page
@model AthlosifyWebArchery.Pages.Administrators.Users.AssignClubUserModel
@{
ViewData["Title"] = "Assign Club";
}
<h2>Assign Club</h2>
<h4>User</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AssignClubUser.FirstName" class="control-label">
</label>
<input asp-for="AssignClubUser.FirstName" disabled class="form-
control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.LastName" class="control-label">
</label>
<input asp-for="AssignClubUser.LastName" disabled class="form-control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.UserName" class="control-label">
</label>
<input asp-for="AssignClubUser.UserName" disabled class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Club</label>
<select asp-for="AssignClubUser.SelectedClubID" class="form-control"
asp-items="@Model.ClubNameSL">
<option value="">-- Select Club --</option>
</select>
<span asp-validation-for="AssignClubUser.SelectedClubID" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Environment:
.Net Core 2.2
Razor Pages
UPDATE - 1:
If we update this directly on the database by doing:
UPDATE [ClubApplicationUser]
SET ClubID = '85715C34-AFC6-4498-DA7F-08D66CAE7A01'
WHERE Id = 'ecbd27b4-03bc-4b99-82b3-76d9aa5bc7fc'
We could update this no problem. So it looks like it's contraint within the .Net core model.
.net-core
add a comment |
I have a model called ClubApplicationUser which is bridge between the Club and ApplicationUser which is extended model of Identity User model:
public class ClubApplicationUser
{
public Guid ClubID { get; set; }
public Club Club { get; set; }
public string Id { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public DateTime DateCreated { get; set; }
public string CreatedBy { get; set; }
public DateTime LastDateModified { get; set; }
public string LastModifiedBy { get; set; }
public DateTime? DateDeleted { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted { get; set; }
[Timestamp]
public byte RowVersion { get; set; }
[ForeignKey("CreatedBy")]
public ApplicationUser ClubApplicationCreatedUser { get; set; }
[ForeignKey("LastModifiedBy")]
public ApplicationUser ClubApplicationLastModifiedUser { get; set; }
}
and in the ApplicationDBContext - OnModelCreating we defined the relationships:
builder.Entity<ClubApplicationUser>()
.HasKey(bc => new { bc.ClubID, bc.Id });
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.Club)
.WithMany(b => b.ClubApplicationUsers)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.ApplicationUser)
.WithMany(c => c.ClubApplicationUsers)
.HasForeignKey(bc => bc.Id);
We have an issue where we could NOT update this and we have the error:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key and so cannot be modified or
marked as modified. To change the principal of an existing entity with
an identifying foreign key first delete the dependent and invoke
'SaveChanges' then associate the dependent with the new principal.
Here's the AssignClub.cs:
public class AssignClubUserModel : ClubNamePageModel
{
private readonly AthlosifyWebArchery.Data.ApplicationDbContext _context;
public AssignClubUserModel(AthlosifyWebArchery.Data.ApplicationDbContext context)
{
_context = context;
}
public class AssignClubUserViewModel<ApplicationUser>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedClubID { get; set; }
public byte RowVersion { get; set; }
}
[BindProperty]
public AssignClubUserViewModel<ApplicationUser> AssignClubUser { get; set; }
public SelectList ClubNameSL { get; set; }
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID,
RowVersion = t.RowVersion
}).SingleAsync();
AssignClubUser = user;
// Use strongly typed data rather than ViewData.
ClubNameSL = new SelectList(_context.Club, "ClubID", "Name");
//PopulateClubsDropDownList(_context);
return Page();
}
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!ModelState.IsValid)
{
return Page();
}
// 1st approach:
// Modify the bridge model directly
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
if (clubApplicationUserToUpdate == null)
{
return await HandleDeletedUser();
}
_context.Entry(clubApplicationUserToUpdate)
.Property("RowVersion").OriginalValue = AssignClubUser.RowVersion;
_context.Entry(clubApplicationUserToUpdate)
.Property("ClubID").OriginalValue = AssignClubUser.SelectedClubID;
await _context.SaveChangesAsync();
// 2nd approach:
// Soft -Delete and Add
// Did the soft-deleting and managed to add a new one BUT then die the roll back (adding the old one)
// Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
// Cannot insert duplicate key in object
// Due to duplicate key
/*var clubApplicatonUserToRemove = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
ClubApplicationUser clubApplicatonUserToAdd = new ClubApplicationUser();
clubApplicatonUserToAdd.Id = id.ToString();
clubApplicatonUserToAdd.ClubID = AssignClubUser.SelectedClubID;
//_context.Entry(clubApplicatonUserToRemove)
// .Property("RowVersion").OriginalValue = User.RowVersion;
if (clubApplicatonUserToRemove != null)
{
_context.ClubApplicationUser.Remove(clubApplicatonUserToRemove);
await _context.SaveChangesAsync();
_context.ClubApplicationUser.Add(clubApplicatonUserToAdd);
await _context.SaveChangesAsync();
}*/
return Page();
}
private async Task<IActionResult> HandleDeletedUser()
{
//ClubA deletedClubApplicationUser = new ApplicationUser();
//ModelState.AddModelError(string.Empty,
// "Unable to save. The user was deleted by another user.");
//ClubNameSL = new SelectList(_context.Roles, "Id", "Name", User.UserRoles.ElementAt(0).RoleId);
return Page();
}
private async Task setDbErrorMessage(ApplicationUser dbValues,
ApplicationUser clientValues, ApplicationDbContext context)
{
if (dbValues.FirstName != clientValues.FirstName)
{
ModelState.AddModelError("User.FirstName",
$"Current value: {dbValues.FirstName}");
}
if (dbValues.LastName != clientValues.LastName)
{
ModelState.AddModelError("User.LastName",
$"Current value: {dbValues.LastName}");
}
if (dbValues.Email != clientValues.Email)
{
ModelState.AddModelError("User.Email",
$"Current value: {dbValues.Email}");
}
ModelState.AddModelError(string.Empty,
"The record you attempted to edit "
+ "was modified by another user after you. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again.");
}
}
... and AssignClub.cshtml:
@page
@model AthlosifyWebArchery.Pages.Administrators.Users.AssignClubUserModel
@{
ViewData["Title"] = "Assign Club";
}
<h2>Assign Club</h2>
<h4>User</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AssignClubUser.FirstName" class="control-label">
</label>
<input asp-for="AssignClubUser.FirstName" disabled class="form-
control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.LastName" class="control-label">
</label>
<input asp-for="AssignClubUser.LastName" disabled class="form-control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.UserName" class="control-label">
</label>
<input asp-for="AssignClubUser.UserName" disabled class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Club</label>
<select asp-for="AssignClubUser.SelectedClubID" class="form-control"
asp-items="@Model.ClubNameSL">
<option value="">-- Select Club --</option>
</select>
<span asp-validation-for="AssignClubUser.SelectedClubID" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Environment:
.Net Core 2.2
Razor Pages
UPDATE - 1:
If we update this directly on the database by doing:
UPDATE [ClubApplicationUser]
SET ClubID = '85715C34-AFC6-4498-DA7F-08D66CAE7A01'
WHERE Id = 'ecbd27b4-03bc-4b99-82b3-76d9aa5bc7fc'
We could update this no problem. So it looks like it's contraint within the .Net core model.
.net-core
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17
add a comment |
I have a model called ClubApplicationUser which is bridge between the Club and ApplicationUser which is extended model of Identity User model:
public class ClubApplicationUser
{
public Guid ClubID { get; set; }
public Club Club { get; set; }
public string Id { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public DateTime DateCreated { get; set; }
public string CreatedBy { get; set; }
public DateTime LastDateModified { get; set; }
public string LastModifiedBy { get; set; }
public DateTime? DateDeleted { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted { get; set; }
[Timestamp]
public byte RowVersion { get; set; }
[ForeignKey("CreatedBy")]
public ApplicationUser ClubApplicationCreatedUser { get; set; }
[ForeignKey("LastModifiedBy")]
public ApplicationUser ClubApplicationLastModifiedUser { get; set; }
}
and in the ApplicationDBContext - OnModelCreating we defined the relationships:
builder.Entity<ClubApplicationUser>()
.HasKey(bc => new { bc.ClubID, bc.Id });
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.Club)
.WithMany(b => b.ClubApplicationUsers)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.ApplicationUser)
.WithMany(c => c.ClubApplicationUsers)
.HasForeignKey(bc => bc.Id);
We have an issue where we could NOT update this and we have the error:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key and so cannot be modified or
marked as modified. To change the principal of an existing entity with
an identifying foreign key first delete the dependent and invoke
'SaveChanges' then associate the dependent with the new principal.
Here's the AssignClub.cs:
public class AssignClubUserModel : ClubNamePageModel
{
private readonly AthlosifyWebArchery.Data.ApplicationDbContext _context;
public AssignClubUserModel(AthlosifyWebArchery.Data.ApplicationDbContext context)
{
_context = context;
}
public class AssignClubUserViewModel<ApplicationUser>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedClubID { get; set; }
public byte RowVersion { get; set; }
}
[BindProperty]
public AssignClubUserViewModel<ApplicationUser> AssignClubUser { get; set; }
public SelectList ClubNameSL { get; set; }
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID,
RowVersion = t.RowVersion
}).SingleAsync();
AssignClubUser = user;
// Use strongly typed data rather than ViewData.
ClubNameSL = new SelectList(_context.Club, "ClubID", "Name");
//PopulateClubsDropDownList(_context);
return Page();
}
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!ModelState.IsValid)
{
return Page();
}
// 1st approach:
// Modify the bridge model directly
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
if (clubApplicationUserToUpdate == null)
{
return await HandleDeletedUser();
}
_context.Entry(clubApplicationUserToUpdate)
.Property("RowVersion").OriginalValue = AssignClubUser.RowVersion;
_context.Entry(clubApplicationUserToUpdate)
.Property("ClubID").OriginalValue = AssignClubUser.SelectedClubID;
await _context.SaveChangesAsync();
// 2nd approach:
// Soft -Delete and Add
// Did the soft-deleting and managed to add a new one BUT then die the roll back (adding the old one)
// Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
// Cannot insert duplicate key in object
// Due to duplicate key
/*var clubApplicatonUserToRemove = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
ClubApplicationUser clubApplicatonUserToAdd = new ClubApplicationUser();
clubApplicatonUserToAdd.Id = id.ToString();
clubApplicatonUserToAdd.ClubID = AssignClubUser.SelectedClubID;
//_context.Entry(clubApplicatonUserToRemove)
// .Property("RowVersion").OriginalValue = User.RowVersion;
if (clubApplicatonUserToRemove != null)
{
_context.ClubApplicationUser.Remove(clubApplicatonUserToRemove);
await _context.SaveChangesAsync();
_context.ClubApplicationUser.Add(clubApplicatonUserToAdd);
await _context.SaveChangesAsync();
}*/
return Page();
}
private async Task<IActionResult> HandleDeletedUser()
{
//ClubA deletedClubApplicationUser = new ApplicationUser();
//ModelState.AddModelError(string.Empty,
// "Unable to save. The user was deleted by another user.");
//ClubNameSL = new SelectList(_context.Roles, "Id", "Name", User.UserRoles.ElementAt(0).RoleId);
return Page();
}
private async Task setDbErrorMessage(ApplicationUser dbValues,
ApplicationUser clientValues, ApplicationDbContext context)
{
if (dbValues.FirstName != clientValues.FirstName)
{
ModelState.AddModelError("User.FirstName",
$"Current value: {dbValues.FirstName}");
}
if (dbValues.LastName != clientValues.LastName)
{
ModelState.AddModelError("User.LastName",
$"Current value: {dbValues.LastName}");
}
if (dbValues.Email != clientValues.Email)
{
ModelState.AddModelError("User.Email",
$"Current value: {dbValues.Email}");
}
ModelState.AddModelError(string.Empty,
"The record you attempted to edit "
+ "was modified by another user after you. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again.");
}
}
... and AssignClub.cshtml:
@page
@model AthlosifyWebArchery.Pages.Administrators.Users.AssignClubUserModel
@{
ViewData["Title"] = "Assign Club";
}
<h2>Assign Club</h2>
<h4>User</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AssignClubUser.FirstName" class="control-label">
</label>
<input asp-for="AssignClubUser.FirstName" disabled class="form-
control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.LastName" class="control-label">
</label>
<input asp-for="AssignClubUser.LastName" disabled class="form-control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.UserName" class="control-label">
</label>
<input asp-for="AssignClubUser.UserName" disabled class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Club</label>
<select asp-for="AssignClubUser.SelectedClubID" class="form-control"
asp-items="@Model.ClubNameSL">
<option value="">-- Select Club --</option>
</select>
<span asp-validation-for="AssignClubUser.SelectedClubID" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Environment:
.Net Core 2.2
Razor Pages
UPDATE - 1:
If we update this directly on the database by doing:
UPDATE [ClubApplicationUser]
SET ClubID = '85715C34-AFC6-4498-DA7F-08D66CAE7A01'
WHERE Id = 'ecbd27b4-03bc-4b99-82b3-76d9aa5bc7fc'
We could update this no problem. So it looks like it's contraint within the .Net core model.
.net-core
I have a model called ClubApplicationUser which is bridge between the Club and ApplicationUser which is extended model of Identity User model:
public class ClubApplicationUser
{
public Guid ClubID { get; set; }
public Club Club { get; set; }
public string Id { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public DateTime DateCreated { get; set; }
public string CreatedBy { get; set; }
public DateTime LastDateModified { get; set; }
public string LastModifiedBy { get; set; }
public DateTime? DateDeleted { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted { get; set; }
[Timestamp]
public byte RowVersion { get; set; }
[ForeignKey("CreatedBy")]
public ApplicationUser ClubApplicationCreatedUser { get; set; }
[ForeignKey("LastModifiedBy")]
public ApplicationUser ClubApplicationLastModifiedUser { get; set; }
}
and in the ApplicationDBContext - OnModelCreating we defined the relationships:
builder.Entity<ClubApplicationUser>()
.HasKey(bc => new { bc.ClubID, bc.Id });
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.Club)
.WithMany(b => b.ClubApplicationUsers)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubApplicationUser>()
.HasOne(bc => bc.ApplicationUser)
.WithMany(c => c.ClubApplicationUsers)
.HasForeignKey(bc => bc.Id);
We have an issue where we could NOT update this and we have the error:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key and so cannot be modified or
marked as modified. To change the principal of an existing entity with
an identifying foreign key first delete the dependent and invoke
'SaveChanges' then associate the dependent with the new principal.
Here's the AssignClub.cs:
public class AssignClubUserModel : ClubNamePageModel
{
private readonly AthlosifyWebArchery.Data.ApplicationDbContext _context;
public AssignClubUserModel(AthlosifyWebArchery.Data.ApplicationDbContext context)
{
_context = context;
}
public class AssignClubUserViewModel<ApplicationUser>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
public Guid SelectedClubID { get; set; }
public byte RowVersion { get; set; }
}
[BindProperty]
public AssignClubUserViewModel<ApplicationUser> AssignClubUser { get; set; }
public SelectList ClubNameSL { get; set; }
public async Task<IActionResult> OnGetAsync(Guid? id)
{
if (id == null)
return NotFound();
var user = await _context.Users
.Include(u => u.ClubApplicationUsers)
.Where(t => t.Id == id.ToString())
.Select(t => new AssignClubUserViewModel<ApplicationUser>
{
FirstName = t.FirstName,
LastName = t.LastName,
UserName = t.UserName,
SelectedClubID = t.ClubApplicationUsers.ElementAt(0).ClubID,
RowVersion = t.RowVersion
}).SingleAsync();
AssignClubUser = user;
// Use strongly typed data rather than ViewData.
ClubNameSL = new SelectList(_context.Club, "ClubID", "Name");
//PopulateClubsDropDownList(_context);
return Page();
}
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!ModelState.IsValid)
{
return Page();
}
// 1st approach:
// Modify the bridge model directly
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
if (clubApplicationUserToUpdate == null)
{
return await HandleDeletedUser();
}
_context.Entry(clubApplicationUserToUpdate)
.Property("RowVersion").OriginalValue = AssignClubUser.RowVersion;
_context.Entry(clubApplicationUserToUpdate)
.Property("ClubID").OriginalValue = AssignClubUser.SelectedClubID;
await _context.SaveChangesAsync();
// 2nd approach:
// Soft -Delete and Add
// Did the soft-deleting and managed to add a new one BUT then die the roll back (adding the old one)
// Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
// Cannot insert duplicate key in object
// Due to duplicate key
/*var clubApplicatonUserToRemove = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
ClubApplicationUser clubApplicatonUserToAdd = new ClubApplicationUser();
clubApplicatonUserToAdd.Id = id.ToString();
clubApplicatonUserToAdd.ClubID = AssignClubUser.SelectedClubID;
//_context.Entry(clubApplicatonUserToRemove)
// .Property("RowVersion").OriginalValue = User.RowVersion;
if (clubApplicatonUserToRemove != null)
{
_context.ClubApplicationUser.Remove(clubApplicatonUserToRemove);
await _context.SaveChangesAsync();
_context.ClubApplicationUser.Add(clubApplicatonUserToAdd);
await _context.SaveChangesAsync();
}*/
return Page();
}
private async Task<IActionResult> HandleDeletedUser()
{
//ClubA deletedClubApplicationUser = new ApplicationUser();
//ModelState.AddModelError(string.Empty,
// "Unable to save. The user was deleted by another user.");
//ClubNameSL = new SelectList(_context.Roles, "Id", "Name", User.UserRoles.ElementAt(0).RoleId);
return Page();
}
private async Task setDbErrorMessage(ApplicationUser dbValues,
ApplicationUser clientValues, ApplicationDbContext context)
{
if (dbValues.FirstName != clientValues.FirstName)
{
ModelState.AddModelError("User.FirstName",
$"Current value: {dbValues.FirstName}");
}
if (dbValues.LastName != clientValues.LastName)
{
ModelState.AddModelError("User.LastName",
$"Current value: {dbValues.LastName}");
}
if (dbValues.Email != clientValues.Email)
{
ModelState.AddModelError("User.Email",
$"Current value: {dbValues.Email}");
}
ModelState.AddModelError(string.Empty,
"The record you attempted to edit "
+ "was modified by another user after you. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again.");
}
}
... and AssignClub.cshtml:
@page
@model AthlosifyWebArchery.Pages.Administrators.Users.AssignClubUserModel
@{
ViewData["Title"] = "Assign Club";
}
<h2>Assign Club</h2>
<h4>User</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="AssignClubUser.FirstName" class="control-label">
</label>
<input asp-for="AssignClubUser.FirstName" disabled class="form-
control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.LastName" class="control-label">
</label>
<input asp-for="AssignClubUser.LastName" disabled class="form-control" />
</div>
<div class="form-group">
<label asp-for="AssignClubUser.UserName" class="control-label">
</label>
<input asp-for="AssignClubUser.UserName" disabled class="form-control" />
</div>
<div class="form-group">
<label class="control-label">Club</label>
<select asp-for="AssignClubUser.SelectedClubID" class="form-control"
asp-items="@Model.ClubNameSL">
<option value="">-- Select Club --</option>
</select>
<span asp-validation-for="AssignClubUser.SelectedClubID" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>
<div>
<a asp-page="./Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Environment:
.Net Core 2.2
Razor Pages
UPDATE - 1:
If we update this directly on the database by doing:
UPDATE [ClubApplicationUser]
SET ClubID = '85715C34-AFC6-4498-DA7F-08D66CAE7A01'
WHERE Id = 'ecbd27b4-03bc-4b99-82b3-76d9aa5bc7fc'
We could update this no problem. So it looks like it's contraint within the .Net core model.
.net-core
.net-core
edited Jan 1 at 23:42
dcpartners
asked Dec 31 '18 at 11:28
dcpartnersdcpartners
1,56783346
1,56783346
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17
add a comment |
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17
add a comment |
4 Answers
4
active
oldest
votes
I think the best solution is that you need to Delete and Insert instead of updating although given your ClubApplicationUser actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete.
If you think of it in the logic of your domain, I don't think a user generally changes being a member of one club to another, they leave (delete) one club and join (insert) another.
Although I could come up with another domain where it does make sense to update so I don't think this is a good generic argument.
The following code shows a cutdown version of your problem. You can see that the Tests allow an Insert and Delete but fail with an Update
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Clubs { get; set; }
}
public class ClubUser
{
public int ClubID { get; set; }
public Club Club { get; set; }
public int Id { get; set; }
public User User { get; set; }
public string Extra { get; set; }
}
public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubUser> ClubUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
}
protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
{
builder.Entity<Club>()
.HasKey(c => c.Id );
builder.Entity<User>()
.HasKey(c => c.Id );
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
builder.Entity<ClubUser>()
.HasOne<Club>(cu => cu.Club)
.WithMany(u => u.Users)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubUser>()
.HasOne<User>(cu => cu.User)
.WithMany(c => c.Clubs)
.HasForeignKey(cu => cu.Id);
}
}
[TestClass]
public class ManyToMany
{
[TestMethod]
public void DeleteAndInsert()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var original = clubusers.First();
clubusers.Remove(original);
var newClubUser = new ClubUser
{
Club = clubs.Last(),
User = users.First(),
Extra = "Another"
};
clubusers.Add(newClubUser);
context.SaveChanges();
}
[TestMethod]
public void Update()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var update = clubusers.First();
update.Club = clubs.Last();
update.Extra = "Changed";
Assert.ThrowsException<InvalidOperationException>( () => context.SaveChanges());
}
}
To initialise the test database:
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_User]
GO
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_Club]
GO
DROP TABLE [dbo].[ClubUsers]
GO
DROP TABLE [dbo].[Clubs]
GO
DROP TABLE [dbo].[Users]
GO
CREATE TABLE [dbo].[Clubs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Club] 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
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_User] 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
CREATE TABLE [dbo].[ClubUsers](
[ClubId] [int] NOT NULL,
[Id] [int] NOT NULL,
[Extra] [varchar](50) NOT NULL,
CONSTRAINT [PK_ClubUser] PRIMARY KEY CLUSTERED
(
[ClubId] ASC,
[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].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_Club] FOREIGN KEY([ClubId])
REFERENCES [dbo].[Clubs] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_Club]
GO
ALTER TABLE [dbo].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_User] FOREIGN KEY([Id])
REFERENCES [dbo].[Users] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_User]
GO
INSERT Clubs(Name)
VALUES ('GlenEagles');
INSERT Clubs(Name)
VALUES ('StAndrews');
INSERT Clubs(Name)
VALUES ('Wentworth');
INSERT dbo.[Users](Name)
VALUES ('Pete');
INSERT dbo.[Users](Name)
VALUES ('Dave');
INSERT ClubUsers(ClubId, Id, Extra)
VALUES (1,1, 'Hello');
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
add a comment |
* THIS DOES NOT WORK - see comment *
My third option which is the quickest for you to implement however i am not sure about all the implications.
If you change OnModelCreating to set an index instead of a key
i.e.
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
becomes
builder.Entity<ClubUser>()
.HasIndex(cu => new { cu.ClubID, cu.Id });
The update will now work but then you do not have a key on the ClubUser which may cause other problems.
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
add a comment |
About:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key...
PrimaryKey in ClubApplicationUsers table is both ClubID and Id.
You can't just make changes to existing records by Id.
For example this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
Must be like this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
Or:
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
About:
2nd approach:
...
Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
I'll explain with example:
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
Trying to remove A1, and add A2 - it says A2 already exists.
Solution is closer to the 2nd approach:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
//delete all club memberships and add new one
var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
{
_context.ClubApplicationUser.Remove(clubApplicatonUser);
}
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
return Page();
}
If you don't want to delete anything, but just add new record:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
// dont delete, just add new one
var clubApplicatonUserExists = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();
if (clubApplicatonUserExists == null)
{
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
}
return Page();
}
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?
– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
add a comment |
Another solution if you have control of the database schema is to add a surragate key to the link table.
Then if you need to update the club or user you are not changing the entity unique identifier and hence it will be allowed.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53986919%2fneed-a-help-to-update-the-model%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
I think the best solution is that you need to Delete and Insert instead of updating although given your ClubApplicationUser actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete.
If you think of it in the logic of your domain, I don't think a user generally changes being a member of one club to another, they leave (delete) one club and join (insert) another.
Although I could come up with another domain where it does make sense to update so I don't think this is a good generic argument.
The following code shows a cutdown version of your problem. You can see that the Tests allow an Insert and Delete but fail with an Update
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Clubs { get; set; }
}
public class ClubUser
{
public int ClubID { get; set; }
public Club Club { get; set; }
public int Id { get; set; }
public User User { get; set; }
public string Extra { get; set; }
}
public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubUser> ClubUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
}
protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
{
builder.Entity<Club>()
.HasKey(c => c.Id );
builder.Entity<User>()
.HasKey(c => c.Id );
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
builder.Entity<ClubUser>()
.HasOne<Club>(cu => cu.Club)
.WithMany(u => u.Users)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubUser>()
.HasOne<User>(cu => cu.User)
.WithMany(c => c.Clubs)
.HasForeignKey(cu => cu.Id);
}
}
[TestClass]
public class ManyToMany
{
[TestMethod]
public void DeleteAndInsert()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var original = clubusers.First();
clubusers.Remove(original);
var newClubUser = new ClubUser
{
Club = clubs.Last(),
User = users.First(),
Extra = "Another"
};
clubusers.Add(newClubUser);
context.SaveChanges();
}
[TestMethod]
public void Update()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var update = clubusers.First();
update.Club = clubs.Last();
update.Extra = "Changed";
Assert.ThrowsException<InvalidOperationException>( () => context.SaveChanges());
}
}
To initialise the test database:
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_User]
GO
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_Club]
GO
DROP TABLE [dbo].[ClubUsers]
GO
DROP TABLE [dbo].[Clubs]
GO
DROP TABLE [dbo].[Users]
GO
CREATE TABLE [dbo].[Clubs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Club] 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
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_User] 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
CREATE TABLE [dbo].[ClubUsers](
[ClubId] [int] NOT NULL,
[Id] [int] NOT NULL,
[Extra] [varchar](50) NOT NULL,
CONSTRAINT [PK_ClubUser] PRIMARY KEY CLUSTERED
(
[ClubId] ASC,
[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].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_Club] FOREIGN KEY([ClubId])
REFERENCES [dbo].[Clubs] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_Club]
GO
ALTER TABLE [dbo].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_User] FOREIGN KEY([Id])
REFERENCES [dbo].[Users] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_User]
GO
INSERT Clubs(Name)
VALUES ('GlenEagles');
INSERT Clubs(Name)
VALUES ('StAndrews');
INSERT Clubs(Name)
VALUES ('Wentworth');
INSERT dbo.[Users](Name)
VALUES ('Pete');
INSERT dbo.[Users](Name)
VALUES ('Dave');
INSERT ClubUsers(ClubId, Id, Extra)
VALUES (1,1, 'Hello');
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
add a comment |
I think the best solution is that you need to Delete and Insert instead of updating although given your ClubApplicationUser actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete.
If you think of it in the logic of your domain, I don't think a user generally changes being a member of one club to another, they leave (delete) one club and join (insert) another.
Although I could come up with another domain where it does make sense to update so I don't think this is a good generic argument.
The following code shows a cutdown version of your problem. You can see that the Tests allow an Insert and Delete but fail with an Update
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Clubs { get; set; }
}
public class ClubUser
{
public int ClubID { get; set; }
public Club Club { get; set; }
public int Id { get; set; }
public User User { get; set; }
public string Extra { get; set; }
}
public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubUser> ClubUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
}
protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
{
builder.Entity<Club>()
.HasKey(c => c.Id );
builder.Entity<User>()
.HasKey(c => c.Id );
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
builder.Entity<ClubUser>()
.HasOne<Club>(cu => cu.Club)
.WithMany(u => u.Users)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubUser>()
.HasOne<User>(cu => cu.User)
.WithMany(c => c.Clubs)
.HasForeignKey(cu => cu.Id);
}
}
[TestClass]
public class ManyToMany
{
[TestMethod]
public void DeleteAndInsert()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var original = clubusers.First();
clubusers.Remove(original);
var newClubUser = new ClubUser
{
Club = clubs.Last(),
User = users.First(),
Extra = "Another"
};
clubusers.Add(newClubUser);
context.SaveChanges();
}
[TestMethod]
public void Update()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var update = clubusers.First();
update.Club = clubs.Last();
update.Extra = "Changed";
Assert.ThrowsException<InvalidOperationException>( () => context.SaveChanges());
}
}
To initialise the test database:
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_User]
GO
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_Club]
GO
DROP TABLE [dbo].[ClubUsers]
GO
DROP TABLE [dbo].[Clubs]
GO
DROP TABLE [dbo].[Users]
GO
CREATE TABLE [dbo].[Clubs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Club] 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
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_User] 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
CREATE TABLE [dbo].[ClubUsers](
[ClubId] [int] NOT NULL,
[Id] [int] NOT NULL,
[Extra] [varchar](50) NOT NULL,
CONSTRAINT [PK_ClubUser] PRIMARY KEY CLUSTERED
(
[ClubId] ASC,
[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].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_Club] FOREIGN KEY([ClubId])
REFERENCES [dbo].[Clubs] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_Club]
GO
ALTER TABLE [dbo].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_User] FOREIGN KEY([Id])
REFERENCES [dbo].[Users] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_User]
GO
INSERT Clubs(Name)
VALUES ('GlenEagles');
INSERT Clubs(Name)
VALUES ('StAndrews');
INSERT Clubs(Name)
VALUES ('Wentworth');
INSERT dbo.[Users](Name)
VALUES ('Pete');
INSERT dbo.[Users](Name)
VALUES ('Dave');
INSERT ClubUsers(ClubId, Id, Extra)
VALUES (1,1, 'Hello');
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
add a comment |
I think the best solution is that you need to Delete and Insert instead of updating although given your ClubApplicationUser actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete.
If you think of it in the logic of your domain, I don't think a user generally changes being a member of one club to another, they leave (delete) one club and join (insert) another.
Although I could come up with another domain where it does make sense to update so I don't think this is a good generic argument.
The following code shows a cutdown version of your problem. You can see that the Tests allow an Insert and Delete but fail with an Update
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Clubs { get; set; }
}
public class ClubUser
{
public int ClubID { get; set; }
public Club Club { get; set; }
public int Id { get; set; }
public User User { get; set; }
public string Extra { get; set; }
}
public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubUser> ClubUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
}
protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
{
builder.Entity<Club>()
.HasKey(c => c.Id );
builder.Entity<User>()
.HasKey(c => c.Id );
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
builder.Entity<ClubUser>()
.HasOne<Club>(cu => cu.Club)
.WithMany(u => u.Users)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubUser>()
.HasOne<User>(cu => cu.User)
.WithMany(c => c.Clubs)
.HasForeignKey(cu => cu.Id);
}
}
[TestClass]
public class ManyToMany
{
[TestMethod]
public void DeleteAndInsert()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var original = clubusers.First();
clubusers.Remove(original);
var newClubUser = new ClubUser
{
Club = clubs.Last(),
User = users.First(),
Extra = "Another"
};
clubusers.Add(newClubUser);
context.SaveChanges();
}
[TestMethod]
public void Update()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var update = clubusers.First();
update.Club = clubs.Last();
update.Extra = "Changed";
Assert.ThrowsException<InvalidOperationException>( () => context.SaveChanges());
}
}
To initialise the test database:
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_User]
GO
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_Club]
GO
DROP TABLE [dbo].[ClubUsers]
GO
DROP TABLE [dbo].[Clubs]
GO
DROP TABLE [dbo].[Users]
GO
CREATE TABLE [dbo].[Clubs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Club] 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
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_User] 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
CREATE TABLE [dbo].[ClubUsers](
[ClubId] [int] NOT NULL,
[Id] [int] NOT NULL,
[Extra] [varchar](50) NOT NULL,
CONSTRAINT [PK_ClubUser] PRIMARY KEY CLUSTERED
(
[ClubId] ASC,
[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].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_Club] FOREIGN KEY([ClubId])
REFERENCES [dbo].[Clubs] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_Club]
GO
ALTER TABLE [dbo].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_User] FOREIGN KEY([Id])
REFERENCES [dbo].[Users] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_User]
GO
INSERT Clubs(Name)
VALUES ('GlenEagles');
INSERT Clubs(Name)
VALUES ('StAndrews');
INSERT Clubs(Name)
VALUES ('Wentworth');
INSERT dbo.[Users](Name)
VALUES ('Pete');
INSERT dbo.[Users](Name)
VALUES ('Dave');
INSERT ClubUsers(ClubId, Id, Extra)
VALUES (1,1, 'Hello');
I think the best solution is that you need to Delete and Insert instead of updating although given your ClubApplicationUser actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete.
If you think of it in the logic of your domain, I don't think a user generally changes being a member of one club to another, they leave (delete) one club and join (insert) another.
Although I could come up with another domain where it does make sense to update so I don't think this is a good generic argument.
The following code shows a cutdown version of your problem. You can see that the Tests allow an Insert and Delete but fail with an Update
public class Club
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public IList<ClubUser> Clubs { get; set; }
}
public class ClubUser
{
public int ClubID { get; set; }
public Club Club { get; set; }
public int Id { get; set; }
public User User { get; set; }
public string Extra { get; set; }
}
public class ApplicationDbContext : Microsoft.EntityFrameworkCore.DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Club> Clubs { get; set; }
public DbSet<ClubUser> ClubUsers { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(@"Server=.;Database=Spike;Trusted_Connection=True;");
}
protected override void OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder)
{
builder.Entity<Club>()
.HasKey(c => c.Id );
builder.Entity<User>()
.HasKey(c => c.Id );
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
builder.Entity<ClubUser>()
.HasOne<Club>(cu => cu.Club)
.WithMany(u => u.Users)
.HasForeignKey(bc => bc.ClubID);
builder.Entity<ClubUser>()
.HasOne<User>(cu => cu.User)
.WithMany(c => c.Clubs)
.HasForeignKey(cu => cu.Id);
}
}
[TestClass]
public class ManyToMany
{
[TestMethod]
public void DeleteAndInsert()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var original = clubusers.First();
clubusers.Remove(original);
var newClubUser = new ClubUser
{
Club = clubs.Last(),
User = users.First(),
Extra = "Another"
};
clubusers.Add(newClubUser);
context.SaveChanges();
}
[TestMethod]
public void Update()
{
var context = new ApplicationDbContext();
var clubusers = context.ClubUsers;
var clubs = context.Clubs;
var users = context.Users;
var update = clubusers.First();
update.Club = clubs.Last();
update.Extra = "Changed";
Assert.ThrowsException<InvalidOperationException>( () => context.SaveChanges());
}
}
To initialise the test database:
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_User]
GO
ALTER TABLE [dbo].[ClubUsers] DROP CONSTRAINT [FK_ClubUser_Club]
GO
DROP TABLE [dbo].[ClubUsers]
GO
DROP TABLE [dbo].[Clubs]
GO
DROP TABLE [dbo].[Users]
GO
CREATE TABLE [dbo].[Clubs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_Club] 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
CREATE TABLE [dbo].[Users](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](50) NOT NULL,
CONSTRAINT [PK_User] 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
CREATE TABLE [dbo].[ClubUsers](
[ClubId] [int] NOT NULL,
[Id] [int] NOT NULL,
[Extra] [varchar](50) NOT NULL,
CONSTRAINT [PK_ClubUser] PRIMARY KEY CLUSTERED
(
[ClubId] ASC,
[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].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_Club] FOREIGN KEY([ClubId])
REFERENCES [dbo].[Clubs] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_Club]
GO
ALTER TABLE [dbo].[ClubUsers] WITH CHECK ADD CONSTRAINT [FK_ClubUser_User] FOREIGN KEY([Id])
REFERENCES [dbo].[Users] ([Id])
GO
ALTER TABLE [dbo].[ClubUsers] CHECK CONSTRAINT [FK_ClubUser_User]
GO
INSERT Clubs(Name)
VALUES ('GlenEagles');
INSERT Clubs(Name)
VALUES ('StAndrews');
INSERT Clubs(Name)
VALUES ('Wentworth');
INSERT dbo.[Users](Name)
VALUES ('Pete');
INSERT dbo.[Users](Name)
VALUES ('Dave');
INSERT ClubUsers(ClubId, Id, Extra)
VALUES (1,1, 'Hello');
answered Jan 3 at 10:44
PhilSPhilS
56425
56425
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
add a comment |
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
As on domain using Soft-Delete approach .. we could not do the Hard-Delete and then Create approach. It's quite interesting ... if we do this at the database level (see UPDATE - 1), it worked well BUT not the code level.
– dcpartners
Jan 3 at 20:37
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
That was mwhat I suspectedwhen I mentioned 'actually it probably means an Update of the IsDeleted field as opposed to actually doing a Delete'. In that case you would change tthe original to mark it as deleted (setting the IsDeleted flag) and create a new one cloned from the original. Does that satisfy your domain or must you update the existing record?
– PhilS
Jan 4 at 11:05
add a comment |
* THIS DOES NOT WORK - see comment *
My third option which is the quickest for you to implement however i am not sure about all the implications.
If you change OnModelCreating to set an index instead of a key
i.e.
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
becomes
builder.Entity<ClubUser>()
.HasIndex(cu => new { cu.ClubID, cu.Id });
The update will now work but then you do not have a key on the ClubUser which may cause other problems.
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
add a comment |
* THIS DOES NOT WORK - see comment *
My third option which is the quickest for you to implement however i am not sure about all the implications.
If you change OnModelCreating to set an index instead of a key
i.e.
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
becomes
builder.Entity<ClubUser>()
.HasIndex(cu => new { cu.ClubID, cu.Id });
The update will now work but then you do not have a key on the ClubUser which may cause other problems.
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
add a comment |
* THIS DOES NOT WORK - see comment *
My third option which is the quickest for you to implement however i am not sure about all the implications.
If you change OnModelCreating to set an index instead of a key
i.e.
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
becomes
builder.Entity<ClubUser>()
.HasIndex(cu => new { cu.ClubID, cu.Id });
The update will now work but then you do not have a key on the ClubUser which may cause other problems.
* THIS DOES NOT WORK - see comment *
My third option which is the quickest for you to implement however i am not sure about all the implications.
If you change OnModelCreating to set an index instead of a key
i.e.
builder.Entity<ClubUser>()
.HasKey(cu => new { cu.ClubID, cu.Id });
becomes
builder.Entity<ClubUser>()
.HasIndex(cu => new { cu.ClubID, cu.Id });
The update will now work but then you do not have a key on the ClubUser which may cause other problems.
edited Jan 4 at 12:24
answered Jan 3 at 10:52
PhilSPhilS
56425
56425
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
add a comment |
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
I now understand this better, without setting the key it is now implicitely making the Id field the key (i.e. the User Id) due to the name of the property - if it had been called UserId instead you would have got an error when instanciating the context that there was no primary key.
– PhilS
Jan 4 at 12:26
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
The implication of this is that you cannot change the User as it changes the Id property, only the Club and if you have a User with more than one club it will update all of them in the database which is bad.
– PhilS
Jan 4 at 12:29
add a comment |
About:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key...
PrimaryKey in ClubApplicationUsers table is both ClubID and Id.
You can't just make changes to existing records by Id.
For example this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
Must be like this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
Or:
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
About:
2nd approach:
...
Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
I'll explain with example:
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
Trying to remove A1, and add A2 - it says A2 already exists.
Solution is closer to the 2nd approach:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
//delete all club memberships and add new one
var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
{
_context.ClubApplicationUser.Remove(clubApplicatonUser);
}
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
return Page();
}
If you don't want to delete anything, but just add new record:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
// dont delete, just add new one
var clubApplicatonUserExists = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();
if (clubApplicatonUserExists == null)
{
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
}
return Page();
}
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?
– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
add a comment |
About:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key...
PrimaryKey in ClubApplicationUsers table is both ClubID and Id.
You can't just make changes to existing records by Id.
For example this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
Must be like this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
Or:
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
About:
2nd approach:
...
Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
I'll explain with example:
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
Trying to remove A1, and add A2 - it says A2 already exists.
Solution is closer to the 2nd approach:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
//delete all club memberships and add new one
var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
{
_context.ClubApplicationUser.Remove(clubApplicatonUser);
}
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
return Page();
}
If you don't want to delete anything, but just add new record:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
// dont delete, just add new one
var clubApplicatonUserExists = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();
if (clubApplicatonUserExists == null)
{
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
}
return Page();
}
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?
– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
add a comment |
About:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key...
PrimaryKey in ClubApplicationUsers table is both ClubID and Id.
You can't just make changes to existing records by Id.
For example this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
Must be like this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
Or:
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
About:
2nd approach:
...
Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
I'll explain with example:
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
Trying to remove A1, and add A2 - it says A2 already exists.
Solution is closer to the 2nd approach:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
//delete all club memberships and add new one
var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
{
_context.ClubApplicationUser.Remove(clubApplicatonUser);
}
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
return Page();
}
If you don't want to delete anything, but just add new record:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
// dont delete, just add new one
var clubApplicatonUserExists = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();
if (clubApplicatonUserExists == null)
{
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
}
return Page();
}
About:
InvalidOperationException: The property 'ClubID' on entity type
'ClubApplicationUser' is part of a key...
PrimaryKey in ClubApplicationUsers table is both ClubID and Id.
You can't just make changes to existing records by Id.
For example this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString());
Must be like this:
var clubApplicationUserToUpdate = await _context.ClubApplicationUser
.FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
Or:
var clubApplicationUsersToUpdate = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
About:
2nd approach:
...
Result: Violation of PRIMARY KEY constraint 'PK_ClubApplicationUser'.
I'll explain with example:
Clubs: 1, 2, 3
ApplicationUsers: A, B, C
ClubApplicationUser: A1, A2
Trying to remove A1, and add A2 - it says A2 already exists.
Solution is closer to the 2nd approach:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
//delete all club memberships and add new one
var clubApplicatonUsersToRemove = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString()).ToList();
foreach (var clubApplicatonUser in clubApplicatonUsersToRemove)
{
_context.ClubApplicationUser.Remove(clubApplicatonUser);
}
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
return Page();
}
If you don't want to delete anything, but just add new record:
public async Task<IActionResult> OnPostAsync(Guid id)
{
if (!this.ModelState.IsValid)
{
return Page();
}
// dont delete, just add new one
var clubApplicatonUserExists = await _context.ClubApplicationUser
.Where(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID).FirstOrDefaultAsync();
if (clubApplicatonUserExists == null)
{
_context.ClubApplicationUser.Add(new ClubApplicationUser()
{
Id = id.ToString(),
ClubID = AssignClubUser.SelectedClubID
});
await _context.SaveChangesAsync();
}
return Page();
}
edited Jan 8 at 12:31
answered Jan 8 at 12:26
ObelixxObelixx
1015
1015
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?
– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
add a comment |
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?
– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "don't want to delete anything, but just add new record" ... how do we differentiate this then when we get the list for instance (.FirstOrDefaultAsync)?
– dcpartners
Jan 8 at 21:23
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
With this approach - "Solution is closer to the 2nd approach:" above, we have a soft-deleted approach that we want to keep the record and this might work.
– dcpartners
Jan 8 at 21:36
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
I din't get exactly what you need to achieve. My examples are just to explain the problem and provide working example code. You can toon it to work for you, or provide exactly what you need to delete and what to keep/create.
– Obelixx
Jan 9 at 7:43
Sorry for the late reply. I did what you suggest in term of :
var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?– dcpartners
Jan 11 at 20:42
Sorry for the late reply. I did what you suggest in term of :
var clubApplicationUserToUpdate = await _context.ClubApplicationUser .FirstOrDefaultAsync(m => m.Id == id.ToString() && m.ClubID == AssignClubUser.SelectedClubID.ToString());
but in this case SelectedClubID we changed to originalClubID ... but when we save still have the same issue?– dcpartners
Jan 11 at 20:42
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
What is the primary goal? (delete all club relations to user or delete one club related to user, and then what - add selected if it dosen't exist) How you get the ClubId that needs to be deleted?
– Obelixx
Jan 15 at 11:36
add a comment |
Another solution if you have control of the database schema is to add a surragate key to the link table.
Then if you need to update the club or user you are not changing the entity unique identifier and hence it will be allowed.
add a comment |
Another solution if you have control of the database schema is to add a surragate key to the link table.
Then if you need to update the club or user you are not changing the entity unique identifier and hence it will be allowed.
add a comment |
Another solution if you have control of the database schema is to add a surragate key to the link table.
Then if you need to update the club or user you are not changing the entity unique identifier and hence it will be allowed.
Another solution if you have control of the database schema is to add a surragate key to the link table.
Then if you need to update the club or user you are not changing the entity unique identifier and hence it will be allowed.
answered Jan 3 at 10:47
PhilSPhilS
56425
56425
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53986919%2fneed-a-help-to-update-the-model%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
To clarify why you can do it in the database versus not in EntityFramework. Entity Framework needs to identify an entity, it does this using the key, this is so it can keep track of various things such as changes - the SQL UPDATE statement needs the key properties in the WHERE clause. Hence the key properties make up the identity of the entity and if you change them its going to have problems. You could think about it as being a different entity and hence the create a new entity approach I suggested.
– PhilS
Jan 4 at 12:02
If you look at the EntityFrameworkCore source code in the SetPropertyModified method you can see it has explicit code blocking updates to keys and throwing InvalidOperationException
– PhilS
Jan 4 at 12:08
To my mind this is a symtom of the object-referential impedience mismatch as ClubApplicationUser is not an 'entity' - its not really responsible for its own lifecycle and should be part of the Club andor User if you were using a different more objectcentric persistence store.
– PhilS
Jan 4 at 12:17