TryUpdateModel,ASP .NET MVC 3的真实示例

Sir*_*lly 29 asp.net-mvc controller model

我无法理解,如何使用TryUpdateModel并同时保存MVC架构.

如果我没有弄错的话,使用datacontexts必须在模型中.所以,这样的代码

var db=new TestEverybody();//it is class, which was generated by EntityFramework 
var currentTesting=db.Testing.(t => t.id == id).First();
Run Code Online (Sandbox Code Playgroud)

必须位于模型中,而不是控制器中,不是吗?

但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");
    }
Run Code Online (Sandbox Code Playgroud)

这种方式不会打破MVC架构吗?我们在控制器中使用数据库,而不是在特殊的Model类中.

那么,在真实项目中使用TryUpdateModel的最佳方法是什么?

Ben*_*ter 115

由于OP问,这是一个ViewModel模式的例子,或者我喜欢称之为 - ASP.NET MVC正确完成.

那么为什么要使用视图特定的模型

  1. 您应该只将信息传递给您需要的视图.
  2. 通常,您需要添加其他视图元数据(例如标题/描述属性).这些不属于您的实体.
  3. 使用TryUpdateModel/UpdateModel是错误的.不要使用(我会解释原因).
  4. 您的视图模型与您的实体完全匹配的情况非常罕见.人们经常最终只是使用ViewBag而不是强类型的视图模型属性来为他们的实体添加额外的内容或者(并不是更好).
  5. 如果您正在使用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;}
}
Run Code Online (Sandbox Code Playgroud)

我们假设您有一个简单的表单,用户只能更新产品NameDescription产品.但是你正在使用(非常贪婪的)TryUpdateModel.

所以我使用任意数量的工具(如Fiddler)自己构建一个POST并发送以下内容:

NAME = WhatverIWant和说明= UnluckyFool及价格= 0

那么ASP.NET MVC模型绑定器将检查输入表单集合,看到这些属性存在于您的实体上并自动为您绑定它们.因此,当您刚刚从数据库中检索到的实体上调用"TryUpdateModel"时,将更新所有匹配的属性(包括Price!).是时候换新选择了.

查看特定型号

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;}
}
Run Code Online (Sandbox Code Playgroud)

这只包含我们在视图中需要的属性.请注意,我们还添加了一些验证属性,显示属性和一些mvc特定属性.

不受我们在视图模型中的限制,它可以使您的视图更清晰.例如,我们可以通过在视图中包含以下内容来渲染我们的整个编辑表单:

@Html.EditorFor(model => model)
Run Code Online (Sandbox Code Playgroud)

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)
}
Run Code Online (Sandbox Code Playgroud)

从这段代码中可以看出它的作用.当我们更新实体时,我们没有任何不良影响,因为我们在实体上明确设置属性.

我希望这能够解释View-Model模式,足以让你想要使用它.

  • +1这是一个很好的答案,帮助我克服了我的应用程序面临的一些问题.这似乎总是很多重复,但你的答案显示了为什么这是一个好主意 - 它实际上使你的应用程序更容易开发和更容易维护. (5认同)
  • 您可以使用AutoMapper之类的内容自动将实体的属性映射到viewmodel.在我看来(以及AutoMapper组中的大多数人)你不应该反过来(从你的视图模型回到你的实体),这可能会导致意想不到的结果. (4认同)
  • 我不明白这个问题.您无法避免从viewModel映射到模型,也不应该.这就是上述要点,向您展示如何在视图特定模型和实体之间进行MAP. (2认同)

Dar*_*rov 19

那么,这样的代码必须位于Model中,而不是Controller中,不是吗?

不必要.我个人更喜欢将数据访问代码放在存储库中.然后使用构造函数注入将一些特定的存储库实现传递给控制器​​(例如,如果我使用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");
    }
}
Run Code Online (Sandbox Code Playgroud)