MVC Music Store Saving concurrency. What is causing this? - asp.net

I am doing the MVC Music Store Tutorials and I have finished, every works fine but for some reason when I edit an album on an Admin account it comes up with this error when I try to save changes. It highlights db.SaveChanges(); what is causing this problem?
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
public ActionResult Edit(int id)
{
Album album = db.Albums.Find(id);
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album)
{
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
I am the only person accessing the site I have made as I am doing it locally only on my machine, I have been told that somebody else may changed something but this is not the case. What's going on?

This issue is explained on this page on the musicstore codeplex site.
Here is an excerpt:
In the Album class, you've defined [Bind(Exclude = "AlbumId")] on the class, which means that the code won't try and bind the AlbumId from the form. Which is fine, except that on the edit page, you're passing in a populated Album, which is presumably being populated using binding, which is of course ignoring the Album Id, so the AlbumId of the object passed into the edit method, is always 0, which throws a misleading concurrency error, because no rows are affected, because there's no album with ID of 0.

Related

With same parameters and methods name, how can the controller finds which one to be invoked in ASP.NET core MVC

I am following the tutorial posted on the Microsoft website https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/details?view=aspnetcore-2.2
I just wonder once I click the delete button, how does it know which method or action should be invoked first? get or post? with the same parameters and action name
The code below might show you more details.
Thank you
// GET: Movies/Delete/5
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie
.FirstOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
return View(movie);
}
// POST: Movies/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
var movie = await _context.Movie.FindAsync(id);
_context.Movie.Remove(movie);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
As stated in the comments above, a GET request will usually return a View to perform an action, but it won't actually perform that action unless it is a POST request, e.g. a GET request to an action named Edit will grab the data to edit and display it. That's it. The changes are not saved to the database until a POST to the Edit action is submitted.
Overloaded methods are required to have different parameter signatures. Since the other scaffolded pairs of CRUD actions (except Delete) have different signatures, they can have the same name. But since both the GET and POST methods for the Delete action have the same parameter signature, one of them needs to be renamed, which is why the POST action is named DeleteConfirmed. However, having GET and POST methods named differently will break the routing built into MVC. Adding the ActionName("Delete") attribute fixes that.
Routing depend on the HTTP Method + The name + The Parameters
so, when you issue a GET request to /Movies/Delete/5 it will use the first one.
When you issue a POST request to /Movies/Delete/5, it will use the second one.
If you have more than one POST method with different parameters, it will use the most specific. ex:
Delete(int id, bool confirm)
Delete(int id)
If you issue a POST request to /Movies/Delete/5, it will go for the second action, but if you change it to /Movies/Delete/5?confirm=true, it will go for the first one unless the parameter confirm was nullable, in this case it will throw an exception as it will not be able to determine which action to invoke

Inserting Sub-Array into Azure's Document DB

I am trying to insert a sub class (document) of "Video" into my Organization document.
However, when I try to add a record, I get "Object reference is not set to an instance of an object."
I tried to use Add and Insert, but neither worked. I looked at the Dcoument explorer and I can see that Videos is returning "null."
I am assuming my problem is that Document DB doesn't know that Video is a list. (in my model, I have defined it as a list though)
Also, I have tried created new objects for Organization and Video. Also, I have a class called Category, it has the exact same code (except the object is Category) and it is inserting fine.
Below is the action that I am using.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "name,description,link")] Video video)
{
if (ModelState.IsValid)
{
UserSession usersession = new UserSession();
usersession = (UserSession)Session["user"];
Organization organization = (Organization)DocumentDBRepository<Organization>.GetItem(d => d.Id == usersession.organizationId);
video.DateAdded = DateTime.Now;
organization.Videos.Add(video);
await DocumentDBRepository<Organization>.UpdateItemAsync(organization.Id, organization);
return RedirectToAction("Index");
}
return View(video);
}
Set organization.Videos to a non-null value. Document db simply preserves what you stored. Apparently, you previously stored null.

ASP.NET MVC5 not saving to the database

I'd like to get a user from db by ID field, update one of the fields and save it.
I have a following action in my Users controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Update()
{
AspNetUser mister = db.AspNetUsers.Find(12);
mister.Permissions = 2;
db.Entry(mister).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("index", "users");
}
It is finding a right user, modifies the value on the mister object and calls save. Doesn't give any errors, but it is not saving?
Any help will be much appreciated.

Weird caching issue with ASP.net/Linq

