C# MVC5 @Html.EnumDropDownListFor 在“回发”上丢失选择

Posted

技术标签:

【中文标题】C# MVC5 @Html.EnumDropDownListFor 在“回发”上丢失选择【英文标题】:C# MVC5 @Html.EnumDropDownListFor loses selection on "postback" 【发布时间】:2015-04-25 15:57:38 【问题描述】:

MVC5 EF6

我有一个产品。一个产品可以有多个 Title,一个 title 有一个 Type,它是一个 Enum。

我正在为产品创建视图 - 模型就是产品

查看:

            @for (int x = 0; x < Model.ProdTitles.Count; x++)
                                
                <tr>
                    <td>
                        @html.TextBoxFor(model => model.ProdTitles.ToArray()[x].Title, new  @class = "form-control" )
                        @Html.ValidationMessageFor(model => model.ProdTitles.ToArray()[x].Title, "", new  @class = "text-danger" )
                    </td>
                    <td>
                        @Html.EnumDropDownListFor(model => model.ProdTitles.ToArray()[x].TitleTypeID, new  @class = "form-control" )
                    </td>
                    <td>
                        @Html.EnumDropDownListFor(model => model.ProdTitles.ToArray()[x].CultureID, new  @class = "form-control" )
                    </td>
                </tr>
            

在控制器中 - 当我创建一个产品以返回视图时,我为每种标题类型创建一个标题并将其添加到产品中。视图显示了我所期望的一切。

当我点击创建按钮时,产品和标题会按预期返回到控制器,并且我会验证标题(不同的验证取决于类型)。我在 ModelState 中添加了任何错误,因此 ModelState.IsValid 为 false。

我回到视图return View(product); 调试此产品时,所有标题都在产品中,并且它们仍然具有正确的类型,但视图现在显示列表中的第一个枚举,用于所有标题,而不是模型中实际存在的标题!

如果我把 EnumDropDown 改成文本框,就会显示正确的类型,所以模型肯定是正确的:

我不确定为什么会发生这种情况,我希望有人可以提出修复建议?它是 EnumDropDownFor 中的错误吗?还是我做错了什么?

控制器代码:

    public ActionResult Create()
    
        Product product = new Product();

        foreach (var enm in Utils.Enums.EnumHelper.GetValues<Utils.Enums.TitleType>())
        
            product.ProdTitles.Add(new ProdTitle()
            
                CultureID = Utils.Enums.CultureID.English_United_Kingdom,                    
                DateCreated = DateTime.Now,
                Title = "",
                TitleTypeID = enm
            );
        

        return View(product);
    


    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "ProdID,DateCreated")] Product product, ICollection<ProdTitle> prodTitles)
    
        //ensure titles are all valid before saving
        for (int x = 0; x < prodTitles.Count; x++)
        
            ProdTitle title = prodTitles.ToArray()[x];
            if (!title.IsValid)
            
                ModelState.AddModelError(string.Empty, title.TitleTypeID + " title is invalid.");
            
            product.ProdTitles.Add(title);
        


        if (ModelState.IsValid)
        
            db.Products.Add(product);
            db.SaveChanges();
            return RedirectToAction("Index");
        

        return View(product);
    

ProdTitle 模型

public partial class ProdTitle

    public long TitleID  get; set; 
    public long ProdID  get; set; 
    public Utils.Enums.TitleType TitleTypeID  get; set; 
    public string Title  get; set; 
    public Utils.Enums.CultureID CultureID  get; set; 
    public System.DateTime DateCreated  get; set; 

    public virtual Product Product  get; set; 
    public virtual DataSource DataSource  get; set; 

【问题讨论】:

显示你的控制器代码 文化栏呢?回发后的值是否正确?我假设一些 javascript 问题,可能是 oncahnge 事件重置所选值... @VladL 文化目前只有一个选项,但我会尝试添加另一个来测试 @EhsanSajjad 我现在不在电脑旁,但会尽快添加。但是控制器中返回的产品按预期填充。 确保检查 Post Method。通常,您将有 2 种方法。单击提交时,您将依赖 [HttpPost] 属性方法而不是原始方法。所以如果你没有从那个方法告诉你控制正确的类型,那么它将返回默认类型。 【参考方案1】:

