TryUpdateModel、ASP .NET MVC 3 的真实示例
Posted
技术标签:
【中文标题】TryUpdateModel、ASP .NET MVC 3 的真实示例【英文标题】:Real example of TryUpdateModel, ASP .NET MVC 3 【发布时间】:2011-10-12 05:26:27 【问题描述】:看不懂,如何使用 TryUpdateModel 和保存 MVC 架构的同时。
如果我没记错的话,使用数据上下文的工作必须在模型中。所以,这样的代码
var db=new TestEverybody();//it is class, which was generated by EntityFramework
var currentTesting=db.Testing.(t => t.id == id).First();
必须位于模型中,而不是控制器中,不是吗?
但 TryUpdateModel 的常见用法示例如下:
public ActionResult Edit(Testing obj)//Testing collection
var db = new TestEverybody();
var currentTesting=db.Testing.(t => t.id == obj.id).First();
TryUpdateModel(currentTesting);
db.SaveChanges();
return RedirectToAction("Index");
这种方式不会破坏 MVC 架构吗?我们在控制器中使用数据库,而不是在特殊的 Model 类中。
那么,在实际项目中使用 TryUpdateModel 的最佳方式是什么?
【问题讨论】:
我的建议,在实际项目中,不要使用它。您应该使用视图特定模型并在视图模型的属性和您要更新的实体上的属性之间进行映射。 TryUpdateModel/UpdateModel 很贪心,会咬你……最终。 是 MVVM 模式吗?我在哪里可以了解您的方法? 不,这是 MVC(正确完成)。我在下面添加了一个答案来解释。希望这会有所帮助。 【参考方案1】:既然 OP 提出要求,这里有一个 ViewModel 模式的示例,或者我喜欢称之为 - ASP.NET MVC 正确完成。
那么为什么要使用特定于视图的模型
-
您应该只将信息传递给它需要的视图。
通常您需要添加额外的视图元数据(例如标题/描述属性)。这些不属于您的实体。
使用 TryUpdateModel/UpdateModel 是错误的。不要使用(我会解释原因)。
您的视图模型与您的实体完全匹配是非常罕见的。人们通常最终会在他们的实体中添加额外的东西,或者(也不是更好)只使用 ViewBag 而不是强类型的视图模型属性。
如果您使用的是 ORM,您可能会遇到延迟加载属性 (N+1) 的问题。您的意见不应提出质疑。
我们将从一个简单的实体开始:
public class Product
public int Id get;set;
public string Name get;set;
public string Description get;set;
public decimal Price get;set;
假设您有一个简单的表单,用户可以仅更新产品的Name
和Description
。但是您正在使用(非常贪婪的)TryUpdateModel。
所以我使用任意数量的工具(如 Fiddler)自己构建一个 POST 并发送以下内容:
Name=WhatverIWant&Description=UnluckyFool&Price=0
ASP.NET MVC 模型绑定器将检查输入表单集合,查看这些属性是否存在于您的实体中并自动为您绑定它们。因此,当您在刚刚从数据库中检索到的实体上调用“TryUpdateModel”时,所有匹配的属性都将被更新(包括价格!)。是时候换个新选择了。
查看具体型号
public class EditProductViewModel
[HiddenInput]
public Guid Id get;set;
[Required]
[DisplayName("Product Name")]
public string Name get;set;
[Allowhtml]
[DataType(DataType.MultilineText)]
public string Description get;set;
这仅包含我们视图中需要的属性。请注意,我们还添加了一些验证属性、显示属性和一些 mvc 特定属性。
通过不受我们在视图模型中的限制,它可以使您的视图更加清晰。例如,我们可以通过在视图中显示以下内容来呈现整个编辑表单:
@Html.EditorFor(model => model)
Mvc 将检查我们添加到视图模型中的所有属性,并自动连接验证、标签和正确的输入字段(即用于描述的文本区域)。
发布表单
[HttpPost]
public ActionResult EditProduct(EditProductViewModel model)
var product = repository.GetById(model.Id);
if (product == null)
return HttpNotFound();
// input validation
if (ModelState.IsValid)
// map the properties we **actually** want to update
product.Name = model.Name;
product.Description = model.Description;
repository.Save(product);
return RedirectToAction("index");
return View(model)
从这段代码中可以很明显地看出它的作用。我们在更新实体时不会产生任何不良影响,因为我们是在实体上显式设置属性。
我希望这足以解释 View-Model 模式,让您想要使用它。
【讨论】:
谢谢。自动属性映射的最佳方法是什么? “model”只有Name和Description,所以如果我尝试使用ApplyCurrentValues,价格会被null代替,不是吗? 您可以使用 AutoMapper 之类的工具自动将实体的属性映射到您的视图模型。在我看来(以及 AutoMapper 组中的大多数人),你不应该再次映射(从你的视图模型回到你的实体),这可能会导致意想不到的结果。 我不明白这个问题。你不能避免从 viewModel 映射到模型,你也不应该。这就是上面的重点,向您展示如何在视图特定模型和实体之间进行映射。 +1 这是一个很好的答案,帮助我克服了我在应用程序中遇到的一些问题。这似乎总是有很多重复,但您的回答说明了为什么这是一个好主意 - 它实际上使您的应用程序更容易开发和维护。 @BenFoster 如果您将 TryUpdateModel 与要包含/排除的字符串列表一起使用,这不会消除它的激进性质吗?难道你不能在 ActionResult 参数中指定 Bind 属性来防止过度发布吗?我宁愿这样做,也不愿像您在示例中那样更新每个属性的正确赋值语句。【参考方案2】:所以,这样的代码必须位于模型中,而不是控制器中,不是吗?
不一定。我个人更喜欢将数据访问代码放在存储库中。然后使用构造函数注入将一些特定的存储库实现传递给控制器(例如,如果我使用的是 EF,我会编写一个 EF 存储库实现)。所以控制器看起来像这样:
public class HomeController: Controller
private readonly IMyRepository _repository;
public HomeController(IMyRepository repository)
_repository = repository;
public ActionResult Edit(int id)
var currentTesting = _repository.GetTesting(id);
TryUpdateModel(currentTesting);
_repository.SaveChanges();
return RedirectToAction("Index");
【讨论】:
展示(或解释)如何实际注入存储库可能会很有用,因为 MVC 会为您实例化控制器。这通常仅用于使用 Mocks 进行测试,因此您的 Controller 可以在其默认构造函数中创建ActulRepository
的实例。但是,新手开发人员可能会看到这一点,并且不知道如何在不本地实例化的情况下将他们的实际 Repo 放入 _repository
。
@JoeBrockhaus,我同意,因为我是其中之一 =)以上是关于TryUpdateModel、ASP .NET MVC 3 的真实示例的主要内容,如果未能解决你的问题,请参考以下文章