格式化逻辑在哪里属于MVC?

时间:2013-02-14 15:21:00

标签: c# asp.net-mvc asp.net-mvc-3 language-agnostic

假设我的电话号码以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相同的实现)

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

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

7 个答案:

答案 0 :(得分:4)

我认为决定如何显示数据是一种观点责任。因为只有视图知道可用于演示的内容。另一方面,在控制器中执行它可能更容易。控制器会知道用户的区域设置。总而言之,我认为这几乎没有什么区别。

答案 1 :(得分:4)

我个人喜欢把东西放在我的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();
     }
 }

你绝对可以改进我的代码,但关键是它可以让你的视图更简单,并且不会因为分支逻辑而混乱。

另外,我从未声称自己是圣人,所以我并不总是遵循自己的建议,但我应该这样做。像我说的那样,不像我一样:)

答案 2 :(得分:2)

首先,对于一般的架构模式,特别是那些处理“关注点分离”的问题,最终的仲裁者始终是“我的场景中最好的方法” - 我坚信教条遵守一套规则不考虑自己的计划和需求是一种可怕的做法。更不用说这里没有明确的共识:取决于你的各种XYZ(MVC,MVP,MVVM),你会发现对于整个互联网的内容存在相反的想法。

那就是说,我对这个问题的快速回答是“用你的判断力”。

“在视图中”的参数:

  • 它涉及演示,因此它是观点责任

“在视图模型中”的参数:

  • 一般来说,视图模型的作用是提供模型的“准备数据绑定”表示 - 因此,将模型数据转换为视图直接消费的形式是视图模型的责任

模型的参数:

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

控制器的参数:

  • ......好吧,在这里想不到一个合理的。控制器通常会响应操作,因此可以证明它属于这里。

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

我的个人意见,fwiw,可能会落在视图或视图模型上。如果这是WPF我几乎肯定会说视图(通过格式提供程序可用于wpf数据绑定)。在网络世界中,我可能倾向于视图,尽管模型存在强有力的论据 - 假设您现在希望通过REST / JSON / etc服务公开这些数据:您可以轻松处理此更改(假设您需要)返回格式化数据,即)

TL / DR:这真的取决于;遵循常识并运用你的判断力。将所有相关逻辑保存在一个地方是重要的部分,并质疑任何教条/诫命式的“Thou Shalt”陈述。

答案 3 :(得分:0)

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

如果是这样,VM肯定可以包含格式化逻辑,即将View Model放在首位的整个点,以模拟视图。所以选项2。

那就是说,这背后的原因是,如果你这样格式化,格式化你自己会开始干燥原则:

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

由于你有一个扩展方法,在你的视图中调用格式化程序并不是一个问题,但我仍然希望在专用的VM中进行。

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

最重要的是 - 这肯定会进入您的域模型。

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

这应该是服务层的责任。我们正在谈论架构,而不是格式。该应用程序要求将字段拆分为其数据协定的一部分。发生此拆分的位置应位于您的应用消耗的Application Services层中。

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

例如,如果我从这样的数据合同中收到电话号码: 1234567890

陈述的要求如下: (123)456 - 7890

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

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

答案 6 :(得分:-1)

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