MVC:我们是否应该在 View 中有任何 if else 或其他逻辑语句?
MVC: Should we have any if else OR other logical statement in View?
这是我的模型:
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 与业务逻辑紧密绑定,这并不好。我有什么办法可以避免这种情况吗?
如果 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>
}
工资高于某个上限的事实本身并不是业务逻辑。然而,上限的价值是。
可以说,视图模型也不是放置此逻辑的最佳位置。您有任何业务逻辑层吗?
问题不在于 if
。有时您必须根据模型的某些方面以不同方式呈现事物。 “问题”是 Salary > 20000
“逻辑”。虽然您给出的示例可能足够简单,因此 非常 并不重要,但理想情况下,这种事情并不真正属于视图。
执行此操作的标准方法是使用 ViewModel
。该对象的存在是为了将视图与数据模型的实现细节分离,并封装一些表示层逻辑。如果您的数据模型是例如一个 Entity Framework 代理,它有很多约束和行为,迫使它进入表示层的错误“形状”。在这个特定的例子中,你的 ViewModel
可能有一个 属性 沿着 bool ShowHighSalarySection
的行 - 要么由 ViewModel
计算,要么由一些外部业务代码计算并存储在 ViewModel
.
你的 ViewModel
的精确形状将在很大程度上取决于你所遵循的 MV* 的特定风格,但是例如:-
- 带有数据模型构造函数的简单
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) { ...
- 您还可以使用更“愚蠢”的
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);
- 有时业务规则非常复杂,您可能需要一些明确的处理代码来明确负责应用业务规则:-
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;
}
}
确切的去向取决于您的体系结构、您的理念以及您准备在 return 中接受多少样板文件(或工具)以保持整洁。 ViewModel 所做的主要事情是它允许您将业务规则从视图中取出并放入适当的地方(更安全、更可测试、更可重用等)。
这是我的模型:
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 与业务逻辑紧密绑定,这并不好。我有什么办法可以避免这种情况吗?
如果 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>
}
工资高于某个上限的事实本身并不是业务逻辑。然而,上限的价值是。
可以说,视图模型也不是放置此逻辑的最佳位置。您有任何业务逻辑层吗?
问题不在于 if
。有时您必须根据模型的某些方面以不同方式呈现事物。 “问题”是 Salary > 20000
“逻辑”。虽然您给出的示例可能足够简单,因此 非常 并不重要,但理想情况下,这种事情并不真正属于视图。
执行此操作的标准方法是使用 ViewModel
。该对象的存在是为了将视图与数据模型的实现细节分离,并封装一些表示层逻辑。如果您的数据模型是例如一个 Entity Framework 代理,它有很多约束和行为,迫使它进入表示层的错误“形状”。在这个特定的例子中,你的 ViewModel
可能有一个 属性 沿着 bool ShowHighSalarySection
的行 - 要么由 ViewModel
计算,要么由一些外部业务代码计算并存储在 ViewModel
.
你的 ViewModel
的精确形状将在很大程度上取决于你所遵循的 MV* 的特定风格,但是例如:-
- 带有数据模型构造函数的简单
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) { ...
- 您还可以使用更“愚蠢”的
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);
- 有时业务规则非常复杂,您可能需要一些明确的处理代码来明确负责应用业务规则:-
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;
}
}
确切的去向取决于您的体系结构、您的理念以及您准备在 return 中接受多少样板文件(或工具)以保持整洁。 ViewModel 所做的主要事情是它允许您将业务规则从视图中取出并放入适当的地方(更安全、更可测试、更可重用等)。