【问题标题】:Best way to populate SelectList for ViewModel on GET/POST在 GET/POST 上为 ViewModel 填充 SelectList 的最佳方法
【发布时间】:2011-11-28 01:05:55
【问题描述】:

我有以下 ViewModel:

public class EditViewModel
{
    public int FooType { get; set; }
    public IEnumerable<SelectListItem> 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# asp.net-mvc asp.net-mvc-3 code-reuse


    【解决方案1】:

    鉴于 c# 是一种面向对象的语言,因此有很多可用的选项。

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

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

    并在设置模型时调用它

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

    如果您想真正进阶,您可以使用 ModelFactory 为您创建 FooType 模型,并使用预先填充的 FooType 属性,因此控制器根本不需要担心。

    有很多选择,您只需要选择最适合您的一个即可。

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

    【讨论】:

      【解决方案2】:

      我以前在模型中做过(当它是那个项目团队的编码实践时),但这取决于你关于什么是“业务逻辑”和什么是“数据访问”的理念,以及什么属于模型与控制器。存在不同且合理的意见。

      模型,你需要一个可以为 FooType 的类型:

      public class EditViewModel
      {
          public int? FooType { get; set; }
          public IEnumerable<SelectListItem> 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);
      }
      

      The View(没有 Barbara Wawa):

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

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

      型号:

      public class EditViewModel
      {
          public int? FooType { get; set; }
          public IEnumerable<int?> 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))
      

      【讨论】:

        【解决方案3】:

        在“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<int?> FooTypes
            {
                get
                {
                    return Repository.GetFooTypes();
                }
            }
        
            private IRepository Repository
            {
                get
                {
                    return ServiceLocator.Current.GetInstance<IRepository>();
                }
            }   
        }
        

        上述带有“依赖查找”的代码现在使用对第三方库的依赖,在本例中是公共服务定位器库。

        我的问题是如何将上面的代码替换为“依赖注入”? ViewModel 本身确实很容易实现,就像这样:

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

        问题是如何让 ViewModel 注入一个实现,当 ASP.NET MVC 框架将实例化“EditViewModel”并将其作为参数发送到诸如 tihs 方法签名之类的 Action 方法中时:

        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 教程似乎没有提供任何好的解决方案。 在以下页面的“处理编辑”部分(方法 'public ActionResult Edit(...)' )中,他们以与您正在阅读的这个 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 操作方法参数时,我创建了一个具有更具体主题的新问题,因此希望更有可能有解决方案的人会找到它并发布一个好的答案: Constructor injection of a View Model instance used as an Action method parameter

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-22
          • 2011-12-15
          • 2015-03-15
          • 1970-01-01
          • 2020-01-08
          • 2015-12-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多