C# 构建视图模型的代码应该放在哪里?

C# 构建视图模型的代码应该放在哪里?,c#,asp.net-mvc-3,viewmodel,C#,Asp.net Mvc 3,Viewmodel,在asp.net的MVC3中构建视图模型时,代码应该去哪里实例化该视图模型的对象?除了查询数据库的代码外,我现在主要是在控制器中进行。下面是一个代码示例: 视图模型: public class WorkListVM { //list for employees [Display(Name = "Select A Employee")] [Required] public int? EmployeeId { get; set; } public Generi

在asp.net的MVC3中构建视图模型时,代码应该去哪里实例化该视图模型的对象?除了查询数据库的代码外,我现在主要是在控制器中进行。下面是一个代码示例:

视图模型:

public class WorkListVM
{
    //list for employees
    [Display(Name = "Select A Employee")]
    [Required]
    public int? EmployeeId { get; set; }
    public GenericSelectList EmployeeList { get; set; }
}
控制器代码:

        //build view model
        var vm = new WorkListVM();

        //build employee list
        vm.EmployeeList = new GenericSelectList(0,"-- Select Employee --");
        var employees = new List<Employee>();
        using (var gr = new GenericRepo<Employee>())
        {
            employees = gr.Get().ToList();
        }
        foreach(var employee in employees)
        {
            var gl = new GenericListItem();
            gl.Id = employee.EmployeeId;
            gl.DisplayFields = employee.FirstName + " " + employee.LastName;
            vm.EmployeeList.Values.Add(gl);
        }

如果您的目标是避免冗余代码,那么应该将公共部分提取到helper方法中。在最简单的形式中,您可以为此使用静态方法

在控制器中构建视图模型通常是正确的方法,除非有特殊原因反对


您可以按任何方式构造代码。使用标准技术来处理复杂性,例如辅助方法和(少量)抽象。简单就足够了,不需要复杂。

1-您可以向
IEnumerable
IQueryable
添加一个扩展方法,该方法返回您的
GenericSelectList
。优点-您可以对任何员工集合重复使用,即筛选列表和非常好的调用语法;您可以根据具体情况自定义选择列表的形成方式。缺点-您必须为每种类型编写其中一种方法

2-您可以将该扩展方法更改为使用泛型,该泛型针对
IEnumerable
工作,并基于
表达式
输入生成
GenericSetList
。优点-您的方法现在是真正通用的,即编写一次并重复使用。您可以这样做,即它与1相结合,这样每个类1的函数就可以使用泛型来保存重复。缺点-假设在使用表达式等时感到舒适

3-可以使用返回ViewModels的factory方法

这些东西也可以结合使用,并且应该有助于删除复制/粘贴代码并促进重用和可测试性

