这是一个MVC反模式吗?

时间:2012-07-30 16:07:49

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

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

我们有一个AccountDto类来表示帐户。有一个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返回给Controller,其中包含一个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;
}

我被告知响应的内容是为了能够处理成功/失败消息而实现的。所以在控制器中,存在如下代码(如果失败则不会发生任何特殊情况):

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或其他网站,请迁移。

2 个答案:

答案 0 :(得分:4)

我同意你的意见,这将是一种反模式。视图应该是非常无知的,特别是像这样的逻辑。

如果成功与失败之间的差异只是UI的一小部分,我可以理解为什么这会很诱人,但想象一下如果要改变。视图几乎没有能力(没有不必要的局部嵌套)来切换到完全不同的视图。它具有 no 发出重定向或其他错误代码的能力。如果您决定更改UI,则可能需要再次返回并重新调整Controller。

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

答案 1 :(得分:1)

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

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

更好的是,将该逻辑迁移到您的服务类中,以便它保证返回一个对象(当没有后台实体时,存储库执行此操作并不一定有意义,但它对服务有效。) / p>

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