【问题标题】:ActionFilter to exclude properties validated in Edit method not working ASP.NET MVCActionFilter 排除在 Edit 方法中验证的属性不起作用 ASP.NET MVC
【发布时间】:2014-08-16 03:00:23
【问题描述】:

我对 ASP.NET MVC 非常陌生。我指的是this 答案来解决我的问题。我花了几个小时研究了很多,以找到一种从编辑操作中排除属性的方法。 我使用了数据绑定,但没有运气,我使用视图模型仅包含需要修改的字段,这很有效,但我并不是 100% 满意,因为我正在寻找一种更简单且可重用的方法。我尝试使用 TryUpdateModel() 但我不完全了解如何实现它。

上面的链接看起来是一个完美的解决方案,但不幸的是我仍然遇到同样的验证错误Cannot insert the value NULL into column 'CreatedBy', table 'xxx'; column does not allow nulls. UPDATE fails. 知道为什么吗?非常感谢帮助。

编辑:让我再次清楚地说明我想要什么。我想在我的模型中使用“CreatedBy”,这就是为什么我有一个属性。我不能只是删除它。我不想在视图中使用隐藏字段来传递值,因此它不会为空。我只想要一种从编辑中排除属性的适当而简单的方法。除了使用视图模型之外,还有更好的解决方案吗?

详情: CreatedBy 是在创建操作时填充的必填字段。 我的视图包含模型中除CreatedBy之外的所有字段的编辑器

我的控制器:

    [HttpPost]
    [ValidateOnlyIncomingValuesAttribute]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Exclude = "CreatedBy")] Request model)
    {
        if (ModelState.IsValid)
        {
            var currentUser = User.Identity.Name;
            var emp = db.Employees.SingleOrDefault(e => e.ApplicationUser == db.Users.FirstOrDefault(u => u.UserName.Equals(currentUser)));

            if (emp != null)
            {
                model.ModifiedBy      = emp.NIC;
                model.ModifiedDate    = DateTime.Now;
                db.Entry(model).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
         // some code
      }
    }

我的操作过滤器

public class ValidateOnlyIncomingValuesAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var modelState = filterContext.Controller.ViewData.ModelState;
            var valueProvider = filterContext.Controller.ValueProvider;

            var keysWithNoIncomingValue = modelState.Keys.Where(x => !valueProvider.ContainsPrefix(x));
            foreach (var key in keysWithNoIncomingValue)
                modelState[key].Errors.Clear();
        }
    }

