5

定义

在 ASP.NET MVC 中,有两种方法可以在操作中进行模型绑定。我们将这些称为“绑定参数方式”和“UpdateModel 方式”。他们都做几乎完全相同的事情,而且他们以几乎完全相同的方式做这件事:

    public ActionResult UpdateWithBindArguments(Foo model)
    {
        Repository.Update(model);
        // error handling removed
        return RedirectToAction(...)
    }

    public ActionResult UpdateWithUpdateModel()
    {
        Foo model; 
        UpdateModel(model); // part of MVC framework
        Repository.Update(model);
        // error handling removed
        return RedirectToAction(...)
    }

正如我所说,这些几乎完全相同。第一个可能更具可读性,但我可以克服它。

两种测试方法

我认为,重要的区别在于您如何对它们进行单元测试:

    [TestMethod]
    public void TestUpdateWithBindArguments()
    {
       var model = new Foo() { PropertyName = "Bar" };
       var controller = new FooController();

       var result = controller.UpdateWithBindArguments(model);

       // assert
    }

    [TestMethod]
    public void TestUpdateWithUpdateModel()
    {
       var formData = new FormCollection() { { "PropertyName", "Bar" } };
       var controller = new FooController();
       controller.ValueProvider = formData.ToValueProvider();

       var result = controller.UpdateWithUpdateModel();

       // assert
    }

第一种方法使用强静态类型构建模型。第二个使用名称/值对构造提交的用户数据。我发现第一种方法更容易阅读,但第二种方法更接近于网站调用控制器时实际发生的情况。

出于这个问题范围之外的原因,我从来没有说服人们应该使用 lambda 表达式而不是字符串来构建 aspx 页面来进行模型绑定。我很乐意与您讨论,但我们不要在这里讨论。出于这个问题的目的,让我们理所当然地认为我将使用内置的 HtmlHelper 方法,这些方法采用字符串而不是采用 lambda 表达式的扩展。因此,第二种方法使用名称/值对作为针对 aspx 页面的“动态”性质的非正式测试具有一定的价值。当然,它不能取代针对站点的集成测试。

问题(终于!)

我看到了这两种方法的优点和缺点。我的问题是,是否有一个非常有力的论据支持我缺少的一种方法?

编辑我正在寻找客观的答案。我正在寻找为什么一种方法比另一种更好的非显而易见的原因,而不是试图进行民意调查。

4

4 回答 4

3

在测试方面,在我看来,将对象直接传递给 action 方法更自然。无需填充 ValueProviderDictionary。

需要其他方法的原因是您可能需要控制要绑定的对象的实例化。DefaultModelBinder 只是查找默认构造函数并调用它。

但在某些情况下,您可能需要在将对象绑定到表单值之前自己创建对象。这就是 UpdateModel 发挥作用的地方。

于 2009-02-07T05:48:10.497 回答
0

我会倾向于这样写

public ActionResult Update(int id, string name)
{
  Person person = PersonRepository.GetByID(id);
  //Do any error handling
  person.Name = name;
  //Do any error handling
  PersonRepository.Update(person);
}

然后像这样测试(使用RhinoMocks)

Person person = new Person();
var mockPersonRepository = MockRepository.GenerateMock<IPersonRepository>();
mockPersonRepository.Expect(x => x.GetByID(1)).Return(person);
mockPersonRepository.Expect(x => x.Update(person));

var controller = new MyController(mockPersonRepository);
controller.Update(1, "Hello");
Assert("Hello", person.Name);
mockPersonRepository.VerifyAllExpectations();

所以我的回答是,以上都不是:-)

于 2009-02-06T16:00:27.797 回答
0

我总是选择对上下文透明以防止噪音进入测试,所以第一种方法是更好的 IMO。

只是好奇您对用于 HTML 生成的表达式的魔术字符串的喜好。是它们对编译器的不可见性,对重构的抵制,还是您讨厌智能感知。:) 哦,我现在已经完成了,开始了一个题外话辩论。

于 2009-02-06T17:55:40.607 回答
0

我喜欢绑定到模型对象的第一个选项。这将控制器从模型和视图的细节中解放出来。因此,我可以根据需要更改模型和视图,而不会影响我的控制器。测试隔离也因此变得更容易。

对于复杂的域模型或只关注部分域的视图,我创建了一个视图模型,它对视图中的数据而不是域中的数据进行建模。然后我编写映射代码或使用对象映射器将视图模型映射到我的域模型。这减少了我的视图和我的域之间的耦合。

在将单个参数传递给控制器​​方法时,我将不得不不同意 Peter Morris 的观点。起初这看起来不错,但一旦您开始使用大型表单,很快就会变得很痛苦。此外,这增加了控制器和视图之间的耦合。

于 2009-02-07T03:41:53.857 回答