So, I have a problem I am curious about. I have a UserAccountViewModel that I am reusing for create account view and Edit Account view. So that I can use one view and one viewmodel for my code but different controller actions.
On Create Account, UserName, Password and Confirm Password are all required fields. However, on Edit Account, those fields are not required fields (I hide those fields in my view on Edit Account).
The problem is when I do this:
[HttpPost]
public ActionResult EditUserAccount_Save(UserAccountViewModel editUserAccountViewModel)
{
var errors = ModelState.Values.SelectMany(v => v.Errors);
if (ModelState.IsValid)
{
editUserAccountViewModel.UserName = UserSession.GetValue(StateNameEnum.UserName, StateNameEnum.UserName.ToString()) as string;
db_user user = new db_user();
user.Title = editUserAccountViewModel.Title;
user.FirstName = editUserAccountViewModel.FirstName;
user.LastName = editUserAccountViewModel.LastName;
user.PhoneNumber = editUserAccountViewModel.PhoneNumber;
user.AltPhoneNumber = editUserAccountViewModel.AltPhoneNumber;
user.EmailAddress = editUserAccountViewModel.EmailAddress;
user.LanguageId = context.languages.Where(t => t.Code == editUserAccountViewModel.Language).Select(t => t.Id).FirstOrDefault();
user.CreatedDate = DateTime.Now;
user.UserId = WebSecurity.GetUserId(editUserAccountViewModel.UserName);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
JsonResult res = Json(new { Success = true, data = "", Message = "" });
return res;
}
JsonResult res2 = Json(new { Success = false, data = "", Message = "" });
return res2;
}
My ModelState.IsValid returns false and I added the errors statement above and it says:
It's because UserName, Password and Confirm Password fields are required. How can I reuse my viewmodel so that I can those fields required when I'm creating an account but not required on editing an account? That is, I want to keep those fields are the same (not editable in edit account?)
You could output some dummy values into hidden fields in the view so they won't be null when they come back in, however, that is more data being pushed back and forth.
Secondly, you can remove the keys from the modelstate and thus remove their errors.
ModelState.Remove("UserName");
ModelState.Remove("Password");
ModelState.Remove("ConfirmPassword");
After doing that, when you call ModelState.IsValid, it will give the answer based only on the fields you are interested in
HTH
Related
I've got a project I've inherited and I've recently just started working with RavenDb. I'm needing to save a document to one database that already has a connection with, but I need to save that document to a second RavenDb. Just wondering how I would go about that? Below is the method I would need to alter.
[HttpPost]
public ActionResult SaveContact(ContactInput input)
{
var id = getId();
var profile = RavenSession.Load<TechProfile>(id) ?? new TechProfile();
input.MapPropertiesToInstance(profile);
// check for existing user
if (RavenSession.Query<TechProfile>().Any(x => x.Email == profile.Email && x.Id != profile.Id))
{
return Json(new {error = "Profile already exists with that email address.", msg = "Error"});
}
RavenSession.Store(profile);
return Json(new {error = "", msg = "Success", id = profile.Id.Substring(profile.Id.LastIndexOf("/") + 1)});
}
You need to create a second session and point it toward the second database.
Entities aren't tied to a particular session in RavenDB, so you can do that.
I am trying to teach myself asp.net.mvc and I started a project using the default template for MVC.
I changed the template a little, so when a register with the default action method I add the newly registered user id in another table called companies.
Basically, I added a new text field in the front end. When a user registers he types a number there. In the backend I look for a company from the companies table, which ID matches the number from the fronted. If there is a match I want to save the current user ID to a column in the companies table called CAId
Here is the whole action method (i made my modifications after the await):
public async Task<ActionResult> RegisterCA(RegisterCAEViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var currentUser = UserManager.FindByName(user.UserName);
var roleresult = UserManager.AddToRole(currentUser.Id, "CompanyAdministrator");
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
var company = db.Companies.Find(model.CompanyCode);
// find the company by the id passed in the frontend.
if (company!=null)
{
company.CAId = currentUser.Id;
//set the id
db.Entry(company).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
return View(model);
}
Here is what I tried in the method above :
var company = db.Companies.Find(model.CompanyCode);
// find the company by the id passed in the frontend.
if (company!=null)
{
company.CAId = currentUser.Id;
//set the id
db.Entry(company).State = EntityState.Modified;
db.SaveChanges();
When I register a user I do not get any errors. The user is registered, but the CAId field is not updated with the newly registered user ID.
Please, can somebody help and explain! I will be very grateful as I've been stuck with this for hours :(.
Try moving this line :
company.CAId = currentUser.Id;
After the line
db.SaveChanges();
The the currentauser id is an identity column it will be set only after it was saved to the db.
For anyone who will look for an answer to a similar question in future - the code in my question works absolutely fine. The problem was that in the view I was using the default Register action method and not the modified above! Thanks to all the people, who tried to help!
my guess, you need to attach your entity to the context underlying the set.
dbSet.Attach(company)
and then
context.Entry(company).State = EntityState.Modified;
db.SaveChanges();
The issue I am experiencing is I am modifying my model in the post action, instead of having it filled in through the form (the fields are a password hash and password salt) for obvious reasons. When post to the action, obviously the password hash and salt are computed values not user entered. The issue is, if I generate them and assign the values to my posted customer model, model state still says they are required even though the properties have values. See code below. This is my register action.
[HttpGet]
public ActionResult Register()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(Customer customer)
{
var password = Request.Form.Get("password");
var ConfirmPassword = Request.Form.Get("confirmpassword");
if ((password != null && ConfirmPassword != null) && (!string.IsNullOrWhiteSpace(password)
&& !string.IsNullOrWhiteSpace(ConfirmPassword)) && password == ConfirmPassword)
{
//generate a password salt
var passwordsalt = Models.Helpers.PasswordHasher.GetSalt();
//convert it into a string that can be used again by calling the Convert.FromBase64String(string); function on what will be stored
customer.PasswordSalt = Convert.ToBase64String(passwordsalt);
//compute the password hash here and store it in the customer
customer.PasswordHash = Models.Helpers.PasswordHasher.ComputeHash(password, "SHA256", passwordsalt);
}
else if (!Models.Helpers.ValidationLibrary.ValidatePasswordRequirements(password))
{
ModelState.AddModelError("", "Password must be 8 characters long, have at least one number or symbol");
}
else
{
ModelState.AddModelError("", "Password and confirm password do not match");
}
if (ModelState.IsValid)
{
//db.Customers.Add(customer);
//db.SaveChanges();
UserRegistration regularUser = new UserRegistration();
regularUser.customer = customer;
regularUser.role = new XREF_CustomerRole { Role_ID = 3, Customer_ID = customer.Customer_ID };
Models.Helpers.Helper.createUser(regularUser);
return Login(new UserLogin { Email = customer.Email, Password = customer.PasswordHash, RememberMe = false });
}
return View(customer); ;
}
Here is a screenshot of the values.
And here is a screenshot of the values in model state
Along with their corresponding keys
That's normal and it is how HTML helpers work. They first use the value of the POST request and after that the value in the model. This means that even if you modify the value of the model in your controller action if there is the same variable in the POST request your modification will be ignored and the POSTed value will be used.
ASP.Net MVC Html.HiddenFor with wrong value
I'm just getting into mvc 4 (and mvc in general) and am just wondering is this action code ok or should it be stripped down again?
[HttpPost]
public ActionResult Index(DashboardViewModel dbModel)
{
//retrieve latest resident order
var residentOrder = db.ResidentOrders.GetById(dbModel.ResidentOrderID);
if (residentOrder == null)
{
var order = db.Orders.GetById(dbModel.OrderID);
var user = db.Users.GetUserByUsername(User.Identity.Name);
residentOrder = new ResidentOrder()
{
CreatedDate=DateTime.Now,
LastUpdateDate = DateTime.Now,
Litres=0,
Customer = user
};
order.ResidentOrders.Add(residentOrder);
db.Commit();
}
//check to see if value has changed
if (!dbModel.ResidentLitresOrdered.Equals(residentOrder.Litres))
{
//get new ordered value
residentOrder.Litres = dbModel.ResidentLitresOrdered;
db.Commit();
//send an email just to notify in writing of the change.
SendOwnOrderQtyUpdateNotification();
}
return View(dbModel);
}
Basically if a resident order doesnt exist then we create one, this is the only place in the system where this would need to happen.
Should I still be stripping that code out into my repositories?
db is my IUnitOfWork btw
I would recommend that you create a "repository" to hide the details from the Controller action.
An "Upsert" method would allow this to be clearly and elegantly implemented hiding the details from the controller.
I'm developing a web app based on Orchard.
I'm coding a module that manages Staff Users, this users are ContentTypes(Staff_User) composed of UserPart and StaffUserPart (Custom part, defined in a migration) -> this part has a MediaPickerField.
This is the code in my controller to show the creation template of a staff users
public ActionResult CreateStaff() {
IContent staffUser = _contentManager.New("Staff_User");
var model = _contentManager.BuildEditor(staffUser);
return View((object)model);
}
Ok, I have a template in EditorTemplates/Staff.cshtml . The MediaPicker field is attached by the the BuildEditor function (as a shape).
This is the Post controller:
public ActionResult CreateStaffPost(FormCollection input) {
IContent staffUser = _contentManager.New("Staff_User");
//UserPart validation
if (String.IsNullOrEmpty(input["user.Email"]))
ModelState.AddModelError("Email", "The Email field is required.");
//Check if user already exits
var oldUser = _contentManager.Query("User").Where<UserPartRecord>(x => x.Email == input["user.Email"])
.List()
.FirstOrDefault();
if (oldUser != null)
ModelState.AddModelError("Email", "That email adress is already registered.");
if (!ModelState.IsValid) {
var model = _contentManager.UpdateEditor(staffUser, this);
return View(model);
}
StaffUserPart staff = staffUser.As<StaffUserPart>();
staff.FirstName = input["FirstName"];
staff.LastName = input["LastName"];
staff.Location = input["Location"];
staff.JobTitle = input["JobTitle"];
staff.Summary = input["Summary"];
staff.AreaOfExpertise = input["AreaOfExperience"];
staff.Category = input["Category"];
staff.Experience = input["Experience"];
//Media picker field values
var staffImageField = (MediaPickerField)staff.Fields.Single(x => x.Name == "Photo");
//TODO Fix image save during creation
staffImageField.Url = input["StaffUserPart.Photo.Url"];
staffImageField.AlternateText = input["StaffUserPart.Photo.AlternateText"];
staffImageField.Class = input["StaffUserPart.Photo.Class"];
staffImageField.Style = input["StaffUserPart.Photo.Style"];
staffImageField.Alignment = input["StaffUserPart.Photo.Alignment"];
staffImageField.Width = String.IsNullOrEmpty(input["StaffUserPart.Photo.Width"]) ? 0 : Convert.ToInt32(input["StaffUserPart.Photo.Width"]);
staffImageField.Height = String.IsNullOrEmpty(input["StaffUserPart.Photo.Height"]) ? 0 : Convert.ToInt32(input["StaffUserPart.Photo.Height"]);
UserPart userPart = staffUser.As<UserPart>();
userPart.UserName = input["user.Email"];
userPart.Email = input["user.Email"];
userPart.NormalizedUserName = input["user.Email"].ToLowerInvariant();
userPart.Record.HashAlgorithm = "SHA1";
userPart.RegistrationStatus = UserStatus.Approved;
userPart.EmailStatus = UserStatus.Approved;
//Set Password
_membershipService.SetPassword(userPart.As<UserPart>(), input["password"]);
//Create the StaffUser
_contentManager.Create(staffUser);
return RedirectToAction("Index");
}
Question
This works but the MediaPickerField doesn;t save the data. I use the debugger to see if the values from input["StaffUserPart.Photo"] and the values are there.
Any ideas?
It looks like you're doing more work than you need to. If you move your call to UpdateEditor, this method will do the work of putting posted values into your content. You'll need to make sure you're implementing IUpdater. Also, I added a dependency on ITransactionManager. I'm hoping this will help catch something not getting put in the right spot.
public ActionResult CreateStaffPost(FormCollection input) {
IContent staffUser = _contentManager.New("Staff_User");
//Create the StaffUser
_contentManager.Create(staffUser);
//UserPart validation
if (String.IsNullOrEmpty(input["user.Email"]))
ModelState.AddModelError("Email", "The Email field is required.");
//Check if user already exits
var oldUser = _contentManager.Query("User").Where<UserPartRecord>(x => x.Email == input["user.Email"])
.List()
.FirstOrDefault();
if (oldUser != null)
ModelState.AddModelError("Email", "That email adress is already registered.");
//This does all the work of hydrating your model
var model = _contentManager.UpdateEditor(staffUser, this);
if (!ModelState.IsValid) {
_transactionManager.Cancel();
return View(model);
}
//Set Password
_membershipService.SetPassword(userPart.As<UserPart>(), input["password"]);
return RedirectToAction("Index");
}