0

i have a User Profile Page that i want that user to update only some fields and cannot change his email or signed up date. but when i want to post that i get a validation error in those fields that are not in my view. by the way i have CUD in separate .cs . my EF models are in DomainModels folder and i override them with another model in EntityModels folder(that has the Validations). here is the codes:

Controller:

[HttpPost]
public ActionResult UserProfile(Merchant merchant)
{
    MerchantRepository blMerchant = new MerchantRepository();

    if (ModelState.IsValid)
    {
        if (blMerchant.Update(merchant))
        {
            return RedirectToAction("UserProfile");
            //return MessageBox.Show("Product Edited Successfuly", MessageType.Success);
        }
        else
        {
            return MessageBox.Show(ModelState.GetErrors(), MessageType.Alert);
        }
    }
    else
    {
        return MessageBox.Show(ModelState.GetErrors(), MessageType.Warning);
    }
}

View:

@using (Html.BeginForm("UserProfile", "Merchant", FormMethod.Post))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)
    @Html.HiddenFor(model => model.id)

    <label for="account-fn">Full Name</label>
    @Html.TextBoxFor(model => model.Name, new { @class="form-control form-control-rounded" , @required="required"})
    @Html.ValidationMessageFor(model => model.Name)

    <label for="account-email">Phone</label>
    @Html.TextBoxFor(model => model.Phone, new { @class = "form-control form-control-rounded", @required = "required" })
    @Html.ValidationMessageFor(model => model.Phone)

    <label>DOB</label>
    @Html.TextBoxFor(model => model.Dob, new { @class = "form-control form-control-rounded" })
    @Html.ValidationMessageFor(model => model.Dob)

    @Html.CheckBoxFor(model => model.NewsLetter, new { @class = "input"})

    <button class="btn btn-sm btn-primary m-r-5">Update Profile</button>
}

Merchant Repository

public bool Update(MySite.Models.DomainModels.Merchant entity, bool autoSave = true)
{
    try
    {
        db.Merchants.Attach(entity);
        db.Entry(entity).State = System.Data.Entity.EntityState.Modified;
        if (autoSave)
            return Convert.ToBoolean(db.SaveChanges());
        else
            return false;
    }
    catch
    {
        return false;
    }
}

Merchant Meta Data

internal class MerchantMetaData
{
    [ScaffoldColumn(false)]
    [Bindable(false)]
    public int id { get; set; }

    [Required(ErrorMessage = "Please Enter Your Name", AllowEmptyStrings = false)]

