模型继承与测试性

在软件开发过程中,经常需要在领域模型中使用继承。例如,可能需要对一本书进行建模,其中包含一系列参考文献。每条参考文献可能指向杂志文章、另一本书、网页等。因此,创建了一个名为Reference的抽象类,以及几个用于不同类型引用的子类。也可能想要为模型创建一个并行的继承链。

假设有一个参考文献列表。每一行都有一个链接到可以编辑相应参考文献对象的页面。虽然不同类型的参考文献有不同的字段需要编辑,但不想为每种类型创建一个单独的页面。显示参考文献很简单:只需在表单中调用Html.EditorFor(),它就会为特定类型的参考文献生成必要的字段。问题是,如何获取新的值。

假设有以下操作方法:

[HttpPost] public ActionResult Update(ReferenceModel model) { // ... }

默认的绑定器会尝试创建一个ReferenceModel的实例,但会失败,因为这是一个抽象类。因此,需要使用一个不同的绑定器。这个绑定器足够智能,能够创建具体类型的实例。

实现SubclassingBinder

为了实现这一点,需要提供模型类型的名称。将通过一个名为ModelType的隐藏字段来实现:

<input type="hidden" name="ModelType" value="<%= this.Model.GetType() %>" />

有人会倾向于重写CreateModel方法,但这还不够。模型会被创建,但它不会被填充为子类特定的属性。绑定器仍然会使用基抽象类的元数据,因此特定于具体类的属性将不会被获取。

因此,将重写BindModel方法并“纠正”模型类型,然后让绑定器为创建并绑定请求类型的模型。以下是代码:

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (bindingContext.ValueProvider.ContainsPrefix("ModelType")) { var typeName = (string)bindingContext.ValueProvider.GetValue("ModelType").ConvertTo(typeof(string)); var modelType = Type.GetType(typeName); bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, modelType); } return base.BindModel(controllerContext, bindingContext); }

有测试吗?

[Test] public void BinderCreatesArticleModelWithValues() { var response = new TestSession().Post("/Sample/UpdateReference", new { ModelType = typeof(ArticleModel).ToString(), ArticleName = NEW_ARTICLE_NAME }); Assert.IsInstanceOf(response.ActionMethodParameters["model"]); var model = (ArticleModel)response.ActionMethodParameters["model"]; Assert.AreEqual(NEW_ARTICLE_NAME, model.ArticleName); }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485