ASP.NET MVC - 从视图中部分更新模型

时间:2009-11-26 12:06:11

标签: asp.net-mvc model-view-controller

我只是想知道人们是如何接近这种情况的。在我使用ORM的MVC(在这种情况下是NHibernate)中,这似乎是一个弱点......

假设您的模型中有一个细粒度且复杂的实体。您可能会有一个管理页面来管理此类对象。如果实体很复杂,则您不太可能以一种形式修改整个实体。您仍然需要将相关属性传递给视图,并在视图返回时将更改合并到模型中的那些属性。

在这种情况下,有什么人做的?

  • 创建一个视图模型,该视图模型是(或包含)实体属性的子集。将此传递给视图和从视图传递。在控制器中的“编辑”操作方法中,从存储库中获取对象,遍历ViewModel中的所有属性并将它们应用于Model对象(model.a = viewmodel.a,modelb = viewmodel.b)。这似乎是明显合理的路线,但产生了许多繁琐的管道代码。这也使验证变得复杂。

  • 还有别的吗?

我简要介绍了一下自动播放器 - 但这似乎不符合规定,也许我错了?

感谢。

7 个答案:

答案 0 :(得分:11)

这听起来像是automapper的完美场景。您创建一个包含字段子集或实际模型的视图模型类,并让AutoMapper注意将域模型对象中的值从视图模型对象中提取出来。你对这种方法有什么问题?

考虑这个例子:

以下是您的域模型和视图模型

public class Person
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }

    public string Address1
    { get; set; }

    public string Address2
    { get; set; }
}

public class PersonViewModel
{
    public string FirstName
    { get; set; }

    public string LastName
    { get; set; }

    public string HomeNumber
    { get; set; }
}

以下是您的映射,您必须在dm-> vm和vm-> dm的两个方向创建映射。

从我在使用Automapper时看到的是,如果你从对象A映射到B而B有一个A没有的属性,它将被重置。因此,当我创建地图时,我指示它忽略那些缺少的属性。我不是Automapper的专家,所以我可能错了。

<强>映射

Mapper.CreateMap<Person, PersonViewModel>();
// Automapper will reset values in dest which don't exist in source, so be sure to ignore them!
Mapper.CreateMap<PersonViewModel, Person>()
    .ForMember(dest => dest.HomeNumber, opt => opt.Ignore());

最后用法:

Person p = new Person()
{
    FirstName = "First",
    LastName = "Last",
    Address1 = "add 1",
    Address2 = "add 2"
};

PersonViewModel pvm = Mapper.Map<Person, PersonViewModel>(p);
// Map to a new person
Person p2 = Mapper.Map<PersonViewModel, Person>(pvm);

// Map to the existing person just to update it
Person p3 = new Person()
{
    HomeNumber = "numberHere"
};

// This will update p3
Mapper.Map<PersonViewModel, Person>(pvm, p3);

由于排除,这显然不太理想,但比手动完成整个事情要好得多。

答案 1 :(得分:5)

  1. 让您的视图模型与您的域模型一对一地进行映射。

  2. 指定Model作为routeValues的参数,如下所示。这意味着您的视图模型将使用域模型中的值进行初始化。在结果personViewData中,只会覆盖表单中的子字段集。

  3. 更新视图:

    @model ViewModel.PersonView
    
    @using (Html.BeginForm("Update", "Profile", Model, FormMethod.Post))
    {
      ...Put your sub set of the PersonView fields here
    }
    

    ProfileController可:

    public ActionResult Update(string userName)
    {
        Person person = _unitOfWork.Person.Get().Where(p => p.UserName == userName).FirstOrDefault();
        PersonView personView = new PersonView();
        Mapper.Map(person, personView);
    
        return View(personView);
    }
    
    [HttpPost]
    public ActionResult Update(PersonView personViewData)
    {
       Person person = _unitOfWork.Person.Get().Where(p => p.UserName == personViewData.UserName).FirstOrDefault();
       Mapper.Map(personViewData, person);
       _unitOfWork.Person.Update(person);
       _unitOfWork.Save();
    
       return Json(new { saved = true, status = "" });
    }
    

