【问题标题】:ASP MVC 5 Organizing Project To Avoid Duplicating CodeASP MVC 5 组织项目以避免重复代码
【发布时间】:2016-10-26 20:13:42
【问题描述】:

我正在构建一个 asp mvc 5 支持票帮助台应用程序。该应用程序将要求用户注册以提交支持票。用户的操作是通过像线程一样添加注释来更新票证。

代理将管理工单,并拥有编辑工单、创建内部和公共注释以及查看所有工单的额外权限。

有一个站点管理员可以配置站点。

问题是,如果我有一个称为工单控制器的控制器,则创建或编辑工单的主要操作对于代理和用户来说都是相似的。不同之处在于,当用户创建工单时,他们只能填写表单上的某些字段,而代理可以更新其他字段。

所以我最终会在我的控制器和视图中使用大量 if then else 语句来检查用户角色,从而限制向不同类型的用户显示哪些表单字段和操作。此外,我的视图模型正在向客户公开他们不应该拥有权限的属性。

所以我的问题是你如何组织这种类型的设置。您是否为代理创建单独的区域并复制所有控制器和视图模型以满足此类用户的需求。

或者您是否会为创建的每种不同类型的用户角色使用 if then else 语句来膨胀控制器和视图?

我们将不胜感激有关此特定应用程序的最佳实践的建议。

为代理创建区域的唯一好处是我可以彻底改变代理门户的外观和感觉,例如使用不同的 URL。 mysite/agentdesk/tickets,它会更容易维护,但会以重复代码为代价。

/* 当前控制器逻辑和视图模型 */ 如您所见,视图模型中公开了只有代理才能设置的状态和优先级属性。

在控制器的create方法中,你可以看到teneray操作符被用来判断用户是否有正确的角色来设置上述属性。

public class TicketViewModel
{

      public int Id { get; set; }

      [Required]
      public string Subject { get; set; }

      [Required]
      public string Description { get; set; }

      // Only an agent can change the status
      [Required]
      public int Status { get; set; }

      // Only an agent can change the priority
      [Required]
      public int Priority { get; set; }


      public IEnumerable<StatusType> StatusTypes { get; set; }
      public IEnumerable<PriorityLevel> PriorityLevels { get; set; }

}

public class TicketController : Controller
{

    [Authorize]
    public ActionResult Create()
    {
        var viewModel = new TicketViewModel
        {
            StatusTypes = _context.StatusTypes.ToList(),
            PriorityLevels = _context.PriorityLevels.ToList()

        };

        return View(viewModel);
    }


    [Authorize]
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create(TicketViewModel viewModel)
    {
        if (!ModelState.IsValid)
        {
            viewModel.StatusTypes = _context.StatusTypes.ToList(),
            viewModel.PriorityLevels = _context.PriorityLevels.ToList()
            return View(viewModel);
        }

        bool agentRole = HttpContext.Current.User.IsInRole("Agent");

        var ticket = new Ticket
        {
            CreatedBy = User.Identity.GetUserId(),
            Subject = viewModel.Subject,
            Description = viewModel.Description,
            StatusId = agentRole ? viewModel.Status : 1,
            PriorityId = agentRole ? viewModel.Priority : 1
        };

        _context.Tickets.Add(ticket);
        _context.SaveChanges();

        return RedirectToAction("Index", "Tickets");
    }
}

【问题讨论】:

  • 我可能会介绍代理区域,所有控制器都装饰有 [Role("Agent")] 等。关于重复视图,如果您将公共共享字段作为局部视图引入,那么这将减少这一点。关于您的控制器,它们应该尽可能少地保留业务逻辑,因此创建一个名为 TicketCreator 的业务类,例如它包含两个方法 AsAgent 和 AsUser.. 然后您可以从不同的控制器调用每个方法,但共享尽可能多的你喜欢的类中的逻辑
  • 补充一下,你的业务类方法也会返回两种不同类型的 ViewModel,这样你就不会向用户发送敏感数据
  • @uk2k05 您能否展示一个名为 TicketCreator 解决方案的业务类示例。我试图弄清楚如何才能将视图模型考虑在内以适应客户和代理。

标签: asp.net-mvc refactoring asp.net-mvc-areas service-layer


【解决方案1】:

我认为一个区域是最好的选择。一个简单的事实是,否则代码的复杂性将迅速失控。根据某些角色显示/隐藏表单字段是一回事,但实际上,为了安全起见,这需要端到端保护。换句话说,如果允许用户访问这些属性,您还需要在您的操作内部检查是否仅使用值设置属性。发布您不应该编辑的内容是微不足道的,即使不存在允许这样做的表单字段。

要解决代码重复问题,请不要重复代码。这听起来有点陈词滥调,但确实如此。您的操作中的类似代码可以分解为基本控制器或只是一些帮助类。您视图中的类似代码可以分解为部分。基本上,在两种情况下相同的任何东西都应该放在可以在两种情况下使用相同代码的地方。

【讨论】:

  • 如果我要为代理设置一个特定的区域,我仍然会得到几乎相同的控制器方法和视图模型 - 请参阅上面的示例,了解当前的逻辑是如何的。您将如何重构视图模型和控制器方法。举个例子会有帮助吗?
  • 同样,如果控制器动作相同,您可以将它们放在基本控制器上并从您所在区域的控制器继承。您不必重新定义操作。如果它们不相同,您可以将包含在该基本控制器上的辅助方法中的部分分解出来,或者您可以创建一个该区域的控制器可以使用的辅助类。对于视图模型,只定义两者共享的属性,然后继承另一个特定于代理的类,从中您可以只定义与代理相关的属性。
  • 您甚至可以使用泛型来子在正确的视图模型、实体等中。基本上,您只需要使用 C# 已经为您提供的代码重用工具。控制器/视图模型没有什么特别之处。它们只是和其他任何类一样的类,你可以用它们做任何事情,你可以用 C# 中的任何类做任何事情。
  • 关于视图,如何与代理区域共享主共享视图目录中的部分视图?如果我在我的主共享视图目录和代理区域中有一个使用基本视图模型的局部视图,我想重用相同的局部视图,但使用从基本视图模型继承的代理视图模型。基本上在我的部分我使用 @model Myapp.ViewModels.TicketViewModel 。当需要在代理区域使用相同的部分时,我需要使用@model Myapp.AgentArea.ViewModels.AgentTicketViewModel
  • 查看我的新帖子以了解我的意思:stackoverflow.com/questions/38424224/…
猜你喜欢
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多