Hello I am trying to edit my table in db and is give me this error An update, insert, or delete instruction in the store has affected an unexpected number of rows (0). Entities may have been modified or deleted since loading.
trying many test and when i'm arrive on SaveChange is stop process
and incomplete operaion
This is the implementation
public ActionResult Edit(int? id)
{
db = new IdentityDBEntities();
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Article article = db.Articles.Find(id);
if (article == null)
{
return HttpNotFound();
}
ViewBag.Idc = new SelectList(db.Categories, "Id", "libelle", article.Idc);
return View(article);
}
// POST: Articles/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Ida,description,UserId,Idc,titre,image")] Article article, HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
if (image != null)
{
article.image = image.FileName;
}
db = new IdentityDBEntities();
article.UserId = System.Web.HttpContext.Current.User.Identity.GetUserId();
db.Entry(article).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.Idc = new SelectList(db.Categories, "Id", "libelle", article.Idc);
return View(article);
}
Article was loaded in one instance of a DbContext, and you are attempting to save it with another instance. The entity would need to be associated to the new context before being saved. (I.e. Attach or Update) However, I do not recommend this approach.
As a general rule you should avoid passing entities to and from a web client. If you accept an entity to a web client, it is a simple matter to find the call passing the modified entity to the server, apply a breakpoint, and modify the contents using the debugger before resuming. If the server merely attaches the entity to a context and saves the changes, I can modify data in ways the application should not allow. It also involves sending more information to/from the client than is typically necessary.
You also run into the problem that the data supporting an entity loaded at one point in your current session has been modified by another session between the time you read the data, and the time you're prepared to update it. Systems should not merely attach and overwrite data without first verifying (via something like a Timestamp or last modified date/time) that the data hasn't been updated since the entity was loaded. From there the system can take an appropriate action. (log that data will be overwritten, overwrite, or merge & notify the user to review their changes.)
edit: To outline an example.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "Ida,description,UserId,Idc,titre,image")] Article article, HttpPostedFileBase image)
{
if (!ModelState.IsValid)
{
ViewBag.Idc = new SelectList(db.Categories, "Id", "libelle", article.Idc);
return View(article);
}
using (var db = new IdentityDBEntities())
{
var existingArticle = db.Articles.Single(x => x.ArticleId == article.ArticleId);
if (existingArticle.RowVersion != article.RowVersion) // Or compare LastModifiedDateTime etc.
{
// Set a validation state to tell the user that the article had changed since they started editing. Perhaps merge values across, but send the article back.
return View(existingArticle);
}
if (image != null)
existingArticle.image = image.FileName;
existingArticle.UserId = System.Web.HttpContext.Current.User.Identity.GetUserId(); // can this be updated?
db.SaveChanges();
}
return RedirectToAction("Index");
}
The key differences:
DbContext's are disposable so should be wrapped in a using block.
We load the article from the context, check if it's been modified since we had originally loaded it. (other validation checks to ensure the user can modify this article would be prudent as well) if it isn't out of date and is valid, transfer the fields across and save the changes.
Related
I'm writing a simple application in .net core mvc.
The problem is with loading related data by view. The 1st time I load the view it gets through wonderfully, I attach data to the view by entity framework include extensions function etc.
This code is on GET, when just displaying the item to be updated, filling fields with current values.
public IActionResult Edit(long id)
{
ServiceResult<LectureDTO> result = _lectureService.GetById(id, new LectureIncludeOptions(true));
LectureEditViewModel model = new LectureEditViewModel
{
Lecture = new LectureEditDTO(result.Entity)
};
if (!result.Success)
{
model.HandleResult(result, ModelState);
}
return View(model);
}
2nd time around when submitting a POST, IF the model I've submitted is not valid or the result of the update is not okay, I'd like to just attach errors to the model and send it to the view (by the HandleResult method).
public IActionResult Edit(LectureEditViewModel model)
{
if (!ModelState.IsValid)
{
ServiceResult<LectureDTO> getResult = _lectureService.GetById(model.Lecture.Id, new LectureIncludeOptions(true));
if (!getResult.Success)
{
model.HandleResult(getResult, ModelState);
}
model.Lecture = new LectureEditDTO(getResult.Entity);
return View(model);
}
ServiceResult<LectureDTO> result = _lectureService.Update(model.Lecture, User.FindFirstValue(ClaimTypes.NameIdentifier));
if (result.Success)
{
model = new LectureEditViewModel()
{
Lecture = new LectureEditDTO(result.Entity)
};
}
model.HandleResult(result, ModelState);
return View(model);
}
This is how I'm currently loading relationship data on bad editing. I think it's wasteful if I already had it in the model before, just displaying the edit form. Is there a way to pass this data by model?
I've tried this in my form to pass the collection, but everytime I debug this controller action, the value for it is empty.
<input asp-for="Lecture.Lecturers" value="#Model.Lecture.Lecturers" class="form-control" type="hidden" />
Am I going about this all wrong? Is what I've been doing okay? What could I improve?
Thanks for taking the time to help me with this.
As Chris Pratt pointed out in comment to my thread, you should not post any data to the server, you don't want the user to be able to change. Therefore my case for using collections in this way is invalid. To post collections to the server, write them out in input fields and post them.
Here is my appointment controller where I have create appointment code what can I add here so if the user books an appointment on the same time as another user it doesn't allow them to book it
public class AppointmentController : Controller
{
Context db = new Context();
// GET: Appointment
public ActionResult Index()
{
string UserName = User.Identity.Name;
return View(db.Appointment.ToList().Where(a=> a.username == UserName));
}
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Appointment appointment)
{
try{
string UserName = User.Identity.Name;
appointment.username = UserName;
// TODO: Add insert logic here
// if (ModelState.IsValid)
// {
db.Appointment.Add(appointment);
db.SaveChanges();
return RedirectToAction("Index", "Appointment");
// }
}
catch (Exception ex)
{
return View(ex.ToString());
}
}
This appears to be an ASP MVC Controller. MVC is generally faster to develop for once you get the hang of it, but the learning curve is steep.
There are many MVC tutorials available, such as MVC 5 Tutorial
For your original question, the below may work:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Appointment appointment)
{
//This action will receive a "Model" (the appointment object) from the View (WebPage).
//Most "HttpPost" methods are attempting to Save data in some way: Update, Create, etc.
//The general flow in an MVC Post method is
//1. Check that the Model is Valid (required fields have a value, numbers don't have letters, etc.)
//2. Then perform any server side checks (like conflicting schedules)
//3. Then attempt to save.
//4. If successful, return to Index. Else, add the Error to the Model and return the Model.
try{
//Use LINQ to check if the Appointment the User wants to book
// 1. Starts before any other appointment ends
// 2. AND ALSO Ends after any other appointment starts.
// If both conditions are true, there is some overlap
// Making a guess as to what the fields representing Start and Stop times are called.
// Replace the field names as appropriate
if (db.Appointment.Where(a => a.EndTime > appointment.StartTime && a.StartTime < appointment.EndTime).Any())
//If any existing appointment exists, then add a model state error.
ModelState.AddModelError("StartTime", "Time not available.");
//Using the Logged In User's UserName as the appointment's UserName
appointment.username = User.Identity.Name;
//TODO: Add in any other checks or changes here.
//Example: Check if the appointment is within business hours.
//They will follow the same format:
// Check the Condition
// If something is wrong, add the ModelError to the ModelState.
//If a field is invalid, or we added a ModelError, the ModelState will NOT be valid.
if (ModelState.IsValid)
{
//Add a row to the appointment table.
db.Appointment.Add(appointment);
//Save the change.
db.SaveChanges();
//Send the user back to Index.
return RedirectToAction("Index", "Appointment");
}
}
catch (Exception ex)
{
//Returns a detailed error message to the User.
//While helpful for development, this is generally a no-no in production.
return View(ex.ToString());
}
//Return the appointment model back to the View.
//The model will contain the ModelError, which will trigger
// any validation code on the View.
return View(appointment);
}
this is a very amateur way of coding i will suggest you compare it with java with a if statements. you need to grab the value and send it to the database and compare it with the value. But like i said i will rethink this code because this is an inefficient way of coding and not the right way.
goodluck
I have just read about TransactionScope. It is very good and informative.
First of all, I was wondering if I really need transactions in MVC 4 / EF 6+. The reason for that is we always invoke DbContext.SaveChanges() to save changes. I'm wondering if SaveChanges() is something that simulates transaction close meaning if I invoke SaveChanges() I commit a transactions.
On the other hand, if I need transactions, then how to implement TransactionScope in MVC / EF applications. My scenario is something similar to the steps below:
I save valid record in database
I save a copy of an old and a new record in another table which is sort of archived version of the original table
I save user's activity in another table
I also provided code. As you can see if something goes wrong in the middle I have inconsistent data. I would be grateful for some examples on how to use TransactionScope. I may also need more to save in other tables. I would like to be certain either I save everything or nothing, such that I save everything if transaction is OK or roll back anything that happened up to the problem.
Thanks.
[HttpPost]
public ActionResult Edit(ApplicationViewModel viewmodel)
{
using(MyDbCOntext dbContext = new MyDbContext())
{
if(!MoselState.IsValid)
return View(application);
// Copy old data from database and assign to an object
ApplicationArchive applicationOld = CopyApplicationFromDB(db, viewmodel.ApplicationID);
// Update model
if (TryUpdateModel(applicationNew), null, null, new string[] { "ApplicationID" })
{
try
{
dbContext.Entry(userToUpdate).State = EntityState.Modified;
dbContext.SaveChanges();
// Archive old application
ApplicationArchive applicationNew = CopyApplicationFromDB(db, viewmodel.ApplicationID);
try
{
dbContext.ApplicationsArchive.Add(applicationOld);
dbCOntext.ApplicationsArchive.Add(applicationNew);
dbContext.SaveChanges();
// Register user activity
string username = GetUserNameFromCookie();
UserActivity useractivity = new UserActivity() { UserName = username, activity = "edit", Table = "application" };
try
{
dbContext.UserActivities.Add(useractivity);
dbContext.SaveChanges();
return RedirectView("Index");
}
}
}
catch
{
ModelState.AddModelError("", "Cannot update this application");
}
}
//
return View(application);
}
}
You need to wrap your database operation within a DbContextTransaction. See this link for Entity Framework transaction examples:
https://msdn.microsoft.com/en-us/data/dn456843.aspx
I want to serve different views which use different ViewModel objects depending on Actions. This can be achieved in a conventional ASP.NET MVC paradigm.
[HttpGet]
public ActionResult Create() {
return View(new CreateViewModel()); //this serves Create.cshtml View
}
[HttpGet, ActionName("Create")]
public ActionResult CreatePOST(CreateViewModel viewModel) {
if (!ModelState.IsValid) {
return View(viewModel); //if error, re-serve Create.cshtml View
}
// Create new model into database
return RedirectToAction("Index");
}
[HttpGet]
public ActionResult Edit(int Id) {
var model = RetriveModel(Id);
var viewModel = new EditViewModel { Id = model.Id, Name = model.Name };
return View(viewModel); //this serves Edit.cshtml
}
[HttpPost, ActionName("Edit")]
public ActionResult EditPOST(EditViewModel viewModel) {
if (!ModelState.IsValid) {
return View(viewModel); //if error, re-serve Edit.cshtml View
}
// Update model in database
return RedirectToAction("Index");
}
How do I do the same to Orchard Content Part? It seems that the overridable Editor method in a ContentPartDriver fused both Create and Update actions together. How do I tell if the method is creating or updating? Something like
// GET
protected override DriverResult Editor(CustomContentPart part, dynamic shapeHelper) {
if (IsNewRecord) {
return ContentShape("Parts_CustomContentPart_Create" () =>
shapeHelper.EditorTemplate(TemplateName: "Parts/CreateTemplate", Model: new CreateViewModel(), Prefix: Prefix)
);
} else {
return ContentShape("Parts_CustomContentPart_Edit" () =>
shapeHelper.EditorTemplate(TemplateName: "Parts/EditTemplate", Model: BuildEditViewModel(part), Prefix: Prefix)
);
}
}
// POST
protected override DriverResult Editor(CustomContentPart part, IUpdateModel updater, dynamic shapeHelper) {
object viewModel;
if (IsNewRecord) {
viewModel = new CreateViewModel();
} else {
viewModel = new EditViewModel();
}
update.TryUpdateModel(viewModel, Prefix, null, null);
return Editor(part, shapeHelper);
}
I'm a beginner in Orchard still learning the ropes on how Orchard does things. Pardon me if my questions are too trivial.
Check for a content item id, if it is null, or possibly 0, I forget, then you are in the process of creating a content item. If it does have a value then you are editing. You can also use this in your view, can be quite handy.
If you need custom functionality to be called on creation/updating then you could consider using handler methods?
So in your parts handler add something like
OnCreated<MyPart>((ctx, part) => CreateItems(part));
Where CreateItems is a method with your part as a parameter. There are a bunch of content item events you can hook into, there is a neat list in the docs: http://docs.orchardproject.net/Documentation/Understanding-content-handlers
As always, check the source code for good examples of their usage.
EDIT
Apparently checking for null id doesn't work, I checked in some of my modules were I used it and have used the following check:
Model.ContentItem.VersionRecord == null || !Model.ContentItem.VersionRecord.Published
Although this question has been asked and answered, just thought of posting my findings so I can find it later.
ContentItem.Id is indeed 0 when the content item isn't created yet. For example, when you're about to create a new Page, ContentItem.Id == 0. Now just click on the Save button without filling up the form and validation will fail since the required field Title wasn't provided and we're getting back the same view with an error. Since validation failed, technically we don't consider the content item to be created yet. However, at this point Orchard already treating it as an existing content item. Orchard even went as far as obtaining and increasing the Identity counter of the Content Item Record table (Orchard_Framework_ContentItemRecord) from the database and assigning it as an Id to the content item.
Orchard even wired up all the Version Records, making it pretty much a full-fledged content item. All these for a content item that failed validation during creation and facing possibility of being discarded altogether. The only thing Orchard hasn't done is inserting it into the database (it's only residing in memory at this point). Therefore there's really no other ways to tell if a content item is an existing one or one that was about to be created other than checking it against the database and see if the content item was really there.
var contentItemRepository = _workContext.Resolve<IRepository<ContentItemRecord>>();
var contentItemRecord = contentItemRepository.Get(Model.ContentItem.Id);
if (contentItemRecord == null) {
isNew = true;
}
or we could also use the IContentManager to do the same thing
var contentManager = Model.ContentItem.ContentManager;
var contentItem = contentManager.Get(Model.ContentItem.Id, VersionOptions.AllVersions);
if (contentItem == null) {
isNew = true;
}
Edit:
Apparently I spoke too soon. When I said above that Orchard hasn't inserted the content item into the database yet and it still resides in memory, it actually already in the database, in a yet to be committed Transaction. In the case above where validation fails, the transaction will be rolled back at the end. The correctness of the above code depends on when it was executed. If it was executed before the transaction was cancelled and rolled back, the content item is still in the database and won't yield an accurate result. If it was executed after transaction rollback (eg. in a View), then it'll behave as expected.
How Orchard handles content item creation can be seen in Orchard.Core.Contents.Controllers.AdminController.CreatePOST(string, string, Action<ContentItem>):
_contentManager.Create(contentItem, VersionOptions.Draft);
var model = _contentManager.UpdateEditor(contentItem, this);
if (!ModelState.IsValid) {
_transactionManager.Cancel();
return View(model);
}
The content item was being created first before it was being fed into IContentManager.UpdateEditor() to validate.
Update:
Filed a bug at
https://github.com/OrchardCMS/Orchard/issues/6534
I'm having some trouble using a helper method to perform an update to a set of model objects. The table uses a lookup table to hold 5 records per agent/user. If I want to save the record for the agent, I need to save that record onto the AgentTransmission table, and up to 5 other records on the RelationshipCodeLookup table.
Since I have to do this five times per agent, and we must do the process in the Create and Edit methods, I created a helper method to save the records. This works fine during the create process as we're simply doing a DbContext.Add(). However when I need to perform an update, I get the error message
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
I think this has to do with the fact I'm passing the model object to my helper method, and therefore the DbContext thinking that it has two separate objects to keep track of. I say this because the lines of code that are commented out work just fine and allow me to save the object. Passing the object to the helper method, however, gets the above error.
Does anyone know of a way around this (using a helper method to perform an update)?
Controller Action
//Save relationship codes in lookup table
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode1))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode1, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode2))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode2, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode3))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode3, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode4))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode4, agenttransmission.ID);
}
if (AgentTransmissionValidator.ValidateRelationshipCode(agenttransmission.RelationshipCode5))
{
//db.Entry(agenttransmission.RelationshipCode1).State = EntityState.Modified;
//db.SaveChanges();
SaveRelationshipCodes(agenttransmission.RelationshipCode5, agenttransmission.ID);
}
Helper Method
public void SaveRelationshipCodes(RelationshipCodeLookup relCode, int id)
{
if (relCode.AgentId == 0) relCode.AgentId = id;
relCode.LastChangeDate = DateTime.Now;
relCode.LastChangeId = Security.GetUserName(User);
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
db.Entry(relCode).State = EntityState.Detached;
}
else
{
if(relCode.RelCodeOrdinal == 0) relCode.RelCodeOrdinal = FindOrdinal(relCode);
db.RelationshipCodeLookup.Add(relCode);
}
db.SaveChanges();
}
EDIT
After scouring the web I attempted to save via this method
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
db.Entry(relCode).CurrentValues.SetValues(relCode);
}
else
{
Member 'CurrentValues' cannot be called for the entity of type 'RelationshipCodeLookup because the entity does not exist in the context. To add an entity to the context call the Add or Attach method of DbSet<RelationshipCodeLookup>
However.... doing that only puts me back at the start with the following error on db.RelationshipCodeLookup.Attach(relCode);
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Try this:
db.RelationshipCodeLookup.Attach(relCode);
db.Entry(relCode).State = EntityState.Modified;
For updates you want to attach the detached object then set it's state to modified.
The issue here seems to be that the Entity Framework cannot track two objects of the same kind at the same time. Because of that I find the solution to this problem more than a little weird. By calling .Find() on the DbContext and instantiating a second copy of the model object I was finally able to save. Seems to break all the rules the EF was laying out for me in the error messages, but hey it works.
public void SaveRelationshipCodes(int id, RelationshipCodeLookup relCode)
{
if (relCode.AgentId == 0) relCode.AgentId = id;
relCode.LastChangeDate = DateTime.Now;
relCode.LastChangeId = Security.GetUserName(User);
//Check to see if record exists and if not add it
if (db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal) != null)
{
//Need to call .Find to get .CurrentValues method call to work
RelationshipCodeLookup dbRelCode = db.RelationshipCodeLookup.Find(id, relCode.RelCodeOrdinal);
db.Entry(dbRelCode).CurrentValues.SetValues(relCode);
}
else
{
if(relCode.RelCodeOrdinal == 0) relCode.RelCodeOrdinal = FindOrdinal(relCode);
db.RelationshipCodeLookup.Add(relCode);
}
db.SaveChanges();
}