this is my first mvc 3 project, i am using linq to sql.
public ActionResult Edit(int ID)
{
try
{
Tutorial tut = reposi.Tutorials.Single(d => d.TutorialID == ID);
return View(tut);
}
catch
{
return RedirectToAction("List");
}
}
[HttpPost]
public ActionResult Edit(Tutorial tut)
{
if (ModelState.IsValid)
{
//tut.TutorialID = ID;
tut.EditDate = DateTime.Now;
tutContext.SubmitChanges();
return RedirectToAction("List");
}
else
{
return View(tut);
}
}
after I click on the "Edit" button, It takes me back to list page, and changes are not saved. still old values.
You need to first get the Tutorial from your database, then make the changes, then SubmitChanges().
[HttpPost]
public ActionResult Edit(Tutorial tut)
{
if (ModelState.IsValid)
{
Tutorial t = tutContext.get(tut.Id);
//tut.TutorialID = ID;
t.EditDate = DateTime.Now;
tutContext.SubmitChanges();
return RedirectToAction("List");
}
else
{
return View(tut);
}
}
Note, your tutContext.get(tut.Id); may be different depending on your implementation.
tut.EditDate = DateTime.Now;
tutContext.SubmitChanges();
return RedirectToAction("List");
Your tutorial object is not managed by db context yet. so the context didn't save the object change when you change the tut object and invoke tutContext.SubmitChanges().
First thing first, you must lookup the tutorial object from the context.
Tutorial tut = ctx.Tutorials.Single(d => d.TutorialID == ID);
after you get the tuts object form the tuts context, that tuts object is managed by the db context. then you can modify the tut object and submit the changes.
Tutorial tut = ctx.Tutorials.Single(d => d.TutorialID == tut.ID);
tut.EditDate = DateTime.Now;
ctx.SubmitChanges();
You need this in the [HttpPost]
When you debug, are you reaching the Post action in the controller? Make sure your form action is set to POST instead of GET.
Related
I know there's a lot of questions with answer on this topic, but I cant seem to find anything relating to my scenario.
Im getting "The relationship between the two objects cannot be defined because they are attached to different ObjectContext objects" on Inventory CurrentInventory = db.Inventories.Find(injection.InventoryID);
This worked the very first time i ran it and subsequent attempts all fail.
I'm not understanding how the I'm getting different context objects.
My Controller:
public class InventoriesController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Inventories/Edit
public ActionResult Edit()
{
return View(db.Inventories.ToList());
}
// POST: Inventories/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(List<Inventory> inventory)
{
//List<Inventory> inventory
if (inventory == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
foreach (Inventory injection in inventory)
{
Inventory CurrentInventory = db.Inventories.Find(injection.InventoryID);
CurrentInventory.Date = DateTime.Now;
CurrentInventory.Quantity = injection.Quantity;
CurrentInventory.LastChangedBy = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
}
if (ModelState.IsValid)
{
// db.Entry(inventory).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(EntityState.Modified);
}
}
EDIT:
I went back to a more single updating methodology just to see if that would help. I was STILL getting the same error. So I compared it with one of my other controllers modified Edit methods and noticed that I did not utilize the .Find() method instead I iterated over them records myself and returned the desired object. When doing do the problem was resolved.
// GET: Inventories/Edit/5
public ActionResult Edit(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Inventory inventory = db.Inventories.Find(id);
ViewData["Id"] = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
if (inventory == null)
{
return HttpNotFound();
}
return View(inventory);
}
// POST: Inventories/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include = "InventoryID,Quantity,LastChanged,Date")] Inventory inventory)
{
Inventory oldInventory = null;
foreach (var a in db.Inventories)
{
if (a.InventoryID == inventory.InventoryID)
{
oldInventory = a;
}
}
if (oldInventory == null)
{
return HttpNotFound();
}
if (ModelState.IsValid)
{
//db.Entry(inventory).State = EntityState.Modified;
TryUpdateModel(oldInventory);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(inventory);
}
Follow-up question:
Does the .Find() method create another dbContext in its implementation so that returned objects are inherently from a different context entirely?
Iterating over the private context in the controller and returning the object needed instead of using the .Find() method solved my problem although I'm not too keen on the reason.
I'm reading Professional ASP.Net MVC 5 and the section on Model Binding shows 3 ways to do the same thing. They are listed below. Can anyone explain the pros and cons of each method?
The first example uses if ModelState.IsValid
the second example uses if TryUpdateModel
The third uses both.
What am I missing here? All seem to work. Why 3 ways to write it?
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit (Album album)
{
if (ModelState.IsValid)
{ db.Entry( album). State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(" Index");
}
return View( album);
}
View( album);
}
[HttpPost]
public ActionResult Edit()
{
var album = new Album();
if (TryUpdateModel( album))
{ db.Entry( album). State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(" Index");
}
else
{
return View( album);
}
}
[HttpPost]
public ActionResult Edit()
{
var album = new Album();
TryUpdateModel( album);
if (ModelState.IsValid)
{
db.Entry( album). State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction(" Index");
}
else
{
return View( album);
}
}
The first is very simple and supports the most common use where simply newing up the model and mapping the data to it is fine. The instance creation and mapping is handled automatically and "just works".
The third allows you map the incoming data to an existing object instance. If for some reason you already have an object instance you want to map the data too (it probably already has extra data in you want to use and that data doesn't exist in a new instance MVC would automatically create) then this is how you would do it.
The second is the same as the third, but allows you to differentiate between "The model failed to update" and "the model updated, but is invalid".
I am submitting some data in database and after submit I want to show same page. But I am viewing the page the textbox value is not empty.
ModelState.Clear();
I have used to clear the textbox.
But still the textbox value is remain. please suggest me to clear the model after submit in mvc3.
public ActionResult AddNewCategory(CategoryViewModel model) {
if (ModelState.IsValid) {
int result = 0;
var categoryEntity = new Category {
CategoryName = model.CategoryName, CategorySlug = model.CategorySlug
};
result = Convert.ToInt32(_categoryRepository.AddNewCategory(categoryEntity));
if (result > 0) {
ModelState.Clear();
}
}
return View(model);
}
You're getting the same model, because you're passing it to the view View(model). You have couple options here: either pass an empty model, or redirect to the get variant of your post action.
1)
if (ModelState.IsValid)
{
//saving
if (result > 0)
{
ModelState.Clear();
return View(new CategoryViewModel());
}
}
2)
if (ModelState.IsValid)
{
//saving
if (result > 0)
{
return RedirectToAction("AddNewCategory");
}
}
PS: I strongly advice to use the second approach as you might want to make other DB calls to construct your model and you won't want to do this in multiple places.
in very simplest way try it
if(ModelState.IsValid)
{
//code here to save data to database
db.SaveChanges();
ModelState.Clear();
}
return view(new CategoryViewModel());
Hear is what does ModelState.Clear()
ModelState.Clear() is used to clear errors but it is also used to
force the MVC engine to rebuild the model to be passed to your View.
So as in your case #Vsevolod Goloviznin suggested you can use:
return View(new CategoryViewModel());
to have view with empty values
I'm really new at all of this. I'm currently reading the tutorial "Getting Started with EF using MVC" on the ASP.NET website: (Chapter 6)
http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/updating-related-data-with-the-entity-framework-in-an-asp-net-mvc-application
In the tutorial, at the part titled "Adding Course Assignments to the Instructor Edit Page" The author wrote about how to edit the Course in the instructor page:
public ActionResult Edit(int id)
{
Instructor instructor = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.InstructorID == id)
.Single();
PopulateAssignedCourseData(instructor);
return View(instructor);
}
public ActionResult Edit(int id, FormCollection formCollection, string[] selectedCourses)
{
var instructorToUpdate = db.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.Courses)
.Where(i => i.InstructorID == id)
.Single();
if (TryUpdateModel(instructorToUpdate, "", null, new string[] { "Courses" }))
{
try
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
db.Entry(instructorToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
Could some one please tell me how to use the same concept (as the author) to accomplish deleting and creating action method? Or if you can direct me to a useful tutorial/site about many to many relationship with EF in MVC, especially on how to create a controller and view for many to many related data, similar to the tutorial mentioned above. I'm a beginner at this, but I still need to get work done, so it would really help if the concepts used coincides, it would be easier for me.
Thanks so much in advance!
Creation and Deletion are usually a bit simpler. I've outlined just the basic structure. The controller for the create entry page is :
public ViewResult CreateInstructor()
{
return View();
}
To generate the create form you can use the scaffolding (just right-click on View and select Add View... and then select the appropriate model and page type).
The post-back of this page is handled by a controller with the [HttpPost] attribute, it will bind the data on the form to the object passed into the controller method and this is then passed to your DBContext Add() method, finally execute the SaveChanges() method to actually hit the database:
[HttpPost]
public ActionResult CreateInstructor(Instructor instructor)
{
db.Instructors.Add(instructor);
db.Instructors.SaveChanges();
return View(); //There will be some other logic here typically such as redirecting on a successful creation or showing specific validation issues with the object .
}
Delete is achieved using the DBContext Remove method, just note that you do not first have to hit the database to load the object and then again to delete it. You will typically be passed the object's id and you can then attach a new instance of the object to the context and delete it.
public ActionResult DeleteInstructor(int instructorId)
{
var instructor = new Instructor {Id = instructorId};
db.Instructors.Attach(instructor);
db.Instructors.Remove(instructor);
db.Instructors.SaveChanges();
return View();
}
I know this is an old thread. But I was trying to do the EXACT same thing
Here's my solution
//
// GET: /Project/Create/
public ActionResult Create()
{
var project = new Project();
ViewBag.Themes = db.Theme.ToList();
return View(project);
}
//
// POST: /Project/Create/
[HttpPost]
public ActionResult Create(Project projet, string[] selectedTheme)
{
var errors = ModelState.Select(x => x.Value.Errors).ToList();
project.Themes = new List<Theme>();
if (TryUpdateModel(project, "", null, new string[] { "Theme" }))
{
try
{
UpdateProjectTheme(selectedTheme, project);
db.Project.Add(project);
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Impossible to create project.");
}
}
return View(project);
}
And in the create view :
#{
List<Project.Models.Theme> themes = ViewBag.Themes;
#:<td>
foreach (var display in themes)
{
<label class="checkbox">
<input type="checkbox"
name="selectedTheme"
value="#display.ThemeID" />
#display.Name
</label>
if (display.Name.Length > 20)
{
#:<br />
}
else
{
if (display.ThemeID % 2 == 0)
{
#:<br />
}
}
}
#:</td>
#: </tr>
}
Hope you find it useful, you can also look on my SO post
This is my post !!
I have been tearing my hair out over this for days and before I go completely bald it's time to ask all the people smarter than me how to do this.
I am using Entity Framework 4 with the Code First CTP 5 and MVC 3.
The exception message right now is "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
First up here is the controller the edit form is posted to:
public ActionResult Save(ClientEntity postedClient)
{
try
{
if (ModelState.IsValid)
{
Base.clientInterface.Save(postedClient);
return RedirectToAction("Index");
}
}
catch (Exception)
{
throw;
}
SetupManageData(null, postedClient);
return View("Manage");
}
The Save method on the client interface is this:
public void Save(ClientEntity theClient)
{
SetContext();
if (theClient.clientId == 0)
this.pContext.Clients.Add(theClient);
else
{
ClientEntity existingClient = GetSingle(theClient.clientId); // Get the existing entity from the data store.
// PseudoCode: Merge existingClient and theClient - Can this be done without using ObjectStateManager?
// PseudoCode: Attach merged entity to context so that SaveChanges will update it in the database - is this correct?
}
this.pContext.SaveChanges();
}
private void SetContext()
{
if (this.pContext == null)
this.pContext = new PersistanceContext();
}
Persistance context is the DBContext and looks like this:
public class PersistanceContext : DbContext
{
public DbSet<ClientEntity> Clients { get; set; }
}
What is the lifestyle of clientInterface? is it a singleton or something that keeps it alive across multiple requests?
My guess is that it holds a live instance of a database context that was used to fetch the entity in the GET request and when the POST tries to (re)add a client entity to the context the old one is still there and they conflict.
Try destroying the object that is behind the clientInterface with every request. Maybe use a DI container that supports per-webrequest lifestyles so you don't have to worry about it.
I hope my guess was right and this is of help.
This should work.
if (theClient.clientId == 0)
{
this.pContext.Clients.Add(theClient);
}
else
{
ClientEntity existingClient = this.pContext.Clients.Single(o => o.ClientId == theClient.ClientId);
// map properties
existingClient.Name = theClient.name;
// ....
}
this.pContext.SaveChanges();
[Edit]
It's easier (IMHO) to split the creation & editing of objects into 2 seperate views and to avoid the mapping of the properties I use TryUpdateModel.
[HttpPost]
public ViewResult Edit(int clientID, FormCollection collection)
{
var client = pContext.Clients.SingleOrDefault(o => o.ID == clientID);
if(!TryUpdateModel(client, collection))
{
ViewBag.UpdateError = "Update Failure";
}
else
{
db.SubmitChanges();
}
return View("Details", client);
}
[HttpPost]
public ViewResult Create(FormCollection collection)
{
var client = new Client();
if(!TryUpdateModel(client, collection))
{
ViewBag.UpdateError = "Create Failure";
}
else
{
db.Clients.Add(client);
db.SubmitChanges();
}
return View("Details", client);
}