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

本文关键字:在哪里 代码 视图 模型 构建 | 更新日期: 2023-09-27 18:28:09

在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);
        }

Generic Select List是一个简单的类,用于保存助手@html.dropdownforSelectList中的数据。我构建了这些selectlist,还为控制器代码中的视图模型构建了类似的数据配置。托管此代码的控制器总共有109行代码,因此它并不庞大或失控。然而,我一直在努力减少冗余,有时//build employee list中的代码最终会被复制粘贴(呃,我讨厌复制粘贴)到其他控制器中。

有更好的地方存放这个代码吗?我应该使用工厂模式来构建这些选择列表/其他视图数据对象的数据吗?

编辑

谢谢你的帮助。以下是我最终做的事情。我最终在泛型选择列表类中创建了一个方法,该方法与Richard和Jesse建议的.ToSelectList(…)非常相似:

    public class GenericSelectList
{
    public List<GenericListItem> Values { get; set; }
    public int StartValue { get; set; }
    public string Message { get; set; }
    public GenericSelectList(int StartValue = 0, string Message = "select")
    {
        Values = new List<GenericListItem>();
        this.StartValue = StartValue;
        this.Message = Message;
    }
    public void BuildValues<T>(List<T> items, Func<T, int> value, Func<T, string> text) where T : class
    {
        this.Values = items.Select(f => new GenericListItem()
        {
            Id = value(f),
            DisplayFields = text(f)
        }).ToList();
    }
}

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

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

然而,除此之外,视图模型的创建在控制器中是非常好的。正如已经注意到的,生成选择列表的方式可以变得更简单(更不用说可重用)。

下面是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;
}

在您的视图模型中,您可以添加这样的属性:

IEnumerable<SelectListItem> Employees { get; set; }

在您的控制器内(我假设您的回购正在返回IEnumberable):

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)

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

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

您可以按照自己的意愿构建代码。使用标准技术来处理复杂性,例如辅助方法和(谨慎地)抽象。简单就足够了,不需要太复杂。

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

在我看来,控制器会做一些事情,比如更新会话、cookie,然后返回视图。我从不使用它来排序任何数据或创建要发送到视图的对象。

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

我添加到一个助手类中的所有这些东西。每次我觉得出现复制粘贴时,我都会在助手中创建一个新方法,然后调用它。

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

Martyn

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

2-您可以更改该扩展方法以使用泛型,该泛型针对IEnumerable<T>工作,并基于Expression输入生成GenericSetList。优点-您的方法现在是真正通用的,即只写一次并重复使用。您可以通过将它与1组合的方式来实现这一点,这样您的每类1函数就可以使用泛型来避免重复。缺点-假设在使用表达式等时感到舒适。

3-您可以有返回ViewModels的工厂方法。

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

编辑-这里是第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

示例用法:

    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"}})