在asp.net MVC 应用程序, 表单提交的数据通过模型绑定将数据从View传递到控制器中。使用模型绑定显然比Request.Form["Price"] ,Decimal.Parse(Request.Form["Price"] )还需要手动类型转换要要方便很多。
模型绑定有两种,隐式绑定和显式绑定。
模型状态Modelstate是模型绑定的副产品,两者都可以调用ModelState.IsValid()方法 来验证模型绑定是否成功,或者添加自定义的ModelState.AddModelError("property","ErrorMessage") 模型绑定错误信息。只要有错误,就不执行更改数据库操作。
隐式绑定和显式绑定的主要区别是:1、隐式绑定使用的是一个绑定的参数对象去填充数据库。而显式绑定 改变数据库中某个字段。2、隐式绑定需要设置 要改变实体的状态,显式绑定在TryUpdateModel后自动将该实体设置为Modified。3、执行绑定后,隐式绑定原数据库的实体的值相当于丢失,需要参数对象来重新指定,而隐式绑定 就是在该实体上执行更改。
1、隐式模型绑定。先将表单值转化为CLR 类型,然后以表单字段name名称,value值参数传递到Action中。为了防止过度提交,使用Bind(Include)属性接受白名单,Bind(Exclude="")属性拒绝黑名单。
// POST: Movie/Create
// 为了防止“过多发布”攻击,请启用要绑定到的特定属性,有关
// 详细信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=317598。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Title,ReleaseDate,Genre,Price")] Movie movie)
{
if (ModelState.IsValid)
{
db.Movies.Add(movie); //将实体的跟踪状态设置为 EntityState.Added;如果是在编辑操作中, 需要手动设置 EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
return View(movie);
}
2、显式绑定 使用TryUpdateModel 和UpdateModel 显式绑定,它自动将数据库上下文对象的跟踪的实体状态设置为EnrityState.Modified。还可以通过设定参数来指定绑定的字段,两者的区别 是如果 绑定失败,前者不会抛出一个异常,后者会引发异常。
[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public ActionResult EditPost(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var studentToUpdate = db.Students.Find(id);
if (TryUpdateModel(studentToUpdate, "", new string[] { "LastName", "FirstMidName", "EnrollmentDate" }))
{
try
{
//已不需要下面的语句,来设置数据库上下文跟踪实体的状态了,已自动设置跟踪状态的EntityState.Modified标记。
// db.Entry(studentToUpdate).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch(DataException /* dex*/)
{
//Log the error (uncomment dex variable name and add a line here to write a log.
ModelState.AddModelError("", "不能保存更改,重试一次,如果问题仍然存在,请与系统管理员联系");
}
}
return View(studentToUpdate);
}
三、两者的区别的实际的例子:
TryUpdateModel或者UpdateModel 与[Bind(Include ="","") model] 的差别的是 Bind白名单 使用 模型绑定的参数model去替换数据库中的白名单字段,使用的是一个新建的参数model对象,如果白名单里面没有某个属性,就为空。因此,如果有构造方法中指定了创建时间,他会使用参数model中的默认创建的值去更新。
而TryUpdateModel(model,new string [] {"properties"})) 先在数据库中找到这个模型,然后,实体状态自动设置为 modified,只更新这个模型的指定属性。如果不包括某些属性的话,就不会更新。因此,如果有构造方法中指定了创建时间,他也不会去更新。
如果 一个模型在构造方法中指明了一个属性的默认值 ,但在模型绑定中未指明是绑定的字段,视图中也无输入字段.
1、如果使用隐式绑定[Bind() model]则会在数据库中自动生成 构造方法中初始化的值,且在更改数据库的一瞬间自动生成默认值,特别是时间。
2、如果使用显式绑定TryUpdateModel()或UpdateModel(),不会更改在构造方法中指定的初始化值。
如:
模型:
public class ReviewIndexItem
{
public ReviewIndexItem()
{
ReviewIndexItemID = Guid.NewGuid().ToString();
ReviewIndexUseItems = new List<ReviewIndexUseItem>();
ReviewIndexItemCreateTime = DateTime.Now;
IsUsed = false;public string ReviewIndexItemID { get; set; }
[Display(Name = "修改时间")]
public DateTime ReviewIndexItemCreateTime { get; set; }
[Display(Name="是否使用")]
public bool IsUsed { get; set; }
}
控制器:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ReviewIndexItemID,ReviewIndexItemNumber,ReviewIndexItemName,ReviewIndexItemDescription,ReviewIndexItemHighestScore,ReviewIndexItemLowestScore")] ReviewIndexItem reviewIndexItem)
{
if (ModelState.IsValid) //如果编码重复,请在视图中返回错误信息。
{
ValidateReviewIndexItemNumberDumplicate(reviewIndexItem);
}
if (ModelState.IsValid)
{
db.ReviewIndexItems.Add(reviewIndexItem);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(reviewIndexItem);
}
private void ValidateReviewIndexItemNumberDumplicate(ReviewIndexItem reviewIndexItem)
{
if (reviewIndexItem.ReviewIndexItemNumber != null)
{
var dumplicateNumberIndexItem = db.ReviewIndexItems.Where(indexItem => indexItem.ReviewIndexItemNumber == reviewIndexItem.ReviewIndexItemNumber).AsNoTracking().FirstOrDefault();
if (dumplicateNumberIndexItem != null && dumplicateNumberIndexItem.ReviewIndexItemID != reviewIndexItem.ReviewIndexItemID)
{
ModelState.AddModelError("ReviewIndexItemNumber", "评审指标编码重复,请重新编码");
}
}
}