【问题标题】:MVC: Should we have any if else OR other logical statement in View?MVC:我们是否应该在 View 中有任何 if else 或其他逻辑语句?
【发布时间】:2016-02-25 10:17:26
【问题描述】:

这是我的模型:

public class Employee
{
    public string EmployeeName { get; set; }
    public string Address { get; set; }
    public DateTime DateOfBirth { get; set; }
    public int Salary { get; set; }
}

这是我的看法:

<div>
    Employee Detail<br />
    Employee Name : @Model.emp.EmployeeName<br />
    Address : @Model.emp.Address <br />
    Age : @Model.Age
    <span style="color:@Model.SalaryColor">Salary : @Model.emp.Salary</span>
     @if (Model.Salary > 20000)
     {
         <div1>..</div1>
     }
     else
     {
        <div2>..</div2>
     }
</div>

有了这个 if/else,我在 MVC 的 UI 层/视图中插入业务逻辑。 但我不知道如何避免这种情况?

我知道这会使 UI 与不好的业务逻辑紧密绑定。有什么办法可以避免这种情况?

【问题讨论】:

  • 简而言之……只要是纯粹的表现形式,视图中的流控制就可以了。
  • Angualr 或 Knockout 适合这种类型的东西,你可以绑定 Visible 属性,这样你就可以显示或隐藏绑定到某个属性的 div

标签: asp.net-mvc asp.net-mvc-3 model-view-controller separation-of-concerns


【解决方案1】:

问题不在于if。有时您必须根据模型的某些方面以不同的方式呈现事物。 “问题”是Salary &gt; 20000“逻辑”。虽然您给出的示例可能很简单,以至于它并不是非常重要,但理想情况下,这种事情并不真正属于视图。

执行此操作的标准方法是使用ViewModel。该对象的存在是为了将视图与数据模型的实现细节分离,并封装一些表示层逻辑。如果您的数据模型是例如一个实体框架代理,它有很多约束和行为,迫使它为你的表示层变成错误的“形状”。在这个特定的例子中,你的ViewModel 可能有一个类似于bool ShowHighSalarySection 的属性——要么由ViewModel 计算,要么由一些外部业务代码计算并存储在ViewModel 中。

ViewModel 的精确形状很大程度上取决于您所遵循的 MV* 的特定风格,但例如:-

  1. 带有数据模型构造函数的简单ViewModel:-
    public class EmployeeViewModel
    {
      private readonly Employee model;

      public EmployeeViewModel(Employee model)
      {
        this.model = model;
      }

      public bool ShowHighSalarySection { get { return this.model.Salary > 20000; } }

      // Some people like to have the ViewModel explicitly only expose
      // the data the view needs.
      public string EmployeeName { get { return this.model.EmployeeName; } }
      public string Address { get { return this.model.Address; } }
      public DateTime DateOfBirth { get { return this.model.DateOfBirth } }
      public int Salary { get { return this.model.Salary; } }

      // Or, if boilerplate freaks you out, and you don't have the tools
      // to automate it, you can even just expose the data model and its
      // properties directly as e.g. Model.Employee.EmployeeName.
      // This gives you some abstraction very cheaply, but obviously
      // still leaves your views somewhat coupled to your data model:-
      public Employee Employee { get { return this.Model; } }
    }

然后在你看来:-

if (Model.ShowHighSalarySection) { ...
  1. 您还可以使用更“愚蠢”的ViewModel 以及AutoMapper 等映射器:-
    public class EmployeeViewModel
    {
      // Note that this ViewModel doesn't even know the data model *exists*!
      public string EmployeeName { get; set; }
      public string Address { get; set; }
      public DateTime DateOfBirth { get; set; }
      public int Salary { get; set; }
      public bool ShowHighSalarySection { get; set; }
    }

在您的控制器中:-

    var query = db.Employees.Select(x => ...);

    // Some people like the "magic" of a mapper, especially when
    // all you're doing is reasonably "dumb" transformations.
    // Some people don't like that there could be somewhat complicated
    // things going on behind the curtain.
    // The huge advantage of this type of code is that you can usually
    // leverage some kind of query projection and have the mapping
    // take place on your database, rather than pulling the entire
    // entity graph into memory.
    var viewModel = this.mapper.Project()
                               .To<EmployeeViewModel>(query)
                               .FirstOrDefault();

    return View(viewModel);
  1. 有时业务规则非常复杂,您可能需要一些明确的处理代码来明确负责应用业务规则:-
    public class GetEmployeeQueryHandler
    {
      private readonly IEmployeeStore store;

      public GetEmployeeQueryHandler(IEmployeeStore store)
      {
        this.store = store;
      }

      public EmployeeViewModel Handle(EmployeeQuery query)
      {
        var employee = store.Get(EmployeeQuery.Identity);
        var viewModel = new EmployeeViewModel()
        {
          // Could leverage some automatic mapping to do the
          // "dumb" part of the mapping if you want tidiness
          // or query projection.
        }

        // This is probably excessive for a calculation as simple
        // as this one, but we've managed to separate ourselves
        // totally from both the database *and* the rendering
        // engine.
        // This lets us e.g. test our important business logic in
        // complete isolation, or reuse it elsewhere.
        viewModel.ShowHighSalarySection = employee.Salary > store.HighSalaryThreshold;
      }
    }

具体的去向取决于您的架构、您的理念以及您准备接受多少样板(或工具)以换取整洁。 ViewModel 所做的主要事情是它允许您将业务规则从视图中取出并放到适当的地方(更安全、更可测试、更可重用等)。

【讨论】:

    【解决方案2】:

    如果 div1 和 div2 的内容或多或少相似,最好的办法是完全放弃 if/else,也许通过在模型中增加一些字段。

    但是如果两者完全不同,可以有if/else,这是可以接受的。但要注意什么是业务逻辑,什么不是业务逻辑。考虑:

    public class Employee
    {
        public int Salary { get; set; }
        private const int SALARY_CAP = 20000;
        public bool IsSalaryAboveTheCap { get { return Salary > SALARY_CAP; } }
    }
    

    在视图中:

     @if (Model.IsSalaryAboveTheCap)
     {
         <div1>..</div1>
     }
     else
     {
        <div2>..</div2>
     }
    

    工资高于某个上限的事实本身并不是业务逻辑。然而,上限的价值是。

    可以说,尽管视图模型也不是放置此逻辑的最佳位置。您是否有任何业务逻辑层可以从那里获得此信息?

    【讨论】:

      猜你喜欢
      • 2018-03-12
      • 1970-01-01
      • 2014-02-19
      • 1970-01-01
      • 1970-01-01
      • 2019-11-24
      • 2022-01-07
      • 1970-01-01
      • 2022-08-15
      相关资源
      最近更新 更多