0

I use Entity Framework in asp.net MVC 5 (vb.net) to store some data including an image (png). The images can be saved to the database as Byte(), but as soon I edit any existing entity without uploading a new image, the stored image is deleted. So I tried this and wrote following code in the edit controller:

       If ModelState.IsValid Then
            If Request.Files("Image").ContentLength <> 0 Then
                dbModel.Image = dbModel.ImageToByte(Request.Files("Image")) 'Function to create Byte() --> this works
            Else
                dbModel.Image = db.Content.Find(dbModel.ID).Image 'This line is responsible for the exception
            End If
            db.Entry(dbModel).State = EntityState.Modified  'Here the exception occours!
            db.SaveChanges()
            Return RedirectToAction("Index")
        End If

In the line, where the EntityState is set to Modified, an exception occured:

An exception of type 'System.InvalidOperationException' occurred in EntityFramework.dll but was not handled in user code

Additional information: Attaching an entity of type 'myDatabase.dbModel' 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.

I guess, that the line where the existing image is loaded from the database is responsible for the exception, but I really don't know what to do.

Community
  • 1
  • 1
roland
  • 900
  • 12
  • 25
  • You can probably remove the whole line `db.Entry(dbModel).State = EntityState.Modified` because I assume `dbModel` is already tracked, so EF will store its changes when you modify it. But did you ever consider table splitting? – Gert Arnold Feb 16 '15 at 21:09
  • @GertArnold: Thank you for the hint with the splitted table. I tried to remove this line, but now the database is not updated. – roland Feb 17 '15 at 09:28

2 Answers2

1

Use view models. The problem you're having is occurring because the Image field is posted back as null, since no new image was set, so when you update the entity, it sets Image to that null value. In effect, by not posted a value of Image, you are literally telling Entity Framework that you want to null this value out.

By using a view model, you can only deal with the fields you need to in your view, and then map the posted values back onto your entity. That means properties like Image would still have their value as retrieved from the database. For example:

public class FooViewModel
{
    // other properties here

    public HttpPostedFileBase Image { get; set; }
}

Then in your action:

public ActionResult Edit(int id, FooViewModel model)
{
    var foo = db.Foos.Find(id);
    if (foo == null)
    {
        return new HttpNotFoundResult();
    }

    // Map properties over
    // foo.Bar = model.Bar;
    if (model.Image != null && model.Image.ContentLength > 0)
    {
        foo.Image = foo.ImageToByte(model.Image);
    }

    ...
}
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • Thank you. I have tried this approach, because a view model seems to be better practice [than exposing the database model](http://www.stevefenton.co.uk/Content/Blog/Date/201303/Blog/Why-You-Never-Expose-Your-Domain-Model-As-Your-MVC-Model/). But at the end, when the entry is updated, I run into the same exception. – roland Feb 19 '15 at 15:00
0

Finally I ended up with this solution.

Function Edit(<Bind(Include:="ImageFile,Image")> ByVal Model As dbModel, ByVal ImageFile As HTTPPostedFileBase) As ActionResult
    If ModelState.IsValid Then
        Dim Current As dbModel = db.Content.Find(Model.ID)  'Get the original entry.
        If Not ImagFile is Nothing Then
            Model.Image = Model.ImageToByte(ImageFile) 'Function to create Byte()
        Else
            Model.Image = db.Content.Find(Model.ID).Image
        End If
        'db.Entry(dbModel).State = EntityState.Modified  'Here the exception occoured before.
        db.Entry(Current).CurrentValues.SetValues(Model) 'This line is new and solved the problem!
        db.SaveChanges()
        Return RedirectToAction("Index")
    End If
    Return View(Model)
End Function

For the sake of completeness, here's the code to convert HTTPPostedFileBase to Byte():

Public Function ImageToByte(ByVal Img As HttpPostedFileBase) As Byte()
    Dim Image As New MemoryStream
    Img.InputStream.CopyTo(Image)
    Return Image.ToArray()
End Function
roland
  • 900
  • 12
  • 25