MVC的格式化逻辑在哪里

本文关键字:在哪里 格式化 MVC | 更新日期: 2023-09-27 17:59:02

假设我的电话号码以10位字符串的形式存储在数据库中:

0000000000

当我把这个电话号码呈现给用户时,我想把它格式化为:

(000)000-0000

我在一个处理这种格式的实用程序集中有一个扩展方法:

static string ToPhoneNumber(this string value)
{
    return Regex.Replace(value, @"('d{3})('d{3})('d{4})", "($1) $2-$3");
}

我的问题是,我在什么时候应用此转换

1) 视图中:

@Model.PhoneNumber.ToPhoneNumber()

2) 视图模型中:

public string FormattedPhoneNumber
{
    get
    {
        return this.PhoneNumber.ToPhoneNumber()
    }
}

3) 在控制器中:

userModel.FormattedPhoneNumber = userModel.PhoneNumber.ToPhoneNumber()

4) 在域模型中(与#2实现相同)

5) 在服务中(与#3实现相同)

此外,答案是否取决于它是全局格式化需求(如电话号码)还是单个视图上的孤立一次性格式化?

我会说出我的想法,但不想影响任何答案。

MVC的格式化逻辑在哪里

我个人喜欢把东西放在我的ViewModel中,因为如果你不这样做,你的视图中会出现奇怪的代码。让我们以你为例。

Razor视图:

@using MyNamespace.Models.Extensions
@model MyNamespace.Models.ViewModels.IndexViewModel
@if (string.IsNullOrWhiteSpace(Model.PhoneNumber) {
   <div> @Model.PhoneNumber.ToPhoneNumber() </div>  
}

对比备选方案:

Razor视图:

@model MyNamespace.Models.ViewModels.IndexViewModel
@Model.FormattedPhoneNumber

ViewModel:

 public string FormattedPhoneNumber {
     get {
         return PhoneNumber.IsEmpty()
         ? "Not Available"
         : PhoneNumber.ToPhoneNumber();
     }
 }

你肯定可以改进我的代码,但关键是它可以让你的视图更简单,避免分支逻辑混乱。

此外,我从未自称是圣人,所以我并不总是听从自己的建议,但我应该这样做。照我说的做,而不是照我做的做:)

我认为决定如何显示数据是视图的责任。因为只有视图知道哪些内容可用于演示。另一方面,在控制器中可能更容易做到这一点。并且控制器将知道用户的区域设置。总的来说,我认为这没什么区别。

首先,对于一般的体系结构模式,尤其是那些处理"关注点分离"的模式,最终的仲裁者总是"在我的场景中什么是最好的方法"-我坚信,在不考虑自己的计划和需求的情况下教条地遵守一组规则是一种可怕的做法。更不用说,这里还没有明确的共识:根据你的XYZ(MVC、MVP、MVVM)的多样性,你会发现在互联网上会有相反的想法。

也就是说,我对这个问题的快速回答是"运用你的判断"。

"在视图中"的论据:

  • 它涉及演示,因此是视图的责任

"视图中模型"的参数:

  • 通常,视图模型的作用是提供模型的"现成数据绑定"表示,因此,将模型数据转换为视图直接可使用的形式是视图模型的责任

模型的论据:

  • 这对于模型数据来说可能是过于常见的表示;因此,在DRY之后,模型将承担该表示的责任

控制器的参数:

  • 。。。好吧,这里想不出一个合理的。控制器通常会对动作做出反应,因此要证明它属于这里是一种延伸

我想说的是,只要系统的一个点接受并承担责任,并且该责任仅由该组件/层/类处理,您就完成了主要目标,即防止稀释/重复/低内聚性。

我个人的观点,fwiw,可能会落在视图或视图模型上。如果这是WPF,我几乎可以肯定地说是视图(通过可用于WPF数据绑定的格式提供程序)。在网络世界中,我可能倾向于这种观点,尽管存在对该模型的有力论据——比如你现在想通过REST/JON/etc服务公开这些数据:你可以很容易地处理这种变化(假设你想返回格式化的数据)

TL/DR:这真的取决于;遵循常识并运用你的判断。将所有相关的逻辑放在一个地方是重要的部分,并质疑任何教条主义/戒律式的"你应该"的说法。

这取决于你对ViewModel的定义,你是否遵循了一种(自创的)MVCVM*方法,即除了域模型之外,还有一个特定于你的视图的ViewModel?

如果是这样的话,虚拟机肯定会包含格式化逻辑,也就是说将这个视图模型放在首位的全部意义,就是为视图建模。选项2也是如此。

也就是说,这背后的原因是,如果你这样格式化,格式化你自己就会开始遵循DRY原则:

@Regex.Replace(Model.PhoneNumber, @"('d{3})('d{3})('d{4})", "($1) $2-$3");

由于您有一个扩展方法,所以在视图中调用格式化程序并不是什么大问题,但我仍然更喜欢在专用的VM中进行。

如果您的虚拟机实际上只是包含原始数据的域模型(请参阅此模式1),那么它肯定应该在您的视图中,因此选项1。请注意,如果您使用这种模式,我建议您不要使用它,因为它会使您的视图与低级对象强耦合,您最好将其抽象为您需要的内容,以确保域模型+视图模型之间的耦合实际上是强的(即在编译时编译,而不是在运行时编译!)。

最重要的是,这当然应该而不是进入您的域模型。

*模型、视图、控制器、视图模型。其中ViewModel包含要在视图中使用的数据,其格式为所需的格式。

我会把它放在视图模型中,而不是放在视图中。该视图仅用于向最终用户呈现数据/信息。保持关注点的分离可以确保每个对象尽可能独立。如果将格式化的数字传递给视图,则视图不关心要显示的内容,只显示格式化的数字即可。

我认为这是建模,而不是格式化。如果接收应用程序需要重新格式化这些字段的顺序、间距或大小写,则必须首先将单个字段拆分为几个单独的字段。

这应该是服务层的责任。我们谈论的是模式,而不是格式。应用程序要求将字段拆分为其数据契约的一部分。发生这种拆分的地方应该是应用程序使用的应用程序服务层。

服务层应该尝试通过模式将元数据添加到信息中。

例如,如果我从数据合同中收到一个电话号码,如下所示:1234567890

演示要求如下:(123)456–7890

服务层应该将电话号码分解为其元素

<PhoneNumber>
<CountryCode>1</CountryCode>
<Area>123</Area>
<Prefix>456</Prefix>
<LineNumber>7890</LineNumber>
</PhoneNumber>

选项1是最好的,其次是2。在控制器中,您实际上应该删除格式以将其发送到服务层,这样域模型和服务模型都不知道格式。