【问题讨论】:

    标签: c# asp.net-mvc validation action-filter


    【解决方案1】:

    正如 manish 所指出的,您的错误与验证无关

    选项 1

    CreatedBy 呈现隐藏输入,以便它回发并从您的方法中删除[Bind(Exclude = "CreatedBy")]。该值将在数据库中更新,但与您开始时使用的值相同,因此其不变

    选项 2

    根据您可以使用“黑名单”方法的 EF 版本(我认为是 EF5+)

    db.Entry(model).State = EntityState.Modified;
    db.Entry(model).Property(x => x.CreatedBy).IsModified = false;
    db.SaveChanges();
    

    或“白名单”方法,您需要为除CreatedBy 之外的每个属性设置EntityState.Modified;

    // db.Entry(model).State = EntityState.Modified; // do do this
    db.Entry(model).Property(x => x.SomeProperty).State = EntityState.Modified;
    // Repeat for all properties except `CreatedBy`
    

    【讨论】:

    • 现在我收到了这个奇怪的错误Member 'IsModified' cannot be called for property 'CreatedBy' because the entity of type 'Request' does not exist in the context 可能是因为传递的请求对象与上下文中的现有对象不同?
    • 模型分离了吗?试试db.Requests.Attach(model);(修改状态前)
    • 我遇到了同样的问题,因为我有一个不想绑定和更新的 xml 属性(服务器拒绝了它!),但它必须在模型中,否则它会被设置为空。使用db.Entry(model).Property(x => x.CreatedBy).IsModified = false; 效果很好!非常感谢
    【解决方案2】:

    这不是 MVC 验证错误。这是你得到的例外。您正在尝试为实际上不可为空的 CreatedBy 列添加空值。

    使 CreatedBy 列可以为空或从控制器为其赋值。

    【讨论】:

    • 哇!是啊,就是!有没有办法使用动作过滤器来实现这一点?我不想让它可以为空或为它分配任何值。需要排除值。
    • 我不明白。您不想为其分配任何值,并且也希望不可为空。这怎么可能?如果你想要不可为空的列,你必须赋值
    • 您是否完整阅读了我的问题?它已经具有价值。将记录添加到数据库时会填充 CreatedBy 字段。这是编辑动作。所以我不再想要 CreatedBy 值了。
    • 你能从模型中排除 CreatedBy 字段吗?那么它不会尝试在您的 Edit() 方法中进行验证
    • @guitarlass,你完全阅读了 manish 的问题吗?它不是验证问题 - 更新数据库时会引发错误。为Created 添加一个隐藏输入以查看,以便其回发原始输入并删除[Bind(Exclude = "CreatedBy")]
    【解决方案3】:

    //更新

    了解您不使用 ViewModels 的决定,即使对我来说这不是为了可重用性,我认为这里有两点很清楚。

    A->您没有遇到验证错误

    B-> [Bind(Exclude = "CreatedBy")] 工作正常

    因此,根据 A,我什至会删除该过滤器。您确定 CreatedBy 属性已根据验证规则创建,因此您无需跳过验证,因为您再次期待该值。如果一个狡猾的用户试图编辑它,那么验证至少不会对你造成任何伤害。

    B-> 您拥有该属性为 null 的模型,因此如几个 cmets 中所述,您应该在更新数据库中的实体之前填充它。但是您不信任隐藏的输入字段作为该属性的值。 我猜您正在使用 ORM,但我不知道是否有任何允许您按照您尝试执行的方式进行部分更新,即仅在不可为空的模型中传递具有某些 null 属性的模型并期望它推断出它必须获取该属性的先前值。我几乎不怀疑。所以这里的选择并不多:

    您应该从数据库中获取模型,单独更新您想要的属性子集并再次保存。所以这意味着基本上你甚至可以摆脱 The Bind(Exclude... 位,你不会在视图中为此提供和编辑器字段,以防有人编辑它并且不会使用该更新。

    我什至不会想到在所谓的可重复使用的过滤器中这样做,因为到数据库的旅行将是每个案例。

    我可以想到其他一些复杂的方法。但不值得一提,实际上这给我留下了一个问题,你是在为那个实体暴露 theId 吗?因为你在那里打开了一个漏洞,他们可以编辑另一个,也许这不是你想要的。等等...我不确定您是否在研究中遇到过this

    对我来说,我坚持认为你不应该这样胡闹:ViewModels 就是答案

    //更新结束

    我会创建一个 ViewModel,其中包含您想要的属性(即省略 CreatedBy),将其用作视图模型,而不使用域实体模型。

    类似的东西

    public ActionResult Edit(RequestViewModel model)
    {
        if (ModelState.IsValid)
        {
            var currentUser = User.Identity.Name;
            var emp = db.Employees.SingleOrDefault(e => e.ApplicationUser == db.Users.FirstOrDefault(u => u.UserName.Equals(currentUser)));
    
            if (emp != null)
            {
                var domainModel = GetRequestById(model.Id) //Or whatever method you have for this
    
                //Update whatever you want in domainModel, basically all apart from CreatedBy from the model
                domainModel.Property1 = model.Property1 
    
                //Save the updated domainModel
            }
         // some code
      }
    }
    

    像这样成为你的 RequestViewModel

    public class RequestViewModel
    {
        Guid Id {get; set;} //Or whatever Id you use (int...)
    
        //Rest of the properties you really want them to be able to edit
    
     }
    

    这样,您甚至可以将 CreatedBy 添加到视图模型中,例如,如果您想在编辑视图中显示它,但如果您愿意,您不会在回帖中更新它(因此将其用作创建视图模型操作也是如此,尽管很多时候我不介意并且实际上更喜欢创建一个编辑视图模型和一个创建视图模型(如果它们不同)

    【讨论】:

    • 谢谢,但正如我在问题中提到的,我实现了一个视图模型并且它可以工作。我只是对那个解决方案不满意,正在寻找不同的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多