roland
roland

Reputation: 900

Edit entity in database and reuse stored image

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.

Upvotes: 0

Views: 735

Answers (2)

roland
roland

Reputation: 900

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

Upvotes: 0

Chris Pratt
Chris Pratt

Reputation: 239300

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);
    }

    ...
}

Upvotes: 1

Related Questions