超文本标记语言当没有指定新图像时,BeginForm POST将覆盖图像数据



这是我在StackOverflow上的第一篇文章。我希望这是有用的。

我有一个Razor视图,旨在允许编辑包含预定义值或空值的模型的可显示属性。视图不应该改变模型属性的内容,除非用户在基于视图的UI中有意地修改它们。除了包含图像数据的类型为byte[]的属性之外,视图的行为正确:Model.ImageData

@using (Html.BeginForm("Edit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" } ))
{
@Html.EditorForModel()
<div class="editor-label">Image</div>
<div class="editor-field">
    @if (Model.ImageData == null)
    {
        @: No image has been assigned in the database.
    }
    else
    {
        <img width="150" height="150" src="@Url.Action("GetImage", "Product", new { Model.ID} )" />
    }
    <div>Upload new image: <input type="file" name="Image" /></div>
</div>
<input type="submit" value="Save" />
}

上述视图适用于模型中除Model.ImageData外的所有属性。在这种情况下,发布将导致任何先前设置的Model.ImageData设置为空。我已经确认Model.ImageData在发布期间被设置为null,通过验证在发布之前,Model.ImageData包含一个有效的字节数组(具有预期的图像)。

上述视图的控制器代码为:

  public ViewResult Edit(int id)
    {
        Product product = repository.Products.FirstOrDefault(p => p.ID == id);
// breakpoint here shows that all model properties including product.ImageData are populated and valid.
        return View(product);
    }
  [HttpPost]
  public ActionResult Edit(Product product, HttpPostedFileBase Image)  
    {
        if (ModelState.IsValid)
        {
// breakpoint here shows that product.ImageData is null (but all other model properties are still populated with data).
            if (Image != null)
            {
                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} has been saved", product.Name);
            return RedirectToAction("Index");
        }
        else
        {
            return View(product);
        }
    } 

下面是更新模型的责任代码(尽管在调用此代码之前模型正在更改):

public void SaveProduct(Product product)
    {
        if (product.ID == 0)
        {
            context.Products.Add(product);  // why doesn't this try to save ID = 0 to the ID field of the product in the database??
        }
        else
        {
            Product dbEntry = context.Products.Find(product.ID);
            if (dbEntry != null)
            {
                dbEntry.Name = product.Name;
                dbEntry.Description = product.Description;
                dbEntry.Category = product.Category;
                dbEntry.Price = product.Price;
                dbEntry.ImageData = product.ImageData;
                dbEntry.ImageMimeType = product.ImageMimeType;
            }
        }
        context.SaveChanges();
    }

我做错了什么?

这确实是您应该考虑使用ViewModel类的地方。但是,为了保持当前的做事方式,尽量减少更改,请尝试将SaveProduct()方法修改为以下内容:

public void SaveProduct(Product product, bool updateImage = false)
{
    context.Products.Attach(product);
    // mark product as modified
    var entry = context.Entry(product);
    entry.State = EntityState.Modified;
    entry.Property(e => e.ImageData).IsModified = updateImage;
    entry.Property(e => e.ImageMimeType).IsModified = updateImage;   
    context.SaveChanges();  
}

,将控制器代码修改为:

...
var updateImage = false;
if (Image != null)
{
    updateImage = true;
    product.ImageMimeType = Image.ContentType; 
    product.ImageData = new byte[Image.ContentLength]; 
    Image.InputStream.Read(product.ImageData, 0, Image.ContentLength); 
}
repository.SaveProduct(product, updateImage);
....

最新更新