I'm working with .net Core 5 and MVC
I'm trying to create a form to fill out Customer information to proceed with Ordering, everything is fine but the order Id generated is always 0. I Don't know where the problem lies. Can someone help me with the problem I'm having?
Here is my Repository
public bool CreateOrder(Order order)
{
var newOrder = new Order
{
FirstName = order.FirstName,
LastName = order.LastName,
Email = order.Email,
PhoneNumber = order.PhoneNumber,
//more
};
_dbContext.Orders.Add(newOrder);
_dbContext.SaveChanges();
return true;
}
Here is my Post method
public IActionResult CreateOrder(Order order)
{
var items = _cartRepo.GetCartItem();
if (items == null)
{
ModelState.AddModelError("", "Add Product First");
}
else
{
_cartRepo.CreateOrder(order);
}
return View();
}
Model
public partial class Order
{
[Key]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
// More
}
Maybe you are missing this in your createOrder view inside the form:-
<input hidden asp-for="Id" />
Do you use SSMS? If yes, go to the menu Tools > Options > Designers and uncheck Prevent Saving changes that require table re-creation.
Related
I am pretty new to the EF world and am trying to understand why I am unable to save off changes to my phone record. I realize that I need to modify my entity but it doesn't seem to make a difference. I have tried using context.xxxx but I am getting an error that context is not defined. What is the using for context? If I change the phone type for the same user I want to update the record with the new info.
Phone definition:
namespace NewSMS.DATA.EF
{
using System;
using System.Collections.Generic;
public partial class Phone
{
public int Phone_ID { get; set; }
public string Phone_Number { get; set; }
public int User_PK1 { get; set; }
public int LU_Phone_Type_PK1 { get; set; }
public Nullable<bool> Available { get; set; }
public virtual LU_Phone_Type LU_Phone_Type { get; set; }
public virtual User User { get; set; }
}
}
Code:
if (phnMems != null && phnMems.Count > 0)
{
var smsPhn = new Phone { };
foreach (var item in phnMems)
{
smsPhn.Phone_Number = item.Phone_Number;
smsPhn.LU_Phone_Type_PK1 = item.Phone_Type_ID;
smsPhn.User_PK1 = obj.User_ID;
Phone phoneInfo = db.Phone.FirstOrDefault(n => n.User_PK1 == obj.User_ID);
if (phoneInfo == null)
{
db.Phone.Add(smsPhn);
db.SaveChanges();
}
else
{
smsPhn.Phone_ID = phoneInfo.Phone_ID; //keep userID
db.Entry(phoneInfo).State = EntityState.Modified;
phoneInfo = smsPhn;
db.SaveChanges();
}
}
} //End IF
smsPhn.Phone_ID = phoneInfo.Phone_ID; //keep userID
db.Entry(phoneInfo).State = EntityState.Modified;
In above code, you are assigning phone_ID in smsPhn object but updating phoneInfo object.
Just a Suggestion:
Move this declaration
var smsPhn = new Phone { };
inside foreach loop and call savechanges outside the loop.
I've got a model that represents a joint table (with payload) in my database:
public class UserHasCar
{
// Foreign keys
[Key, Column(Order = 0)]
public string ApplicationUserId { get; set; }
[Key, Column(Order = 1)]
public int CarId { get; set; }
// Navigation properties
[Required]
public virtual ApplicationUser ApplicationUser { get; set; }
[Required]
public virtual Car Car{ get; set; }
// Additional fields
public int YearsRidden { get; set; }
}
public class Car
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
public class ApplicationUser : IdentityUser
{
public int BirthYear{ get; set; }
public virtual ICollection<UserHasCar> UserHasCars { get; set; }
}
I have a form that includes multiple select boxes, and upon submitting I want to clear out all records related to that user who submitted the form in the UserHasCar table and replace them with the new updated information. I'm getting a An entity object cannot be referenced by multiple instances of IEntityChangeTracker. because I am doing something wrong, but I don't see where I am using more than one context. This code happens in my controller:
public ApplicationUser GetCurrentUser()
{
return UserManager.FindById(User.Identity.GetUserId());
}
public string GetUserId()
{
string id = User.Identity.GetUserId();
var user = UserManager.FindById(id);
return user.Id;
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ManageCars(FormCollection form)
{
string id = GetUserId();
// Remove cars records with my id from database
var queryCars = (from m in db.UserHasCars where m.ApplicationUserId == id select m).ToList();
foreach (var record in queryCars )
{
// Could the problem be here?
db.UserHasCars.Remove(record)
}
// Add user-submitted cars to the database
string carval = form["Cars[0]"];
Car car = (from m in db.Cars where m.Name == carval select m).First();
int carid = car.ID;
// I get the abovementioned title error here
db.UserHasCars.Add(
new UserHasCar()
{
ApplicationUser = GetCurrentUser(),
ApplicationUserId = id,
Car = car,
CarId = carid,
YearsRidden = 0
}
);
db.SaveChanges();
}
I've seen many SO posts, but can't seem the problem as why my code doesn't want to save the new database entries.
EDIT
The solution was to remove the call to get the user and replace it with a query. Why? I was making database conflict errors by having both types of calls (database and DataManager calls in the same controller action). I ended up using a modified GetUser() function instead of GetCurrentUser()
Code:
public ApplicationUser GetUser()
{
// As opposed to:
// UserManager.FindById(User.Identity.GetUserId())
// We make a database call to grab our user instead
// So we don't get database context conflicts by using UserManager
string id = GetUserId();
return db.Users.Where(m => m.Id == id).First();
}
public string GetUserId()
{
return User.Identity.GetUserId();
}
// snip
// in ManageCars(FormCollection form)
ApplicationUser user = GetUser();
// snip
var newRow = db.UserHasCars.Create();
newRow.ApplicationUser = user;
// snip
db.UserHasCars.Add(newRow);
Try removing this line:
ApplicationUser = GetCurrentUser(),
from your object instantiation when adding.
Entity populates this object automatically once you set the foreign key ApplicationUserId. If UserManager.FindById(User.Identity.GetUserId()) uses a different db context that's where your exception is coming from.
Also to save yourself further trouble down the line, you should always call db.SaveChanges() in between the two operations. If you're worried about the atomicity of the db operation, just wrap the whole thing in a Transaction.
And when adding new rows to a table, I usually prefer to use something like:
var newRow = db.SomeTable.Create();
newRow.SomeColumn1 = "something";
newRow.SomeColumn2 = 5;
db.SomeTable.Add(newRow);
db.SaveChanges();
In order to delete entries from UserHasCars you need to change their EntityState to Deleted.
Example:
foreach (var record in queryCars )
{
db.ObjectStateManager.ChangeObjectState(record, EntityState.Deleted);
}
Hope this will fix your issue.
I am creating an MVC4 application that has an index page that shows a grid. The index action for the page has parameters for a search string, sort order, account id, and vendor id that control what is shown.
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
}
From this main page I can click on rows in the grid, which take me to the create and edit actions. In addition, I have several other actions on the control that are called from the index page.
When I press Save or Cancel on any of these views (the [HttpPost] action, as usual they redirect to the index action. But these views don't have the search string, sort order, account id, and vendor id so that the index view can "restore" what was being shown.
So I have been adding these parameters to the other actions methods, and making an object with the properties needed to in the view models for the methods.
public class PageFeatures
{
public string SearchFilter {get; set;}
public string SortOrder {get; set;}
public int AccountId {get; set;}
public int VendorId {get; set;}
}
public class CreateViewModel
{
public PageFeatures Features { get; set; }
}
public class EditViewModel
{
public EditViewModel(int id)
{
Id=id;
}
public int Id {get; set;}
public PageFeatures Features { get; set; }
}
public class MyController
{
public ActionResult Index(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
...
}
public ActionResult Create(string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new CreateViewModel();
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Create(CreateViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
public ActionResult Edit( int id=0,
string searchFilter="",
string sortOrder="createddate",
int accountid=0,
int vendorid=0)
{
var model = new EditViewModel(id);
model.Features = new PageFeatures
{
SearchFilter = searchFilter,
SortOrder = sortOrder,
AccountId = accountid,
VendorId = vendorid
}
return View(model);
}
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
}
But what I don't like about this approach is that I am peppering my views and every controller method with these values.
Am I doing the right thing, or is there a better way, like an action filter that would make this stuff easier and less repetitive?
[HttpPost]
public ActionResult Edit(EditViewModel model)
{
if(ModelState.IsValid)
{
....
return RedirectToAction("Index", new {searchFilter=model.Features.SearchFilter,
sortOrder = model.Features.SortOrder,
accounted = model.Features.AccountId,
vendorid = model.Features.VendorId} );
}
}
You are using the right approach.
If you dont want your URL to be ugly filled with query string parameters. You can try storing these values in a session and retrieving them in each action of your controller. Set them once and your done to use it anywhere, if you want to change the session values you can just over write it
Have you thought about using TempData and setting these params in there. The best part is that you can persist TempData across Controllers and you can perhaps modify the TempData values in a base controller class.
I am trying to use the following ViewModel
public class ProjectViewModel
{
public Project Project { get; set; } //bulk of the information
public int SelectedGovernmentClientId { get; set; } //selected ID for the dropdown
public IEnumerable<SelectListItem> GovernmentClients { get; set; } //dropdown values
}
Here's my project class
public class Project
{
public int ID { get; set; }
public string Title { get; set; }
//omitting extra fields
public virtual GovernmentClient GovernmentClient { get; set; }
}
Here's the action that I have
[HttpPost]
public ActionResult Edit(ProjectViewModel projectViewModel)
{
if (ModelState.IsValid)
{
//i am getting the following from debugging
//projectViewModel.Project.GovernmentClient.Name is NULL
//projectViewModel.Project.GovernmentClient.ID is the correct ID from the dropdown
db.Entry(projectViewModel.Project).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(projectViewModel);
}
All the values are getting updated EXCEPT the government client. Why is this happening?
projectViewModel.Project.GovernmentClient =
db.GovernmentClients.Find(projectViewModel.SelectedGovernmentClientId);
You are retrieving the value from database and setting its state to modified. However, there is no modification at all.
After a whole day of playing around with this, I think I might've solved it
I added the following to my Project model
[ForeignKey("GovernmentClient")]
public int GovernmentClient_ID { get; set; }
To render the dropdown, I used this
#Html.DropDownListFor(model => model.Project.GovernmentClient_ID, Model.GovernmentClients)
To generate the GovernmentClients list, I used the following method
private IEnumerable<SelectListItem> GetGovernmentClientsList(int selectedItem = -1)
{
var defaultItem = Enumerable.Repeat(new SelectListItem
{
Value = "-1",
Text = " - Select a government client - ",
Selected = (selectedItem == -1)
}, count: 1);
var clients = db.GovernmentClients.ToList().Select(x => new SelectListItem
{
Value = x.ID.ToString(),
Text = x.ClientName,
Selected = (selectedItem == -1) ? false : (x.ID == selectedItem)
});
return defaultItem.Concat(clients);
}
Overall, I am happy with this, because I am not hardcoding any property names, which I know would come back and bite me.
I am trying to find the best way to use MVC for models which are only partially edited.
Below is an simple example.
Model
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
View
using System.ComponentModel.DataAnnotations;
public class SimpleModel
{
public int Id { get; set; }
public string Parent { get; set; }
[Required]
public string Name { get; set; }
}
Controller
using System.Web.Mvc;
public class SimpleController : Controller
{
public ActionResult Edit(int id)
{ return View(Get(id)); }
[HttpPost]
public ActionResult Edit(int id, SimpleModel model)
{
if (model.Name.StartsWith("Child")) //Some test that is not done client-side.
{
Save(model);
//Get the saved data freshly.
//model = Get(id);
}
else
{
ModelState.AddModelError("", "Name should start with 'Child'");
}
//Is this the way to set the Parent property?
//var savedModel = Get(id);
//model.Parent = savedModel.Parent;
return View(model);
}
//Mock a database.
SimpleModel savedModel;
private void Save(SimpleModel model)
{ savedModel = new SimpleModel() { Id = model.Id, Name = model.Name }; }
private SimpleModel Get(int id)
{
if (savedModel == null)
{ return new SimpleModel() { Id = id, Parent = "Father", Name = "Child " + id.ToString() }; }
else
{ return new SimpleModel() { Id = savedModel.Id, Parent = "Father", Name = savedModel.Name }; }
}
}
The Name field is editable. The Parent field is only for reference and should not be updated. Therefore, it is rendered using DisplayFor.
Upon post, I receive a model with property Parent set to null. That's no problem as it will not be saved. However, when I simply return the received model to the view, the Parent field will no longer be displayed. When the model is valid, I can easily get it again from the database and thus get the Parent field's value back.
When the model is not valid, I would like to allow the user to correct input and attempt to save once more. There, I the received model's values that are input should be used, but the displayed values should be shown as well.
In reality, there are many more fields to be shown for reference, most often from different database entities than the one that is being edited.
I have seen suggestions to pass the fields as hidden fields in the view, but I feel very reluctant to read data from the client that should not be updated.
Is there a more elegant way to do this than copying these values into the model manually or passing them as hidden fields?
What about giving those un-editable properties to another model and let it take care of those properties?
View Model
public class PersonViewModel
{
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public PersonDetailsModel DetailsModel { get; set; }
}
Details Model
public class PersonDetailsModel
{
public string Mother { get; set; }
public string Father { get; set; }
public PersonDetailsModel() { }
public PersonDetailsModel(int personId)
{
// pull required model data from databases
var db = DBParentContext;
Mother = db.Parent.Where(m => m.ChildId == personId)
Father = db.Parent.Where(m => m.ChildId == personId)
}
}
Controller
public class PersonController : Controller
{
[HttpPost]
public ActionResult Edit(PersonViewModel viewModel)
{
viewModel.DetailsModel = new PersonDetailsModel(viewModel.Id)
if (ModelState.IsValid) {
// ~
}
return View(viewModel)
}
}
View
#model PersonViewModel
#Html.DisplayFor(m => m.DetailsModel.Mother)
#Html.DisplayFor(m => m.DetailsModel.Father)
#Html.TextBoxFor(m => m.FirstName)
#Html.TextBoxFor(m => m.LastName)
Since details like your Mother are un-editable then they're not really part of the "Edit" model, so I'd box like that away and try to let something else take care of them.
If you aren't going to update the Parent field, then it really doesn't matter if it's a hidden or not, since you won't update it on post.
I would use the hidden in this case, just make sure not to update that field.