    [StringLength(50, ErrorMessage = "Please Enter Shorter Name")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Please Enter your Email Address", AllowEmptyStrings = false)]
    public string Email { get; set; }

    [ScaffoldColumn(false)]
    public bool EmailConfirmed { get; set; }

    [Required(ErrorMessage = "Please Enter your Password", AllowEmptyStrings = false)]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    [ScaffoldColumn(false)]
    public int MembershipId { get; set; }

    [ScaffoldColumn(false)]
    [DataType(DataType.DateTime)]
    public System.DateTime SignupDate { get; set; }

and in namespace MySite.Models.DomainModels

[MetadataType(typeof(MySite.Models.EntityModels.MerchantMetaData))]
public partial class Merchant
{
    [Compare("Password", ErrorMessage = "Confirm Password Doesn't Match The Field Above, Type Again !")]
    [DataType(DataType.Password)]
    public string ConfirmPassword { get; set; }
}

Update

I Made a class with those fileds only and maped it as @StephenMuecke said but still getting validation error:

[HttpGet]
    public ActionResult UserProfile()
    {
        MerchantRepository blMerchant = new MerchantRepository();
        var username = User.Identity.Name;
        var UserData = blMerchant.Where(x => x.Email == username).SingleOrDefault();
        var user=new UserVM();
        user.id = UserData.id;
        user.Name = UserData.Name;
        user.Dob = DateTime.Now;
        user.NewsLetter = UserData.NewsLetter;
        user.Gender = UserData.Gender;
        user.Phone = UserData.Phone;
        return View(user);
    }

[HttpPost]
    public ActionResult UserProfile(UserVM user)
    {
        MerchantRepository blMerchant = new MerchantRepository();
        var username = User.Identity.Name;
        var merchant = blMerchant.Where(x => x.Email == username).SingleOrDefault();
        merchant.id = user.id;
        merchant.Name = user.Name;
        merchant.Dob = user.Dob;
        merchant.NewsLetter = user.NewsLetter;
        merchant.Gender = user.Gender;


        if (ModelState.IsValid)
        {
            if (blMerchant.Update(merchant))
            {
                return RedirectToAction("UserProfile");
                //return MessageBox.Show("Product Edited Successfuly", MessageType.Success);
            }
            else
            {
                return MessageBox.Show(ModelState.GetErrors(), MessageType.Alert);
            }
        }
        else
        {
            return MessageBox.Show(ModelState.GetErrors(), MessageType.Warning);
        }
    }

UserVM

public class UserVM
{ 
    public int id { get; set; }
    public string Name { get; set; }
    public string Password { get; set; }
    public string Phone { get; set; }
    public DateTime Dob { get; set; }
    public string Gender { get; set; }
    public bool NewsLetter { get; set; }
}

ERROR ------->

System.Data.Entity.Validation.DbEntityValidationException was caught
  HResult=-2146232032
  Message=Validation failed for one or more entities. See 'EntityValidationErrors' property for more details.
  Source=EntityFramework
  StackTrace:
       at System.Data.Entity.Internal.InternalContext.SaveChanges()
       at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
       at System.Data.Entity.DbContext.SaveChanges()
       at MySite.Models.Repositories.MerchantRepository.Update(Merchant entity, Boolean autoSave) in d:\MySite\MySite\Models\Repositories\MerchantRepository.cs:line 59
  InnerException: 
shaaraa
  • 59
  • 10
  • The correct approach is to use a view model (which contains only the properties you need in the view, and then you map the data model to the view model in the GET method and vice versa in the POST method - [What is ViewModel in MVC?](https://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) –  Oct 14 '18 at 08:04
  • @StephenMuecke thank you so much,im new to mvc and is it possible to tell me exactly what should i do with this code above? – shaaraa Oct 14 '18 at 08:17
  • Read my first comment and the link :) –  Oct 14 '18 at 08:19
  • @StephenMuecke it was really helpfull :) but could u help me more :( just with my code. should i change my Update repository too? i mean my Update Method – shaaraa Oct 14 '18 at 09:01
  • There appears to be no need to change that (its dealing with saving your data model) –  Oct 14 '18 at 09:36
  • @shaaraa can you add a snapshot of error which you are getting. – Saineshwar Bageri - MVP Oct 14 '18 at 10:53
  • @Saineshwar it passes the modelstate.valid and stuck in try catch and gives me this error https://ibb.co/csUTdp – shaaraa Oct 14 '18 at 11:15
  • @shaaraa can you post exception by clicking on "copy exception details to the clipboard" – Saineshwar Bageri - MVP Oct 14 '18 at 11:59
  • @Saineshwar i copied it and paste it in question :) – shaaraa Oct 14 '18 at 12:40
  • catch (DbEntityValidationException e) { foreach (var eve in e.EntityValidationErrors) { Console.WriteLine("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:", eve.Entry.Entity.GetType().Name, eve.Entry.State); foreach (var ve in eve.ValidationErrors) { Console.WriteLine("- Property: \"{0}\", Error: \"{1}\"", ve.PropertyName, ve.ErrorMessage); } } throw; } – Saineshwar Bageri - MVP Oct 14 '18 at 12:47
  • add this code in the catch block and check it will give you an accurate error. – Saineshwar Bageri - MVP Oct 14 '18 at 12:54
  • @Saineshwar it gave the same error no additional description. – shaaraa Oct 14 '18 at 12:54
  • @shaaraa key fields to check is "DOB","id" and "NewsLetter " are they null while you are updating data. – Saineshwar Bageri - MVP Oct 14 '18 at 13:01
  • @shaaraa have you check the above fields. – Saineshwar Bageri - MVP Oct 14 '18 at 13:09
  • @Saineshwar no but Dob is Nullable datetime , and Id is in html.hidden in view and they are not null. – shaaraa Oct 14 '18 at 13:09

1 Answers1

1

Remove this property from Entity and apply it on ViewModel. While updating data you are only updating a few fields, not a whole entity.

internal class MerchantMetaData 
{
    [ScaffoldColumn(false)]
    [Bindable(false)]
    public int id { get; set; }

    [Required(ErrorMessage = "Please Enter Your Name", AllowEmptyStrings = false)]

    [StringLength(50, ErrorMessage = "Please Enter Shorter Name")]
    public string Name { get; set; }

    [Required(ErrorMessage = "Please Enter your Email Address", AllowEmptyStrings = false)]
    public string Email { get; set; }

    [ScaffoldColumn(false)]
    public bool EmailConfirmed { get; set; }

    [Required(ErrorMessage = "Please Enter your Password", AllowEmptyStrings = false)]
    [DataType(DataType.Password)]
    public string Password { get; set; }

    [ScaffoldColumn(false)]
    public int MembershipId { get; set; }

    [ScaffoldColumn(false)]
    [DataType(DataType.DateTime)]
    public System.DateTime SignupDate { get; set; }
}

Do Not update entire Entity Only update Property which you are going to update.

public bool Update(MySite.Models.DomainModels.Merchant entity, bool autoSave = true)
{
    try
    {
        db.Entry(vR).Property(x => x.Name).IsModified = true;
        db.Entry(vR).Property(x => x.Phone).IsModified = true;
        db.Entry(vR).Property(x => x.Dob).IsModified = true;
        db.Entry(vR).Property(x => x.NewsLetter).IsModified = true;

        if (autoSave)
            return Convert.ToBoolean(db.SaveChanges());
        else
            return false;
            else
        return false;
    }
    catch
    {
        return false;
    }
}
Saineshwar Bageri - MVP
  • 3,851
  • 7
  • 41
  • 47