在处理集合中的下拉列表时,您需要自定义EditorTemplate

/Views/Shared/EditorTemplates/ProdTitle.cshtml

@model yourAssembly.ProdTitle

<tr>
  <td>
    @Html.TextBoxFor(m => m.Title, new  @class = "form-control" )
    @Html.ValidationMessageFor(m => m.Title, new  @class = "text-danger" )
  </td>
  <td>
    @Html.EnumDropDownListFor(m => m.TitleTypeID, new  @class = "form-control" )
  </td>
  <td>
    @Html.EnumDropDownListFor(m => m.CultureID, new  @class = "form-control" )
  </td>
</tr>

然后在主视图中

@model yourAssembly.Product
@using(Html.BeginForm())

  .... // other controls for properties of Product
  @Html.EditorFor(m => m.ProdTitles) // not in a loop!

然后修改控制器为

public ActionResult Create(Product product)

注意:您当前的 [Bind] 属性从绑定中排除了 ProdTitle 属性,并且在任何情况下,您都应该使用视图模型来仅表示您想要显示/编辑的内容

【讨论】:

完美 - 完全按照预期/要求工作。我是 MVC 的新手,所以我不知道这种方法。我的方法几乎可以满足我的需要,但现在我看到您的解决方案显然不是正确的方法。您能否扩展您对[Bind] 的最后评论并查看模型。我使用了脚手架 Product Create 并尝试对其进行扩展。 一般来说,如果数据模型用于创建或编辑对象,则不应在视图中使用它们。始终使用仅包含视图中所需属性的视图模型(请参阅What is ViewModel in MVC?),这意味着您永远不需要[Bind(Include="..")] 属性。根据您显示的内容,您将拥有 class ProdTitleVM 和属性 TitleTitleTypeIDCultureIDclass ProductVM,其中包含属性 List&lt;ProdTitleVM&gt; ProdTitle 然后您可以在 GET 方法中将属性映射到视图模型,并在 POST 方法中从视图模型映射到数据模型 - 使用诸如 automapper 之类的工具会更容易。另外,查看您当前的属性 - 您排除了除 "ProdID, DateCreated" 之外的所有属性,但它是一种创建新产品的方法 - ID 和 DateCeated 属性的值甚至还不存在(它尚未保存到数据库中!)所以您实际上是从绑定中排除所有属性 太好了,感谢您提供的信息 - 我会查看您链接到的信息。作为跟进,假设在您的示例中,我想将 TitleTypeID 设为只读(但仍保留下拉列表的外观),我可以将 @readonly="readonly", @disabled="disabled" 添加到属性中并在模板中添加一个隐藏字段:@Html.HiddenFor(m =&gt; m.TitleTypeID) 但这样做一旦我点击创建按钮而不填写标题,我就会回到被重置为列表中第一个枚举的值。您将如何实现这一目标 抱歉,不完全清楚您的要求。如果TitleTypeID 的初始值是(比如)A 并且您已禁用下拉列表,则只有隐藏输入A 的值将被回发,然后在您返回视图时再次显示。你是怎么改的?

以上是关于C# MVC5 @Html.EnumDropDownListFor 在“回发”上丢失选择的主要内容,如果未能解决你的问题,请参考以下文章

使用 MVC5 C# 和 Razor 在局部视图中运行 javascript

从控制器获取结果,但在 MVC5 和 C# 中,ajax 调用获取错误而不是成功

C# MVC5 JavaScript Chart.js 饼图,无需刷新即可从 SQL Server 数据库实时更新

C# 程序员编程 ASP.NET CORE MVC5 EF Redis 多线程 异步 爬虫 视频教程

如何在没有 ef 的情况下在 MVC5 中使用参数进行搜索

Ajax.BeginForm 处理两个不同的 onSuccess 响应,MVC 5,C#