【问题标题】:How do you exclude properties from binding when calling UpdateModel()?调用 UpdateModel() 时如何从绑定中排除属性?
【发布时间】:2012-01-10 01:57:17
【问题描述】:

我有一个视图模型发送到我的控制器的编辑操作。 ViewModel 包含对 EntityObjects 的引用。 (是的,我很好,不需要复制视图模型中的所有实体属性)。

我实例化视图模型,然后调用 UpdateModel。我收到一个属性为“null”的错误,这很好,因为它是一个相关模型。我试图在模型绑定期间排除该属性的绑定。在调试它时,我在模型绑定器试图将属性值设置为 null 的实体中看到。

这是我的编辑操作:

var model = new SimplifiedCompanyViewModel(id);

var excludeProperties = new string[] { 
   "Entity.RetainedEarningsAccount.AccountNo"
   ,"Property.DiscountEarnedAccount.ExpenseCodeValue"
   ,"Entity.EntityAlternate.EntityID"
   ,"Property.BankAccount.BankAccountID"
   ,"Entity.PLSummaryAccount.AccountNo"
   ,"Property.RefundBank.BankAccountID"
   ,"Company.Transmitter.TCC"
};

try
{
    UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);

    if (ModelState.IsValid)
    {
       //db.SaveChanges();
    }
       return RedirectToAction("Index");
}
catch
{
    return View(model);
}

我已经查看了有关指定“前缀”的其他一些问题,但我认为这不是问题,因为我告诉它绑定到 viewmodel 实例而不仅仅是实体对象。

我是否正确排除了属性?奇怪的是似乎只发生在这个项目上。我怀疑这可能是一个问题,因为实际上没有与我的实体相关的退款银行。但是我有其他不存在的相关项目并且没有看到相同的问题。

更多信息...因为有人告诉我模型设计得不好。

公司与银行账户相关。公司视图显示当前相关的 BankAccount.BankAccountId,并且有一个带有 BankAccount.Key 的隐藏字段。 I use jQueryUI autocomplete feature to provide a dropdown of bank account displaying the BankAccount.BankAccountId and when one is selected the jQuery code changes the hidden field to have the correct Key value.因此,当发布此内容时,我不希望修改当前的银行帐户 BankAccountID,因此我希望它跳过绑定该字段。

如果我在模型中排除 BankAccountId,那么在 BankAccount 编辑视图中,用户将永远无法更改 BankAccountId,因为它不会被绑定。我不确定这如何表明模型设计不佳。

【问题讨论】:

  • 你可能会接受它,但你会让自己面临潜在的麻烦。如果新的值被添加到您的实体中,而您在未来不考虑怎么办?假​​设您添加了一个名为“DiscountRate”的字段(假设)并且最终用户看到它在其他地方使用,他们可以在表单中包含此值和注入你的模型。还有很多其他使用 ViewModel 的原因。复制属性通常是简单的复制/粘贴/清理,它不适用于这里的 DRY 原则。
  • 好吧,你一针见血。对我来说似乎不是很干。
  • DRY 通常适用于逻辑,不适用于视图模型。一个视图 = 一个视图模型。使用 automapper 在它们之间轻松映射。 Jimmy Bogard 有一个很好的属性,这使得它几乎是自动的——例如,您创建视图模型,加载您的客户实体,然后在操作方法中返回它。然后 AutpMap 属性会将其转换为 ViewModel。见lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models
  • 如果 ViewModel 与 Model 相同,对我来说似乎并不干燥。然而,这是一个如此复杂的视图,它使用了几个相关的实体,我可能不得不求助于这样做。我只是不确定自动映射器或其他东西将如何处理所有关系,我可能不得不手动编写所有更新代码。我会看看你上面列出的文章。
  • 是的,如果它是手动的,那么您不必担心那时绑定的bankaccount.name。

标签: asp.net-mvc-3 model-binding


【解决方案1】:

使用Bind 属性的Exclude 属性:

[Bind(Exclude="Id,SomeOtherProperty")]
public class SimplifiedCompanyViewModel
{
    public int Id { get; set; }

    // ...
}

这是System.Web.Mvc 命名空间的一部分。绑定时需要排除以逗号分隔的属性名称列表。

您还应该考虑使用 TryUpdateModel 而不是 UpdateModel。您也可以通过将默认模型绑定器作为参数传递给构造函数来计算它:

public ActionResult Create([Bind(Exclude="Id")]SimplifiedCompanyViewModel model)
{
    // ...
}

【讨论】:

  • @Bellash 考虑到 Id 未定义为 Nullable,这将永远不会将 Id 设置为 null
  • null 在数据库中,否则如果IdNO NULL,它将抛出异常@Dismissile
  • @Bellash 如果没有从表单发布的属性与 Id 匹配(或者如果在这种情况下它被排除在外),则 MVC 模型绑定器将只使用 int 的默认值(即为 0),因为它不是可为空的字段。
【解决方案2】:

我想出了一个非常简单的解决方案。

 try
{
   UpdateModel<SimplifiedCompanyViewModel>(model, String.Empty, null, excludeProperties);
   ModelState.Remove("Entity.RetainedEarningsAccount.AccountNo");
   ModelState.Remove("Property.DiscountEarnedAccount.ExpenseCodeValue");
   ModelState.Remove("Entity.EntityAlternate.EntityID");
   ModelState.Remove("Property.BankAccount.BankAccountID");
   ModelState.Remove("Entity.PLSummaryAccount.AccountNo");
   ModelState.Remove("Property.RefundBank.BankAccountID");
   ModelState.Remove("ompany.Transmitter.TCC");

    if (ModelState.IsValid)
    {
       //db.SaveChanges();
    }
       return RedirectToAction("Index");
}
catch
{
    return View(model);
}

【讨论】:

    【解决方案3】:

    这里的另一个选项是根本不在您的视图中包含此属性,它不会被绑定。是的 - 如果有人在页面上创建它,您仍然可以接受模型注入,但这是另一种选择。 MVC 中的默认模板会将您的 EditorFor 等创建为单独的项目,因此您可以删除它们。这会阻止您使用带有 EditorForModel 的单行视图编辑器,但无论如何模板不会为您生成它。

    编辑(添加以上评论)

    DRY 通常适用于逻辑,而不适用于视图模型。一个视图 = 一个视图模型。使用 automapper 在它们之间轻松映射。 Jimmy Bogard 有一个很好的属性,这使得它几乎是自动的——例如,您创建视图模型,加载您的客户实体,然后在操作方法中返回它。然后 AutpMap 属性会将其转换为 ViewModel。见lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models

    【讨论】:

    • 没那么容易。该视图是显示与该实体相关的银行帐户的公司。当他们更改 BankAccount 时,我有一个隐藏字段,其中填充了 PK 值。但是,BankAccount.Name 仍然与表单一起发布。我不想更改 BankAccount.Name,我希望更改关系。
    【解决方案4】:

    试试Exclude 属性。
    我承认我没用过。

    [Exclude]
    public Entity Name {get; set;}
    

    【讨论】:

    • hmm.. 当我实际编辑银行记录时,我不希望它从绑定中排除。但是,在编辑父公司记录时,我希望排除 BankAccountId(实际上是业务键),因为我实际上是通过修改 FK 值来修改相关的实体。
    • @PilotBob 更多证据表明您确实需要更好地设计模型。
    • @Dismissile 不同之处在于修改与 BankAccount 相关的实体与修改 BankAccount 实体本身。它与模型无关。我们并非都使用简单的 3 个实体模型来开发演示软件。
    猜你喜欢
    • 1970-01-01
    • 2014-06-05
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 2021-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多