无法使用 SetModelValue 修改 ModelState

Posted

技术标签:

【中文标题】无法使用 SetModelValue 修改 ModelState【英文标题】:Cannot modify ModelState using SetModelValue 【发布时间】:2017-05-10 03:38:03 【问题描述】:

我正在开发一个 MVC/EF Web 应用程序。在其中一种形式中,我编辑了一个模型。该模型有一个图像字段(public byte[] BioPhoto)

当我将图像上传到该字段并保存数据时,ModelState.IsValidfalse,因为在 ModelState 中 BioPhoto 属性是 null。请注意,模型中的 BioPhoto 已加载图像数据。

我尝试使用以下代码将图片注入 ModelState,但 ModelState.IsValid 仍然是 false

public ActionResult Edit([Bind(Include = "BusinessId,Name,About,Phone,TollFree,FAX,Email,Bio,BioPhoto")] Business business)

    if (System.IO.File.Exists("image.jpg"))
    
        business.BioPhoto = System.IO.File.ReadAllBytes("image.jpg");
        ModelState.SetModelValue("BioPhoto", 
                   new ValueProviderResult(business.BioPhoto, "", System.Globalization.CultureInfo.InvariantCulture));
    

    if (ModelState.IsValid)
    
        db.Entry(business).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    
    return View(business);

我做错了什么。这个问题有没有更好的解决方案?

我在 SO 中阅读了几个答案,但不知道如何解决我的问题。

这是我的模特

public class Business

    public int BusinessId  get; set; 

    [Required]
    [StringLength(100)]
    public string Name  get; set; 

    [Required]
    public Address address  get; set; 

    [Required]
    [StringLength(20)]
    public string Phone  get; set; 

    [StringLength(20)]
    public string TollFree  get; set; 

    [StringLength(20)]
    public string FAX  get; set; 

    [Required]
    [StringLength(50)]
    public string Email  get; set; 

    [Required]
    [StringLength(100)]
    public string WebSite  get; set; 

    [Required]
    public string About  get; set; 

    [Required]
    public string Bio  get; set; 

    [Required]
    public byte[] BioPhoto  get; set; 

我的观点

<div class="form-group">
    @html.LabelFor(model => model.BioPhoto, "BIO Photo (Best Size: 350 x 450)", htmlAttributes: new  @class = "control-label col-md-2" )
    <div class="col-md-10">
        <form enctype="multipart/form-data">
            <div class="form-group" style="width:400px">
                <input id="BioPhoto" type="file" multiple class="file" data-overwrite-initial="false" />
            </div>
        </form>
        @Html.ValidationMessageFor(model => model.BioPhoto, "", new  @class = "text-danger" )
    </div>
</div>

【问题讨论】:

显示类及其所有验证属性。 我添加了模型 你如何从客户端上传模型中的图像 我正在使用引导文件输入。我加我查看 如果不需要,为什么要标记Required 【参考方案1】:

由于您没有使用视图模型,因此您应该为图像使用HttpPostedFileBase 类型的参数。然后在你的action方法中,你可以convert it's InputStream property value to byte array并保存。

public ActionResult Edit([Bind(Include = "BusinessId,Name,About,Phone,TollFree,
                    FAX,Email,Bio")] Business business,HttpPostedFileBase BioPhoto)

   // to do read BioPhoto.InputStream and convert to your byteArray
   // to do  :Return something

您必须从模型类的字节数组属性中删除 [Required] 属性。

最好的解决方案是使用视图模型,该模型仅具有您需要的视图属性。您无需每次都从编辑屏幕重新发布图像。仅当用户在编辑表单中选择新图像时才更新该属性。如果不是,请不要在保存时更新该列的现有值。

public class EditBusinessVm

    public int Id  get; set; 

    [Required]
    [StringLength(100)]
    public string Name  get; set; 

    [Required]
    public Address address  get; set; 

    [Required]
    [StringLength(20)]
    public string Phone  get; set; 

    public HttpPostedFileBase BioPhoto  set;get;

    // Add other properties as needed.

现在你的视图必须是这个类的强类型

@model EditBusinessVm
@using(Html.BeginForm())

  @Html.TextBoxFor(s=>s.Name)
  <input type="file" name="BioPhoto" />
  @Html.HiddenFor(f=>f.Id)
  <!-- add other fields needed -->
  <input type="submit"  />

并且您的 httppost 操作方法将使用此类型作为参数

public ACtionResult Edit(EditBusinessVm model)

  // purposefully omitting null checks/model validations code here

   //get existing entity
   var b=db.Businesses.FirstOrDefault(f=>f.BusinessId==model.Id);
  //update the properties from the posted view model
   b.Name = model.Name;
   if(model.BioPhoto!=null)  // user selected a new photo.
     b.BioPhoto = GetByteArray(model.BioPhoto);
   // to do : Set other properties as well.

   db.Entry(business).State = EntityState.Modified;
   db.SaveChanges();
   return RedirectToAction("Index");


private byte[] GetByteArray(HttpPostedFileBase file)

   MemoryStream target = new MemoryStream();
   file.InputStream.CopyTo(target);
   return target.ToArray();      

【讨论】:

OP 刚刚说这是必需的,所以问题在其他地方。 我打赌字节数组字段的数据没有从表单中发布,因此模型验证失败。他不应该从表单中加载和发布 byte array 数据。如果需要,他应该发布一个 HttpPostedFileBase 实例。 删除必需的属性是最后的选择。我仍在尝试了解为什么 SetModelValue 不起作用。无论如何感谢您的回答。当我完成我的研究时,我会标记它。 @Shyju,你是说我使用 HttpPostedFileBase 而不是 byte[] 吗? 您可以将byte[] 用于实体模型,但使用HttpPostedFileBase 接收上传的文件数据,除非您有自定义模型绑定器来进行转换【参考方案2】:

就像我说的那样,问题是表单没有将BioPhoto 发布到控制器——很可能。您可以使用 Chrome 查看 http 请求正文中的内容。

如果你想在控制器没有接收到图像的情况下添加图像,那么试试这个。

尝试仅删除与图像属性相关的错误:

ModelState["BioPhoto"].Errors.Clear();

然后添加图片:

business.BioPhoto = System.IO.File.ReadAllBytes("image.jpg");

然后更新模型:

UpdateModel(business);

现在如果您调用ModelState.IsValid,它将考虑BioPhoto的设置并重新评估IsValid

编辑:

我建议打电话

ModelState.SetModelValue("BioPhoto", new ValueProviderResult(business.BioPhoto, "", System.Globalization.CultureInfo.InvariantCulture));

将值实际推送到 ModelState.Values。不过这不是必须的。

【讨论】:

我选择这个答案,因为它比另一个容易得多。无论如何感谢@Shyju。

以上是关于无法使用 SetModelValue 修改 ModelState的主要内容,如果未能解决你的问题,请参考以下文章

在 mod_perl2 中修改 POST 请求

如何修改游戏mod中后缀为json的文件?

无法启用 mod_rewrite

修改Minecraft mod中的.class文件

无法使用 mod_wsgi 从 python wsgi 连接到 pymssql

无法使用 mod-wsgi 在 Apache 上部署 django