I'm writing an application involving storing a profile. I'm using Linq to access the database, but having a weird issue when saving a profile. When I save it, it writes to the DB correctly - but when I leave the page and come back, the old values still remain in profile form.
My profile page:
if(!Page.IsPostBack) {
Profile p = Student.GetProfile(Int32.Parse(Session["userID"].ToString()));
if (p != null)
{
FirstNameTextBox.Text = p.FirstName;
LastNameTextBox.Text = p.LastName;
Address1TextBox.Text = p.Address1;
.....
}
And my Student class:
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
I'm not doing any fancy caching anywhere, so not sure where the old values are stored...
** EDIT **
So, it seems that it's down to a global LinqDataContext. In my Student class, I had:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
If I give the GetProfile method it's own DataContext, problem solved.
Still being very new to Linq, what's the best way to have a class with numerous methods that use the same access to a database? Having a global context like this? Or each method using it's own data context?
Assuming you're storing the userID in Session["userID"] somewhere and not clearing it out when you save, this could be where the "caching" occurs. Session objects will live (roughly) for the life of the browser session (or the life of the server process if in-memory session is enabled).
i guess even if linq send the query to the database it use the old value stored in the cache.so you have to clear the cache if you want the new value.
Going on your added comments my guess is that the page you are getting is cached by the browser / http server. If the url is the same as a previously requested page some default setting will tell the browser / server to use the cache html. To avoid this and get new html for each request you could try adding a meta tag within the head tags of your html.
<meta http-equiv="Pragma" CONTENT="no-cache">
It ended up being the DataContext I was using. I'm not exactly sure why this fixed the issue, but I changed my class from:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}
to:
public class Student
{
public static Profile GetProfile(int uID)
{
LinqClassesDataContext db = new LinqClassesDataContext();
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}

Database not updating model in MVC

So i just started using ASP.NET MVC and i'm really liking it, except i seem to have an odd knack to encounter the most bizarre of errors. I'm making a simple blogging application for myself. I have two simple models: post and comment. I have a partial view for creating a comment that is embedded in the details view for each post. When i submit the form to update the comment, it goes to my CommentsController's create action, which looks like...
[HttpPost]
public ActionResult Create(comment comment)
{
comment.date = DateTime.Now;
if (ModelState.IsValid)
{
post p = db.posts.Find(comment.post); //I've verified that comment.post is coming in
if (p.comments == null) p.comments = new List<comment>();
p.comments.Add(comment);
db.Entry(p).State = EntityState.Modified; //I'm using this line since that's how its done in the edit actionmethod of the BlogController. I was just updating db.posts.Find(... manually, but that wasn't workign either.
db.comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Details", "Blog", new { id = comment.post });
}
return PartialView(comment);
}
The problem is that while the comment gets added to the database just fine, the post doesn't update. When i examine p just before the changes are saved, it's updated, but apparently it never actually commits to database since when i redirect to the Details, those comments aren't there. Is there anything obviously wrong with my code? Am i missing some basic fundamental of .NET or MVC? Let me know if i need to provide more code or context.
Interesting Note: No matter what, post.comments always seems to be null. I set it to an empty list when the post is created, but it still seems to come back null. Not sure if this is just a result of trying to store an empty list or if it has to do with my problem, though. Again, lemme know and i'll stick anything else needed up here.
Thanks!
Perhaps saving the changes is working fine but you don't see the saved comments to a post because you don't load them when you display the post. You can eager load the comments of a post in your action which displays a post like so:
post p = db.posts
.Include(p1 => p1.comments)
.Where(p1 => p1.Id == id)
.SingleOrDefault();
I also think that you can simplify your Create action:
[HttpPost]
public ActionResult Create(comment comment)
{
comment.date = DateTime.Now;
if (ModelState.IsValid)
{
db.comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Details", "Blog", new { id = comment.post });
}
return PartialView(comment);
}
This should work if comment.post is the foreign key of a comment to the related post. (Your code looks like this is the case, because of Find(comment.post))
While #Slauma led me to my solution, I'm just posting my final code i used for future reference (thanks #George Stocker)
public ActionResult Create(comment comment)
{
comment.date = DateTime.Now;
if (ModelState.IsValid)
{
db.comments.Add(comment);
db.SaveChanges();
return RedirectToAction("Details", "Blog", new { id = comment.post });
}
return PartialView(comment);
}
and to retrieve comments...
public ActionResult Details(int id)
{
var post = (from p in db.posts
where p.id == id
select new { p.id, p.title, p.content, p.date, p.tag, comments = (from c in db.comments where c.post == id select c) }).SingleOrDefault();
post p2 = new post(post.id, post.title, post.content, post.date,post.tag, post.comments.ToList());
return View(p2);
}

Resources