编辑-这里有一种方法可以实现第2条(在VB中,但是翻译成C#很简单)。然后,我会为输入的有用排列添加额外的负载

Imports System.Runtime.CompilerServices
Imports System.Linq.Expressions

Module IQueryableExtensions

    <Extension()>
    Public Function ToSelectList(Of T)(source As IEnumerable(Of T), nameExpression As Expression(Of Func(Of T, String)), valueExpression As Expression(Of Func(Of T, String)), selectedExpression As Expression(Of Func(Of T, Boolean)), additionalItems As IEnumerable(Of SelectListItem)) As IEnumerable(Of SelectListItem)
        Return additionalItems.Union(
            source.Select(Function(x) New SelectListItem With {
                              .Text = nameExpression.Compile.Invoke(x),
                              .Value = valueExpression.Compile.Invoke(x),
                              .Selected = selectedExpression.Compile.Invoke(x)
                          }
                      )
                  )
    End Function

End Module

我不知道这是否是正确的方法,但我使用它,它帮助我控制一切

我眼中的控制器执行更新会话、cookies等操作,然后返回视图。我从不使用它来排序任何数据或创建要发送到视图的对象

(如果是一对一的话,我可能会作弊:p)

我添加到helper类中的所有这些东西。每次我感觉到一个复制粘贴开始了,我就在我的助手中创建一个新方法,并调用它

另外,当3天后你需要使用这种方法时,你会有一种很好的幸福感,而这一切都在等待着你


Martyn

如果创建视图模型的业务逻辑非常复杂,我通常会将其提取到一个助手方法中,我可以独立于控制器进行测试

但是,撇开这一点不谈,您在控制器中创建的视图模型是完全正确的。如前所述,如何使生成选择列表变得更简单(更不用说可重用)

以下是IEnumerable的ToSelectList扩展以及用法示例:

public static List<SelectListItem> ToSelectList<T>( this IEnumerable<T> enumerable, Func<T, string> value, Func<T, string> text, string defaultOption)
{
    var items = enumerable.Select(f => new SelectListItem()
                                          {
                                              Text = text(f) ,
                                              Value = value(f)
                                          }).ToList();

    if (!string.IsNullOrEmpty(defaultOption))
    {
                    items.Insert(0, new SelectListItem()
                        {
                            Text = defaultOption,
                            Value = string.Empty
                        });
    }

    return items;
}

如果将viewmodel返回到视图,则代码将进入控制器@白ジェームス - 是的,在这个actionresult方法的末尾是返回视图(vm)。视图是强类型的
@model WorkListVM
。有些代码被多次使用(在不同的控制器中)。有一个工厂包装这些viewmodel对象的创建以减少冗余难道没有意义吗?我可以这样做#2,因为我会为选择列表生成添加一个通用响应。然而,这需要反思。在存储库中使用反射是不明智的吗?你说反射是因为你想包含类名吗?还有其他的方法,给我一点时间,我会发布一个示例。@TravisJ-好了-一个没有反射的可能实现。谢谢你提供了一个非常详细的示例:)但是,我所说的反射是保留存储库的通用方面,不需要显式定义任何字段。因此,反射将确定要返回的对象的所有字段,然后将它们作为selectlistitem返回。在您的示例中,该方法是否会成为控制器中使用的扩展(保持存储库不变)?抱歉,我的vb有点弱。@TravisJ-这些是扩展方法,它们位于vb的模块中或C#的静态类中,我通常将其放入“扩展”命名空间和名称{typeName}扩展中-因此这将是IEnumerableExtensions,因为它扩展了IEnumerable。我可能会结合2和3,因此工厂方法将使用通用扩展作为构建ViewModel的一部分。它将处理Repository.Get()方法的输出,因此通常会从调用存储库的同一个对象调用它,而不是在存储库内部。还想在C#中使用它吗?因此,也许我应该使用反射来创建一个通用的帮助器方法来管理创建这些选择列表…+1-伟大的头脑都是一样的;)你帮我保存了一个翻译!非常好的扩展,谢谢你在这个主题上的输入:)@Jesse-你的扩展想法很好,但我决定将它作为我用于选择列表的类中的一个方法,这非常好!有关实现,请参见我的编辑。再次感谢!
    Dim People As New List(Of Person) From {New Person With {.Name = "Richard", .ID = 1}}

    Dim sl = People.ToSelectList(Function(p) p.Name,
                                 Function(p) p.ID,
                                 Function(p) p.ID = 1,
                                 {New SelectListItem With {.Value = 0,
                                                           .Text = "Please Select A Person"}})
public static List<SelectListItem> ToSelectList<T>( this IEnumerable<T> enumerable, Func<T, string> value, Func<T, string> text, string defaultOption)
{
    var items = enumerable.Select(f => new SelectListItem()
                                          {
                                              Text = text(f) ,
                                              Value = value(f)
                                          }).ToList();

    if (!string.IsNullOrEmpty(defaultOption))
    {
                    items.Insert(0, new SelectListItem()
                        {
                            Text = defaultOption,
                            Value = string.Empty
                        });
    }

    return items;
}
IEnumerable<SelectListItem> Employees { get; set; }
var employees = new IEnumerable<Employee>();
using (var gr = new GenericRepo<Employee>())
{
    employees = gr.Get();
}

vm.Employees = employees.ToSelectList(x=>x.FirstName + " " + x.LastName, x=>x.Id, "-- Select Employee --")
@Html.DropDownListFor(model => model.EmployeeId, Model.employees)