ActionFilter用于排除在Edit方法中验证的无法使用ASP.NET MVC的属性

时间:2014-08-14 10:31:30

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

我是ASP.NET MVC的新手。我提到this回答来解决我的问题。我花了好几个小时研究了一种从编辑动作中排除属性的方法。 我使用数据绑定,但没有运气,我使用视图模型只包括需要修改的字段,这有效但但我并不百分百,因为我正在寻找一种更容易和可重用的方法。我尝试使用TryUpdateModel(),但我不完全了解如何实现它。

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

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

详细说明: 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();
        }
    }

3 个答案:

答案 0 :(得分:2)

如图所示,您的错误与验证无关

选项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();

或“白名单”方法,您需要为EntityState.Modified;以外的每个媒体设置CreatedBy

// 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`

答案 1 :(得分:1)

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

使CreatedBy列可为空,或者从控制器为其分配值。

答案 2 :(得分:0)

//更新

了解您决定不使用ViewModels,即使对我来说这不是为了重用,我认为这里有两件事情可以解决。

A->您没有验证错误

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

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

B->您将具有该属性的模型设置为null,因此在一些注释中说明您应该在更新db中的权限之前填充它。但是,您不相信隐藏的输入字段作为该属性的值。 我猜您正在使用ORM而且我不知道是否有任何允许您部分更新您尝试做的方式,即只是将一个null属性的模型传递给一个非可空的属性并期望它推断它必须获得该属性的先前值。我几乎不怀疑。所以这里的选项并不多:

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

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

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

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

//更新结束

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

像这样的东西

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添加到viewmodel中,例如,如果您想在编辑视图中显示它,但如果您愿意,则无​​法在帖子中更新它(因此可以将其用作视图模型虽然很多时候我都不介意创建一个编辑viwmodel和一个创建视图模型(如果它们不同)