ASP.NET MVC 5 Model-Binding Edit View

廉价感情. 提交于 2019-12-05 07:52:39

You should leverage ViewModels:

public class WarrantyModelCreateViewModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime CreatedDate { get; set; }
    DateTime LastModifiedDate { get; set; }
}

public class WarrantyModelEditViewModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime LastModifiedDate { get; set; }
}

The intention of a ViewModel is a bit different than that of a domain model. It provides the view with just enough information it needs to render properly.

ViewModels can also retain information that doesn't pertain to your domain at all. It could hold a reference to the sorting property on a table, or a search filter. Those certainly wouldn't make sense to put on your domain model!

Now, in your controllers, you map properties from the ViewModels to your domain models and persist your changes:

public ActionResult Edit(WarrantyModelEditViewModel vm)
{
    if (ModelState.IsValid)
    {
        var warrant = db.Warranties.Find(vm.Id);
        warrant.Description = vm.Description;
        warrant.LastModifiedDate = vm.LastModifiedDate;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(warrantymodel);
}

Furthermore, ViewModels are great for amalgamating data from multiple models. What if you had a details view for your warranties, but you also wanted to see all servicing done under that warranty? You could simply use a ViewModel like this:

public class WarrantyModelDetailsViewModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime CreatedDate { get; set; }
    DateTime LastModifiedDate { get; set; }
    List<Services> Services { get; set; }
}

ViewModels are simple, flexible, and very popular to use. Here is a good explantion of them: http://lostechies.com/jimmybogard/2009/06/30/how-we-do-mvc-view-models/

You're going to end up writing a lot of mapping code. Automapper is awesome and will do most of the heavy lifting: http://automapper.codeplex.com/

This is not an answer for the questions, but it might be critical for those who is using Bind() and facing different problems. When I was searching "why Bind() clears out all pre-existing but not-bound values", I found this:

(in the HttpPost Edit()) The scaffolder generated a Bind attribute and added the entity created by the model binder to the entity set with a Modified flag. That code is no longer recommended because the Bind attribute clears out any pre-existing data in fields not listed in the Include parameter. In the future, the MVC controller scaffolder will be updated so that it doesn't generate Bind attributes for Edit methods.

from a official page (last updated in 2015, March): http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application#overpost

According to the topic:

  1. Bind is not recommended and will be removed in the future from the auto-generated codes.

  2. TryUpdateModel() is now the official solution.

You can search "TryUpdateModel" in the topic for details.

Moynul Haque Biswas

It may solve your problem

In Model: Use ?

public class WarrantyModel
{
    [Key]
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime? CreatedDate { get; set; }
    DateTime? LastModifiedDate { get; set; }
}

After form submit:

public ActionResult Edit([Bind(Include = "Id,Description,CreatedDate,LastModifiedDate")] WarrantyModel warrantymodel)
{
    if (ModelState.IsValid)
    {
        db.Entry(warrantymodel).State = EntityState.Modified;
        db.Entry(warrantymodel).Property("CreatedDate").IsModified=false
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(warrantymodel);
}

+1 for cheny's answer. Use TryUpdateModel instead of Bind.

public ActionResult Edit(int id)
{
    var warrantymodel = db.Warranties.Find(id);
    if (TryUpdateModel(warrantymodel, "", new string[] { "Id", "Description", "LastModifiedDate" }))
    {
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(warrantymodel);
}

If you want to use View Model, you can use Automapper and configure it to skip null values so the existing data still exists in the domain model.

Example:

Model:

public class WarrantyModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime CreatedDate { get; set; }
    DateTime? LastModifiedDate { get; set; }
}

ViewModel:

public class WarrantyViewModel
{
    public int Id { get; set; }
    public string Description { get; set; }
    DateTime? CreatedDate { get; set; }
    DateTime? LastModifiedDate { get; set; }
}

Controller:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit([Bind(Include="Id,Description,LastModifiedDate")] WarrantyViewModel warrantyViewModel)
{
    var warrantyModel = db.Warranties.Find(warrantyViewModel.Id);
    Mapper.Map(warrantyViewModel, warrantyModel);
    if (ModelState.IsValid)
    {
        db.Entry(warrantyModel).State = EntityState.Modified;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    return View(warrantyModel);
}

Automapper:

Mapper.CreateMap<WarrantyViewModel, WarrantyModel>()
    .ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

try to remove the Create date prompt text box in the Edit view. In my application, the scaffold generated Edit and Create Views contain the Primary key which is generated in the database.

Controller:

...
warrantymodel.CreatedDate = DateTime.Parse(Request.Form["CreatedDate"]);
...
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!