0

I am struggling to implement a simple 'update profile' functionality(learning purposes). I simply want to be able not to update the profile image everytime I update a give profile. When the picture is there and some other part of the profile is update I want the picture to stay the same.

I came up with the following code for this :

Controller :

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "UserDetailsId,ImageData,FirstName,LastName,UserAddress,UserCountry,UserPostalCode,UserPhoneNumber,CompanyId,identtyUserId")] UserDetails userDetails, HttpPostedFileBase UploadImage)
    {
        if (ModelState.IsValid)
        {
            if (UploadImage!=null) {
                byte[] buf = new byte[UploadImage.ContentLength];
                UploadImage.InputStream.Read(buf, 0, buf.Length);
                userDetails.ImageData = buf;
            }
            else {
                var userFromDb = db.UsersDetails.Where(u => u.identtyUserId == userDetails.identtyUserId).First();//i am getting the old user data
                userDetails.ImageData = userFromDb.ImageData; //saving the image to the modified state
            }
            db.Entry(userDetails).State = EntityState.Modified;//error here
            db.SaveChanges();
            return RedirectToAction("Index");

        }
        //ViewBag.CompanyId = new SelectList(db.Companies, "CompanyId", "CompanyName", userDetails.CompanyId);
        return View(userDetails);

The error I am getting on this row db.Entry(userDetails).State = EntityState.Modified; is the following :

Attaching an entity of type 'eksp.Models.UserDetails' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate.

The model :

public class UserDetails
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int UserDetailsId { get; set; }
    public byte[] ImageData { get; set; }
    [NotMapped]
    public HttpPostedFileBase UploadImage { get; set; }
    [NotMapped]
    public string ImageBase64 => System.Convert.ToBase64String(ImageData);
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserPhoneNumber { get; set; }
    public int CompanyId { get; set; }
    public virtual Company Company { get; set; }
    public string identtyUserId { get; set; }
    public virtual ICollection<WorkRolesUsersDetails> WorkRolesUsersDetails { get; set; }

Although it may looke pretty self explenatory for me it is not clear this is happening?

Can somebody guide me how to achieve what I want to achieve?

Thanks!

Robert Ross
  • 1,151
  • 2
  • 19
  • 47

2 Answers2

3

When you update an entity, you should map the posted values onto an instance pulled from the database, rather than trying to directly save the instance created from the post data. This is yet another reason to avoid using Bind as it confuses the issue and makes developers who don't know better think it's okay to directly save the entity created from the post data. It's not, and never is.

Instead, use something like UserDetailsId to lookup the entity:

var userDetails = db.UserDetails.Find(model.UserDetailsId);

Where model is the parameter from your action. Then, you can map over the posted values onto your entity:

userDetails.FirstName = model.FirstName;
// etc.

Finally, save usersDetails, which is now the version from the database, with all the original data on the entity, modified to the posted data, where appropriate.

Now, given that you need to do this mapping over of the posted data anyways, go a step further can create a view model with just the properties you need to allow the user to modify. You can then post to that, instead of using Bind. Really, Bind is just awful. It's one of those things Microsoft hastily adds because they think it solves one problem, and it actually ends up causing ten other problems.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
2

You can retrieve data entity from the db in any case and update it with the details coming as part of the Post Model and Save that data entity back to the db.

var userFromDb = db.UsersDetails.Where(u => u.identtyUserId == userDetails.identtyUserId).First();
if (UploadImage!=null) 
{
    byte[] buf = new byte[UploadImage.ContentLength];
    UploadImage.InputStream.Read(buf, 0, buf.Length);
    userFromDb.ImageData = buf;
}
userFromDb.FirstName = userDetails.FirstName;
userFromDb.LastName = userDetails.LastName;
userFromDb.UserAddress = userDetails.UserAddress;
userFromDb.UserCountry = userDetails.UserCountry;
userFromDb.UserPostalCode = userDetails.UserPostalCode;
userFromDb.UserPhoneNumber = userDetails.PhoneNumber;
userFromDb.CompanyId = userDetails.CompanyId;
db.SaveChanges();

This should help you to achieve the feature you want.

Chetan
  • 6,711
  • 3
  • 22
  • 32
  • in this case I can modify the record only if i modify the image itself, which is not what i am looking for :( – Robert Ross Feb 10 '17 at 15:06
  • As per the code you posted only image property of the object is being changed? Is there anything else you are doing with the object which forces you to retrieve the data from the db? Little bit more information about what's your requirement would help to provide appropriate solution. – Chetan Feb 10 '17 at 15:07
  • i added the model class to the question. Basically with the controller code I posted I can change all the properties and it always works. The problem is that when i don't add an image it saves null to the database and this brakes my app. What I want to achive is to be able to edit and save the rest of the fields without having to upload an image everytie when i edit. Basically I want to be able to update the image field whenever I want and not every time. – Robert Ross Feb 10 '17 at 15:15
  • I updated my answer. I hope this will help you to resolve the issue. – Chetan Feb 10 '17 at 15:19