Asp.net mvc TryUpdateModel的实例,ASP.NETMVC3

Asp.net mvc TryUpdateModel的实例,ASP.NETMVC3,asp.net-mvc,model,controller,Asp.net Mvc,Model,Controller,我不明白,如何在使用TryUpdateModel的同时保存MVC架构 如果我没有弄错的话,使用DataContext必须在模型中。那么这样的代码, var db=new TestEverybody();//it is class, which was generated by EntityFramework var currentTesting=db.Testing.(t => t.id == id).First(); 必须位于模型中,而不是控制器中,不是吗 但是TryUpdateMo

我不明白,如何在使用TryUpdateModel的同时保存MVC架构

如果我没有弄错的话,使用DataContext必须在模型中。那么这样的代码,

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架构?我们在控制器中使用数据库,而不是在特殊的模型类中

那么,在实际项目中使用TryUpdateModel的最佳方式是什么

所以,这些代码必须位于模型中,而不是控制器中,不是吗

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

由于OP的要求,这里有一个ViewModel模式的示例,或者我喜欢称之为ASP.NET MVC done Rightly

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

  • 您应该只将所需的信息传递给视图
  • 通常,您需要添加额外的视图元数据(例如标题/描述属性)。这些不属于您的实体
  • 使用TryUpdateModel/UpdateModel是错误的。不要使用(我会解释原因)
  • 视图模型与实体完全匹配是非常罕见的。人们通常会在实体中添加额外的cruft,或者仅仅使用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;}
    }
    
    假设您有一个简单的表单,其中用户只能更新产品的
    名称
    说明
    。但是您使用的是(非常贪婪的)TryUpdateModel

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

        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");
        }
    
    Name=WhatverIWant&Description=UnluckyFool&Price=0

    NET MVC模型绑定器将检查输入表单集合,查看实体上是否存在这些属性,并自动为您绑定它们。因此,当您对刚从数据库检索到的实体调用“TryUpdateModel”时,所有匹配的属性都将更新(包括价格!)。是时候换个新的选择了

    视图特定模型 它只包含视图中所需的属性。注意,我们还添加了一些验证属性、显示属性和一些特定于mvc的属性

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

    @Html.EditorFor(model => model)
    
    Mvc将检查我们添加到视图模型中的所有属性,并自动连接验证、标签和正确的输入字段(即用于描述的文本区域)

    张贴表格 从这段代码中可以明显看出它的作用。更新实体时不会产生任何不良影响,因为我们在实体上显式设置属性


    我希望这足以解释视图模型模式,让您愿意使用它。

    我的建议是,在实际项目中,不要使用它。您应该使用视图特定的模型,并在视图模型的属性与要更新的实体上的属性之间进行映射。TryUpdateModel/UpdateModel是贪婪的,最终会咬到你。这是MVVM模式吗?我在哪里可以读到你的方法?不,这是MVC(正确完成)。我在下面添加了一个答案来解释。希望这有帮助。谢谢。自动属性映射的最佳方式是什么?“model”只有名称和描述,因此如果我尝试使用ApplyCurrentValues,价格将替换为null,不是吗?您可以使用类似AutoMapper的工具自动将实体的属性映射到viewmodel。在我看来(以及AutoMapper组中的大多数人),您不应该反过来映射(从viewmodel映射回实体),因为这可能会导致意外的结果。我不理解这个问题。您不能避免从viewModel映射到模型,您也不应该这样做。这就是上面的要点,向您展示了如何在视图特定模型和实体之间进行映射。+1这是一个很好的答案,帮助我克服了应用程序中遇到的一些问题。这似乎总是有很多重复,但您的回答说明了为什么这是一个好主意-它实际上使您的应用程序更易于开发和维护。@BenFoster如果您使用带有要包括/排除的字符串列表的TryUpdateModel,这不就消除了它的攻击性吗?您不能在ActionResult参数中指定Bind属性以防止过度过帐吗?我宁愿这样做,也不愿像您在示例中那样为每个要更新的属性指定正确的赋值语句。这通常仅用于模拟测试,因此您的控制器可以在其默认构造函数中创建
    ActulRepository
    的实例。然而,新手开发人员可能会看到这一点,并不知如何在不在本地实例化的情况下将其实际回购协议放入
    \u存储库
    。@JoeBrockhaus,我同意,因为我是他们中的一员=)
    
    [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)
    }