This was asked in different forms in the past, but I am still unable to get it working.
I am building a site in dotnet core / ef core and on one page I want to use jqGrid to easily edit one table that will be changing very often. Table is called CoList (Company List)
I can already add new rows and I can delete them, however when I try to edit row I am getting this error:
The property 'AutoId' on entity type 'CoList' is part of a key and so cannot be modified or marked as modified.
I have created database first as it was used somewhere else before I started to work on this new site.
My model CoList.cs:
public partial class CoList
{
public int AutoId { get; set; }
public string CompanyName { get; set; }
public string CoLevel { get; set; }
public string CoCode { get; set; }
}
Database context file
modelBuilder.Entity<CoList>(entity =>
{
entity.HasKey(e => e.AutoId)
.HasName("PK_CoList");
entity.HasIndex(e => e.AutoId).IsUnique();
entity.Property(e => e.AutoId).HasColumnName("AutoID");
entity.Property(e => e.CoCode)
.IsRequired()
.HasColumnType("varchar(50)");
entity.Property(e => e.CoLevel)
.IsRequired()
.HasColumnType("varchar(50)");
entity.Property(e => e.CompanyName)
.IsRequired()
.HasColumnType("varchar(50)");
});
In controller for edit I have:
public string EditCoList(int? Id, [Bind(include: "CompanyName, CoLevel, CoCode")] CoList coList)
{
FPSDemoContext db = _context;
string msg;
try
{
if (ModelState.IsValid)
{
db.Entry(coList).State = EntityState.Modified;
db.SaveChanges();
msg = "Saved";
}
else
{
msg = "Did not validate";
}
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
if (entry.Entity is CoList)
{
var databaseEntity = db.CoList.AsNoTracking().Single(p => p.AutoId == Id);
var databaseEntry = db.Entry(databaseEntity);
foreach (var property in entry.Metadata.GetProperties())
{
var proposedValue = entry.Property(property.Name).CurrentValue;
var originalValue = entry.Property(property.Name).OriginalValue;
var databaseValue = databaseEntry.Property(property.Name).CurrentValue;
entry.Property(property.Name).CurrentValue = proposedValue;
entry.Property(property.Name).OriginalValue = databaseEntry.Property(property.Name).CurrentValue;
}
}
else
{
msg = "Error occured:" + ex.Message;
throw new NotSupportedException("Concurrency conflict " + entry.Metadata.Name);
}
}
// Retry the save operation
db.SaveChanges();
msg = "Saved";
}
return msg;
}
Once I hit save, the code crashes on the 'Retry Save operation'.
jqGrid Code:
$(function () {
$("#jqGrid").jqGrid({
regional: 'en',
url: "/SiteOptions/GetCoList",
datatype: 'json',
mtype: 'Get',
colNames: ['Id', 'Company Name', 'Company Level', 'Company Code'],
colModel: [
{ key: true, name: 'autoId', index: 'autoId', editable: false },
{ key: false, name: 'companyName', index: 'companyName', editable: true },
{ key: false, name: 'coLevel', index: 'coLevel', editable: true },
{ key: false, name: 'coCode', index: 'coCode', editable: true }],
pager: jQuery('#jqControls'),
rowNum: 10,
rowList: [10, 20, 30, 40, 50],
height: '100%',
viewrecords: true,
caption: 'Company List - Grid',
emptyrecords: 'No Companies to display',
jsonReader: {
root: "rows",
page: "page",
total: "total",
records: "records",
repeatitems: false,
Id: "0"
},
autowidth: true,
multiselect: false
}).navGrid('#jqControls', { edit: true, add: true, del: true, search: false, refresh: true },
{
zIndex: 100,
url: '/SiteOptions/EditCoList',
closeOnEscape: true,
closeAfterEdit: true,
recreateForm: true,
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
},
{
zIndex: 100,
url: "/SiteOptions/CreateCoList",
closeOnEscape: true,
closeAfterAdd: true,
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
},
{
zIndex: 100,
url: "/SiteOptions/DeleteCoList",
closeOnEscape: true,
closeAfterDelete: true,
recreateForm: true,
msg: "Are you sure you want to delete this row? ",
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
});
});
I have read those answers but they did not help me, or I am misunderstanding something.
The property on entity type is part of a key and so cannot be modified or marked as modified
The property 'name' is part of the object's key information and cannot be modified. Entity Framework
https://github.com/aspnet/EntityFrameworkCore/issues/4560
EDIT #2
As per comments I have changed the edit method to this:
public async Task<IActionResult> EditCoList(int Id, CoList coList)
{
string msg;
msg = "Model state is not valid";
if (Id != coList.AutoId)
{
msg = "Not Found";
}
if (ModelState.IsValid)
{
try
{
_context.Update(coList);
await _context.SaveChangesAsync();
msg = "Saved";
}
catch (DbUpdateConcurrencyException)
{
if (!CoListExists(coList.AutoId))
{
msg = "Concurrency Exception - Not Found";
}
else
{
msg = "Error";
throw;
}
}
}
return Content(msg);
}
private bool CoListExists(int? id)
{
return _context.CoList.Any(e => e.AutoId == id);
}
I am using viewmodel too (with extra table I am going to need on that view later):
namespace ASPNET_Core_1_0.Models.SiteOptionsViewModels
{
public class SiteOptionsViewModel
{
public IEnumerable<ASPNET_Core_1_0.Models.SiteOptions> SiteOptions { get; set; }
public IEnumerable<ASPNET_Core_1_0.Models.CoList> CoList { get; set; }
public ASPNET_Core_1_0.Models.SiteOptions AutoId { get; set; }
public ASPNET_Core_1_0.Models.SiteOptions CurrentMonth { get; set; }
public ASPNET_Core_1_0.Models.SiteOptions CurrentYear { get; set; }
public ASPNET_Core_1_0.Models.SiteOptions SelectedCompanyId { get; set; }
public ASPNET_Core_1_0.Models.CoList CompanyName { get; set; }
public ASPNET_Core_1_0.Models.CoList CoLevel { get; set; }
public ASPNET_Core_1_0.Models.CoList CoCode { get; set; }
}
}
And in controller I call the view like so:
public async Task<IActionResult> Companylist()
{
ViewData["SubTitle"] = "Company List";
ViewData["Message"] = "Edit company list";
var model = new SiteOptionsViewModel
{
SiteOptions = await _context.SiteOptions.ToListAsync(),
CoList = await _context.CoList.ToListAsync()
};
return View(model);
}
I am still getting the concurrency error though:
Microsoft.EntityFrameworkCore.DbContext:Error: An exception occurred in the database while saving changes. Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions. at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.d__6.MoveNext() --- End of stack trace from previous location where exception was thrown ---
Interestingly when I generate CRUD controller for any table in my DB from VS it all always works, no issues at all in editing or saving the edits. I followed the generated code to create my own and still get the concurrency error.
EDIT #3
The jqGrid js script with added prmNames: { id: "AutoId" } (as per Oleg comment),
$(function () {
$("#jqGrid").jqGrid({
regional: 'en',
prmNames: { id: "AutoId" },
url: "/SiteOptions/GetCoList",
datatype: 'json',
mtype: 'Get',
colNames: ['Id', 'Company Name', 'Company Level', 'Company Code'],
colModel: [
{ key: true, name: 'autoId', index: 'autoId', editable: false },
{ key: false, name: 'companyName', index: 'companyName', editable: true },
{ key: false, name: 'coLevel', index: 'coLevel', editable: true },
{ key: false, name: 'coCode', index: 'coCode', editable: true }],
pager: jQuery('#jqControls'),
rowNum: 10,
rowList: [10, 20, 30, 40, 50],
height: '100%',
viewrecords: true,
caption: 'Company List - Grid',
emptyrecords: 'No Companies to display',
jsonReader: {
root: "rows",
page: "page",
total: "total",
records: "records",
repeatitems: false,
Id: "0"
},
autowidth: true,
multiselect: false
}).navGrid('#jqControls', { edit: true, add: true, del: true, search: false, refresh: true },
{
zIndex: 100,
url: '/SiteOptions/EditCoList',
closeOnEscape: true,
closeAfterEdit: true,
recreateForm: true,
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
},
{
zIndex: 100,
url: "/SiteOptions/CreateCoList",
closeOnEscape: true,
closeAfterAdd: true,
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
},
{
zIndex: 100,
url: "/SiteOptions/DeleteCoList",
closeOnEscape: true,
closeAfterDelete: true,
recreateForm: true,
msg: "Are you sure you want to delete this row? ",
afterComplete: function (response) {
if (response.responseText) {
alert(response.responseText);
}
}
});
});
Method (as per Oleg comment):
public async Task<IActionResult> EditCoList(CoList coList)
{
string msg;
msg = "Model state is not valid";
/*if (Id != coList.AutoId)
{
msg = "Not Found";
}*/
if (ModelState.IsValid)
{
try
{
_context.Update(coList);
await _context.SaveChangesAsync();
msg = "Saved";
}
catch (DbUpdateConcurrencyException)
{
if (!CoListExists(coList.AutoId))
{
msg = "Concurrency Exception - Not Found";
}
else
{
msg = "Error";
throw;
}
}
}
return Content(msg);
}
Still no joy
Can someone please tell me what am I doing wrong? I am still learning so it might be simple. If possible at all could any of you show me correct solution to this?