Reputation: 43
I'm reading Pro ASP.NET MVC 3 Framework by Apress. I'm following the example of uploading and displaying images. The problem is that it works fine when uploading images to products, but if I later want to edit the description for example and then saving the product the image disappears. I understand that the problem is that when saving the product I'm not passing the image data because the image upload is empty, and the context.SaveChanges() saves every data field, including the empty image data fields.
I'm stuck and I would really appreciate if someone could help me!
This is a part of the edit page:
<label>Image</label>
if (Model.ImageData == null)
{
@:Null
}
else
{
<img id="imageFile" runat="server" src="@Url.Action("GetImage", "Product", new { Model.Name })" />
}
<label>Upload image:</label>
<input type="file" name="Image" runat="server" />
When updating:
public ActionResult Edit(Product product, HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
if (image != null && image.ContentLength > 0)
{
product.ImageMimeType = image.ContentType;
product.ImageData = new byte[image.ContentLength];
image.InputStream.Read(product.ImageData, 0, image.ContentLength);
}
repository.SaveProduct(product);
TempData["message"] = string.Format("{0} har sparats", product.Name);
return RedirectToAction("Index");
}
else
{
return View(product);
}
}
Saving the product:
public void SaveProduct(Product product)
{
if (product.ProductID == 0)
{
context.Products.Add(product);
}
else
{
context.Entry(product).State = EntityState.Modified;
}
int result = context.SaveChanges();
}
Upvotes: 4
Views: 2294
Reputation: 127
I had a similar problem. When I added @Html.HiddenFor(model => model.Image)
to my edit view, it passed the imageURL perfectly.
Upvotes: 0
Reputation: 2638
If you try the second option provided by @nemesv and your entity classes are in a different namespace relative to your DbContext then you will experience an exception raised by the entity framework as explained here:
entry.GetDatabaseValues() throw EntitySqlException
Type 'XXX' could not be found. Make sure that the required schemas are loaded and that the namespaces are imported correctly.
It appears there is a work around but its not pleasant. It does allow the image to stay associated to the product though.
Here is how I did it:
if (product.ImageData == null)
{
// Get Product Context reference
var productContext = context.Entry(product);
productContext.State = EntityState.Modified;
// Copy client values and reload orignal product values from DB.
var clientValues = productContext.CurrentValues.Clone().ToObject();
productContext.Reload();
productContext.CurrentValues.SetValues(clientValues);
// Get database values (Original)
var currentValues = productContext.Entity;
var databaseValues = (Product)productContext.OriginalValues.ToObject();
// Change image properties to match original values
productContext.Entity.ImageData = databaseValues.ImageData;
productContext.Entity.ImageMimeType = databaseValues.ImageMimeType;
}
Upvotes: 0
Reputation: 139758
Your understanding of the problem is correct: when you mark your Product
as EntityState.Modified
EF marks all of its properties modified. So when your current Product
is coming from the controller and it doesn't have an image EF removes it from the DB when calling SaveChanges()
.
I see two options:
You load the original Product
and just update the needed properties instread of using EntityState.Modified
:
var productInDb = context.Products.Find(product.Id);
productInDb.Name = product.Name;
productInDb.Description = product.Description;
if (product.ImageData !=null )
{
productInDb.ImageData = product.ImageData;
productInDb.ImageMimeType = product.ImageMimeType;
}
In this case you have to manually set every property on the Product
.
After you have marked your Product
as Modified you re-set the image values from the db:
context.Entry(product).State = EntityState.Modified;
if (product.ImageData == null)
{
var databaseValues = context.Entry(product).GetDatabaseValues();
product.ImageData = (byte[])databaseValues["ImageData"];
product.ImageMimeType = (string)databaseValues["ImageMimeType"];
}
I this case you only need to re-set the image related properties.
With both implementation there is no way to remove the ImageData during update.
Upvotes: 4