【问题标题】:Is this an MVC anti-pattern?这是 MVC 反模式吗?
【发布时间】:2012-07-30 16:07:49
【问题描述】:

我对任何类型的 .NET Web 开发都很陌生(到目前为止,我主要使用 Winforms 和服务。)我已经开始与另外两个开发人员一起开发现有的 MVC3 项目。我在概念上熟悉 MVC,并且正在尝试了解它在这个项目中的使用方式。

我们有一个 AccountDto 类来表示 Accounts。每个Entity都有一个Response类被另一个类继承,即AccountResponse:

public class Response
{
    [DataMember]
    public bool IsSuccess{get;set;}

    [DataMember]
    public string DisplayMessage { get; set; }

    [DataMember]
    public string DetailedMessage { get; set; }

    [DataMember]
    public ErrorType ErrorType { get; set; }

    public Response(){
        this.IsSuccess=true;
        this.ErrorType = ErrorType.None;
    }
}

public partial class AccountResponse : Response
{
    [DataMember]
    public IList<AccountDto> AccountList { get; set; }
}

有一个 AccountService 会返回一个 AccountResponse 给控制器,其中包含一个 AccountDto 对象的列表:

public AccountResponse GetAccountByAccountId(Int64 accountId)
{
    _logger.Info("Executing GetAccountByAccountId()");
    AccountResponse response = new AccountResponse();

    try
    {
        Account item = AccountPersistence.GetAccountByAccountId(accountId);
        AccountDto dto = Mapper.Map<AccountDto>(item);

        response.AccountList = new List<AccountDto>() { dto };
        response.IsSuccess = true;
    }
    catch (Exception ex)
    {
        response.IsSuccess = false;
        response.ErrorType = ErrorType.GeneralFault;
        response.DetailedMessage = ex.ExceptionMessageBuilder();
        response.DisplayMessage = "System Failure: Failed to get Account by AccountId";
        _logger.Error(ex);
    }
    return response;
}

有人告诉我,Response 的实现是为了能够处理成功/失败消息。所以在控制器中,有如下代码(如果发生故障,不会发生任何特殊情况):

public ActionResult ToBeCalled(int id)
{
    AccountDto dto = null;
    var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
    if (response.IsSuccess)
    {
        dto = response.AccountList[0];
        return View(dto);
    }
    return View(dto);
}

这对我来说很有意义,尽管我不确定成功/错误消息将在哪里使用。但是,他们现在想从在视图中使用 DTO 切换到使用 Response,因此必须在视图中处理成功/失败:

public ActionResult ToBeCalled(int id)
{
    var response = _accountService.GetAccountByAccountId(Convert.ToInt64(id));
    return View(response);
}

这对我来说似乎是 - 而不是针对作为模型的 DTO 进行编码,我必须为每个页面执行以下操作:

@{
    if (Model.IsSuccess)
    {
        var account = Model.AccountList.FirstOrDefault();

        if (account != null)
        {
            @Html.HiddenFor(x => account.AccountNumber)
        }
}

控制器的 ActionResult / HttpPost 方法还必须从这些 Response 对象中解析 DTO。这对我来说似乎是一种反模式。这样的做法正常吗?

抱歉,如果这太长了,如果它属于 Code Review 或其他网站,请迁移。

【问题讨论】:

  • 我参与的第一个 MVC 项目,一个几乎相同的模式(实际上有点可怕)已经到位,我的想法和你完全一样。 FWIW 我同意下面的@bhamlin
  • 这是非常程序化的。控制器是这个概念中关键的破坏元素。我们需要真实的对象,而不是程序控制器。

标签: c# asp.net-mvc-3 design-patterns response dto


【解决方案1】:

我同意你的观点,这将是一种反模式。视图应该是很无知的,尤其是这样的逻辑。

如果成功和失败之间的差异只是 UI 的一小部分,我可以理解为什么这很诱人,但想象一下,如果这种情况发生了变化。视图几乎没有能力(没有不必要的部分嵌套)切换到完全不同的视图。它没有能够发出重定向或其他错误代码。如果您决定更改您的 UI,您可能需要返回并再次重写您的控制器。

如果将逻辑移动到视图背后的原因是从 Controller 中删除 response.IsSuccess 逻辑(老实说,这对我来说似乎很好;它与经典的 Model.IsValid 几乎相同),你可以考虑另一种方法:重构您的Response 类以从ActionResult 继承。然后你可以将该逻辑移到ExecuteResult() 方法中,它将与你的控制器分开。

【讨论】:

    【解决方案2】:

    只需使用 coalesce 运算符,您就可以摆脱一大堆杂乱无章的东西(比如那个奇怪的 Response 基类(如果它继续存在,应该标记为抽象))并避免空值检查。

    public ActionResult ToBeCalled(int id)
    {
        var response = _accountService.GetAccountByAccountId(id) ??
            new AccountResponse();
        return View(response);
    }
    

    更好的是,将该逻辑迁移到您的服务类中,以便它保证对象的返回(当没有支持实体时,存储库这样做不一定有意义,但它适用于服务)。

    无论哪种方式,您都不需要在视图中包含难看的空检查或 if/else 逻辑。将尽可能多的逻辑移到可以测试的地方,这样你会更快乐。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 2011-01-28
      • 1970-01-01
      • 1970-01-01
      • 2015-08-25
      • 2013-01-05
      相关资源
      最近更新 更多