我只是想知道人们是如何接近这种情况的。在我使用ORM的MVC(在这种情况下是NHibernate)中,这似乎是一个弱点......
假设您的模型中有一个细粒度且复杂的实体。您可能会有一个管理页面来管理此类对象。如果实体很复杂,则您不太可能以一种形式修改整个实体。您仍然需要将相关属性传递给视图,并在视图返回时将更改合并到模型中的那些属性。
在这种情况下,有什么人做的?
创建一个视图模型,该视图模型是(或包含)实体属性的子集。将此传递给视图和从视图传递。在控制器中的“编辑”操作方法中,从存储库中获取对象,遍历ViewModel中的所有属性并将它们应用于Model对象(model.a = viewmodel.a,modelb = viewmodel.b)。这似乎是明显合理的路线,但产生了许多繁琐的管道代码。这也使验证变得复杂。
还有别的吗?
我简要介绍了一下自动播放器 - 但这似乎不符合规定,也许我错了?
感谢。
答案 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)
让您的视图模型与您的域模型一对一地进行映射。
指定Model
作为routeValues
的参数,如下所示。这意味着您的视图模型将使用域模型中的值进行初始化。在结果personViewData
中,只会覆盖表单中的子字段集。
更新视图:
@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被定义为实体+支持属性:SelectList
或MultiSelectList
以及string
或List<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,但我肯定会尝试一下。