I have a small demo application to work around Binary data stored in the database. See below for related code:
Entity Class:
[HiddenInput(DisplayValue = false)]
public int Id { get; set; }
[Display(Name = "Full Name:")]
public string Name { get; set; }
[DataType(DataType.Upload)]
[Display(Name = "Photo:")]
public byte[] ImageData { get; set; }
[HiddenInput(DisplayValue = false)]
public string ImageMimeType { get; set; }
Edit Action:
[HttpGet]
public ActionResult Edit(int id)
{
var mensPlayer = _dataSource.MensPlayers.FirstOrDefault(p => p.Id == id);
return View(mensPlayer);
}
[HttpPost]
public ActionResult Edit(MensPlayer mensPlayer, HttpPostedFileBase image)
{
if (ModelState.IsValid)
{
//Added line below
_dataSource.ImageTemp(mensPlayerInDb, mensPlayer);
if (image != null)
{
mensPlayer.ImageMimeType = image.ContentType;
mensPlayer.ImageData = new byte[image.ContentLength];
image.InputStream.Read(mensPlayer.ImageData, 0, image.ContentLength);
}
//Added line below
mensPlayer.ImageData = mensPlayerInDb.ImageData;
//Save Player
_dataSource.Update(mensPlayer);
_dataSource.Save();
TempData["message"] = string.Format("{0} has been saved", mensPlayer.Name);
return RedirectToAction("Detail", "MensPlayer", new {id = mensPlayer.Id});
}
return View(mensPlayer);
}
GetImage Method:
public FileContentResult GetImage(int id)
{
var image = _dataSource.MensPlayers.FirstOrDefault(p => p.Id == id);
if (image != null)
{
return File(image.ImageData, image.ImageMimeType);
}
return null;
}
Display Image:
@if (Model.ImageData != null) {
<div >
<img width="300" height="400" src="@Url.Action("GetImage", "MensPlayer",
new { Model.Id })" alt="Player Image"/>
</div>
}
DataSource class
public interface IDataSource
{
IQueryable<MensTeam> MensTeams { get; }
IQueryable<MensPlayer> MensPlayers { get; }
IQueryable<MensHome> MensHomes { get; }
void Save();
void Update(MensPlayer mensPlayer);
void Delete();
void ImageTemp(MensPlayer mensPlayerInDb, MensPlayer mensPlayer);//I added this
}
In Db class
void IDataSource.Save()
{
SaveChanges();
}
void IDataSource.Update(MensPlayer mensPlayer)
{
Entry(mensPlayer).State = EntityState.Modified;
}
//Added code below
void IDataSource.ImageTemp(MensPlayer mensPlayerInDb, MensPlayer mensPlayer)
{
Entry(mensPlayerInDb).CurrentValues.SetValues(mensPlayer);
}
My problem is, every time i try to edit a player, every data is nicely retrieved from the database but when i hit save after editing, the ImageData is lost, more like replacing the original data with null value. It's like, the application expects me to re-upload the image at every edit i attempt.
What can i do to clean this up?
Well, if image
is null
then in your entity mensPlayer.ImageData
is null
. Later when you call _dataSource.Update(mensPlayer)
you set the state of the entity to Modified
(at least I guess that Update
just sets the state to Modified
), so you are telling EF that your have modified the image too (namely set it to null
) and EF will save that change.
In order to solve the problem you can either load the entity including the image from the database first...
var mensPlayerInDb = context.MensPlayers.Find(mensPlayer.Id);
mensPlayer.ImageData = mensPlayerInDb.ImageData; // save the ImageData
// copy changed values to loaded entity and save it, ImageData is unchanged
context.Entry(mensPlayerInDb).CurrentValues.SetValues(mensPlayer);
context.SaveChanges();
...or you can try:
context.Entry(mensPlayer).State = EntityState.Modified;
context.Entry(mensPlayer).Property(m => m.ImageData).IsModified = false;
The latter approach didn't work with EF < 5.0 (because it was forbidden to set the Modified
state of a property to false
once the entity was marked as Modified
) but it should work with EF 5.
You need to integrate one of the solutions into your _dataSource
service class, possibly by introducing new methods because it won't work with your general Update
method.