在GET / POST上为ViewModel填充SelectList的最佳方法

我有以下ViewModel:

public class EditViewModel { public int FooType { get; set; } public IEnumerable FooTypes { get; set; } } 

我最初在我的编辑操作中填充它,如下所示:

 public ActionResult Edit(int id) { EditViewModel model = new EditViewModel(); model.FooTypes = new SelectList(repository.GetFooTypes(), "Id", "Value"); return View(model); } 

当我创建POST的操作时,我必须重复相同的代码:

 public ActionResult Edit(int id, EditViewModel model) { if( !ModelState.IsValid ) { model.FooTypes = new SelectList(repository.GetFooTypes(), "Id", "Value"); return View(model); } return RedirectToAction("Index"); } 

我不喜欢在两个不同的位置使用此代码。 是否有任何常见的做法将其重构为一个点,所以我不需要重复这段代码?

鉴于c#是面向对象的语言,有很多选项可供选择。

最简单的方法是将其包装在控制器内的方法中:

 private SelectList GetFooTypesList() { return new SelectList(repository.GetFooTypes(), "Id", "Value); } 

并在设置模型时调用它

或者如果您在多个类中使用它,您可以在另一个接受存储库或IEnumerable作为参数的类中创建一个辅助方法。

如果你想获得真正的高级,你可以使用ModelFactory为你创建FooType模型,使用预先填充的FooType属性,这样控制器根本不需要担心它。

有很多选择,您只需选择最适合您的选项。

我个人的偏好是控制器中的简单帮助方法。

我之前已经在模型中完成了它(当时它是该项目团队的编码实践),但这取决于你对什么是“业务逻辑”和什么是“数据访问”的理念,以及模型与控制器中的内容。 存在不同且合理的观点。

模型,您需要FooType的可空类型:

 public class EditViewModel { public int? FooType { get; set; } public IEnumerable GetFooTypes(object selectedFooType = null) { return new SelectList(repository.GetFooTypes(), "Id", "Value", selectedFooType); } } 

“获取”控制器,您需要首先创建模型以确保视图中的Model属性可用:

 public ActionResult Edit(int id) { EditViewModel model = new EditViewModel(); return View(model); } 

观点(没有Barbara Wawa):

 @Html.DropDownListFor(m => m.FooType, Model.GetFooTypes(Model.FooType)) 

从模型中取出“视图内容”的替代方法可能如下所示:

模型:

 public class EditViewModel { public int? FooType { get; set; } public IEnumerable FooTypes { get { // declare/define repository in your model somewhere return repository.GetFooTypes(); } } } 

视图:

 @Html.DropDownListFor(m => m.FooType, new SelectList(Model.FooTypes, "Id", "Value", Model.FooType)) 

在“nekno”(9月30日22:19回答)的回复中,ViewModel有两种选择,它们返回’IEnumerable ‘或’IEnumerable ‘。 这两种方法都使用了存储库但没有实际创建它,所以我想稍微扩展一下代码示例,并选择第二种方法,即类型为’IEnumerable ‘的类:

 using Microsoft.Practices.ServiceLocation; // ServiceLocator , http://commonservicelocator.codeplex.com/ using MyOwnRepositoryNameSpace; // IRepository public class EditViewModel { public int? FooType { get; set; } public IEnumerable FooTypes { get { return Repository.GetFooTypes(); } } private IRepository Repository { get { return ServiceLocator.Current.GetInstance(); } } } 

上面带有“Dependecy Lookup”的代码现在使用依赖于第三方库,在本例中是Common Service Locator库。

我的问题是如何将上述代码替换为“dependency injection”? 实现ViewModel本身确实非常简单,就像这样:

 using MyOwnRepositoryNameSpace; // IRepository public class EditViewModel { private readonly IRepository _repository; public EditViewModel(IRepository repository) { _repository = repository; } public int? FooType { get; set; } public IEnumerable FooTypes { get { return _repository.GetFooTypes(); } } } 

问题是当ASP.NET MVC框架将实例化’EditViewModel’并将其作为参数发送到Action方法(如tihs方法签名)时,如何使ViewModel注入实现;

 public ActionResult Edit(int id, EditViewModel model) { // How do we make the framework instantiate the above 'EditViewModel' with an implementation of 'IRepository' when the Action method is invoked ??? 

到目前为止,官方MVC教程似乎没有提供任何好的解决方案。 在以下页面的“处理编辑”(方法’公共ActionResult编辑(…)’)部分中,他们以与您正在阅读的此stackoverflow问题的海报类似的方式复制选项的创建。

http://www.asp.net/mvc/tutorials/mvc-music-store/mvc-music-store-part-5

http://mvcmusicstore.codeplex.com/SourceControl/changeset/view/d9f25c5263ed#MvcMusicStore%2fControllers%2fStoreManagerController.cs

如果有关于如何使用数据检索器(例如存储库)构建框架注入视图模型的解决方案,那么我相信可能会使用“IModelBinderProvider”或“IModelBinder”的某些实现,但我已经尝试了这些真正成功……

那么,任何人都可以使用ASP.NET MVC 3代码提供一个完整工作示例的链接,该代码可以将数据检索器注入到框架实例化的视图模型的构造函数中,并将其作为参数发送到操作方法中吗?

更新2012-01-01 :对于那些关于构造函数注入ViewModel实例的特定问题的解决方案感兴趣的人,当框架实例化它并将其作为参数发送到MVC Action Method参数时,我创建了一个新问题更具体的主题,因此希望更有可能有解决方案的人会找到并发布一个好的答案: 构造函数注入用作Action方法参数的View Model实例