答案 2 :(得分:2)

为什么不将TryUpdateModel与表单集合一起使用。

如果您的观点正在编辑某人

public class Person
{
    public string ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Email { get; set; }
    public string Address { get; set; }
}

您的视图只是编辑名字和姓氏,您可以这样做:

public ActionResult Action(FormCollection form)
{
    Person personToUpdate = Repository.GetPerson(form["ID"]);

    TryUpdateModel<Person>(personToUpdate, form);

    Repository.Update(personToUpdate)

    return View();
}

这只会使用表单集合的一部分更新Person。如果您不想更新字段,请不要使用表单提交。

答案 3 :(得分:1)

如果您有完整型号但每个页面仅使用和更新所需部件,该怎么办?然后使用最后一页的完整视图数据更新业务模型。

答案 4 :(得分:1)

y,viewmodel + automapper。

PS。一个复杂的模型,往往......使一切变得复杂 - 在你的场景中它真的值得吗?

答案 5 :(得分:1)

我使用类似的方法(在我的情况下是实体框架)与实体 - &gt; ViewModel - &gt;在具有1:M或M:M关系的“复杂”实体的视图上查看。在大多数情况下,当我有一个简单的实体时,我选择了实体 - >视图。

我的ViewModel被定义为实体+支持属性:SelectListMultiSelectList以及stringList<string>。我还将使用ViewModel作为我拥有视图所需属性的实例,但在实体(数据库)中可能不一定需要。

Http Get控制器方法非常简单ActionResults,其中return View(repository.FetchNewViewModel())表示创建,repository.FetchModelById(id)表示编辑。在这两个实例中,我在将实体传递给视图之前初始化它们。

Create([Bind(Exclude = "Entity.EntityId")] ViewModel model)Edit(ViewModel model)是创建和编辑的Http Post控制器方法。我的编辑视图有一个隐藏的输入字段,供EntityId来回传递。

当Http Post方法具有viewmodel时,我将丢失所有Entity.Relation和ViewModel。(Multi)SelectList值。如果我希望我的视图正确显示,我必须重建对象:     `

try
{
    var tags = model.TagIds; // List<string> or <int> depending on your Id type
    if (model.TagsList == null) // It will be
    {
        model.TagsList = _repository.FetchSelectedTags(tags); // Build a new SelectList
    }

    if (!ModelState.IsValid)
    {
        return View(model);
    }

    _repository.Add(model.Article, tags); // or Save() for Edit
}
catch
{
    return View(model); // Generally means something screwed in the repository class
}

return RedirectToAction("Index");

`

我的实体库中可能有30%使用ViewModel,因此我绝对只在需要时使用它。如果在大多数情况下您有复杂的视图和模型数据,则可以将其分解为较小的视图。

答案 6 :(得分:0)

现在我正在使用S#arp架构进行大型项目,我也使用这种方法:

Model -> ViewModel -> Model

我使用ViewModel作为Binding部分和Validations,另一种方法是使用Model Directly(使用我们在原型开发期间使用的tryUpdateModel / UpdateModel)但是对于复杂的场景,我们最终处理像SelectLists / Checkbox这样的特殊情况/ Projections / HMAC验证在一点ViewModel中使用了很多Request.Form [“key”] =(,另一个缺点是处理你想用用户输入重新填充表单的错误情况,我发现它直接使用Model更复杂一点(使用ViewModel,我们充分利用了ModelState的尝试价值,为我们节省了几次DB,任何遇到这种情况的人都知道我的意思。)

这种方法有点耗时,就像你说的那样,你最终会匹配属性,但在我看来,这是复杂形式的方法。

值得一提的是,我们只是将ViewModels用于创建/编辑场景,几乎所有其他我们直接使用该模型。

到目前为止我还没有使用过autommapers,但我肯定会尝试一下。

相关问题