通过遵循最佳实践,可以编写更好的控制器。所谓的“瘦”控制器(指代码更少、职责更少的控制器)更容易阅读和维护。而且,一旦你的控制器很瘦,可能就不需要对它们进行太多测试了。相反,你可以专注于测试业务逻辑和数据访问代码。瘦控制器的另一个优点是,它更容易维护控制器的多个版本。

这篇文章讨论了使控制器变胖的坏习惯,然后探索了使控制器变瘦和易于管理的方法。我列出编写控制器的最佳实践可能并不全面,但我已经讨论了最重要的一些,并在适当的情况下提供了相关的源代码。在接下来的几节中,我们将研究什么是胖控制器,为什么它是一种代码坏味道,瘦控制器是什么,为什么它是有益的,以及如何使控制器瘦、简单、可测试和可管理。

从控制器中删除数据访问代码

在编写控制器时,你应该坚持单一责任原则,这意味着控制器应该有“一个责任”或“有且只有一个原因可以更改”。换句话说,你希望将更改控制器代码的原因减至最少。下面的代码显示了具有数据访问逻辑的典型控制器。

在.NET生态系统中使用特定的技术堆栈会产生一些困惑,因为有很多选择,比如应该使用哪种类型的运行时"htmlcode">

public class AuthorController : Controller
{
  private AuthorContext dataContext = new AuthorContext();
  public ActionResult Index(int authorId)
  {
    var authors = dataContext.Authors
      .OrderByDescending(x=>x.JoiningDate)
      .Where(x=>x.AuthorId == authorId)
      .ToList();
    return View(authors);
  }
}

在action内部使用数据上下文实例读取数据,违反了单一职责原则,并使你的控制器充斥着不应该出现在那里的代码。在本例中,我们使用一个DataContext(假设我们使用Entity Framework Core)来连接、处理数据库中的数据。

明天如果你决定更改数据访问技术(为了更好的性能或其他原因),你也必须更改你的控制器。例如,如果我想使用Dapper连接到底层数据库该怎么办"htmlcode">

public class AuthorController : Controller
{
  private AuthorRepository authorRepository = new AuthorRepository();
  public ActionResult Index(int authorId)
  {
    var authors = authorRepository.GetAuthor(authorId);
    return View(authors);
  }
}

控制器现在看起来更瘦了。那么这是编写这个控制器的最佳方法吗"htmlcode">

public class AuthorController : Controller
{
  private AuthorService authorService = new AuthorService();
  public ActionResult Index(int authorId)
  {
    var authors = authorService.GetAuthor(authorId);
    return View(authors);
  }
}

AuthorService类利用AuthorRepository类执行CRUD操作。

public class AuthorService
{
  private AuthorRepository authorRepository = new AuthorRepository();
  public Author GetAuthor (int authorId)
  {
    return authorRepository.GetAuthor(authorId);
  }
}

避免编写样板代码来映射对象

你经常需要映射数据传输对象(DTO)和域对象,反之亦然。请参考下面给出的代码片段,它显示了控制器方法内部的映射逻辑。

public IActionResult GetAuthor(int authorId)
{
  var author = authorService.GetAuthor(authorId);
  var authorDTO = new AuthorDTO();
  authorDTO.AuthorId = author.AuthorId;
  authorDTO.FirstName = author.FirstName;
  authorDTO.LastName = author.LastName;
  authorDTO.JoiningDate = author.JoiningDate;
 }

你不应该在控制器中编写这样的映射逻辑,因为它会使控制器膨胀并增加额外的责任。如果你要编写映射逻辑,可以利用像AutoMapper这样的对象映射器工具来避免编写大量样板代码。

最后,你应该将映射逻辑移到前面创建的服务类中。注意AutoMapper是如何被用来映射两个不兼容的类型Author和AuthorDTO的。

public class AuthorService
{
  private AuthorRepository authorRepository = new AuthorRepository();
  public AuthorDTO GetAuthor (int authorId)
  {
    var author = authorRepository.GetAuthor(authorId);
    return Mapper.Map<AuthorDTO>(author);
  }
}

避免在控制器中编写业务逻辑代码

不应该在控制器中编写业务逻辑或验证逻辑。控制器应该只接受一个请求,然后跳转下一个action,除此之外没有其他的。所有的业务逻辑代码都应该转移到其他类中(比如我们前面创建的AuthorService类)。有几种方法可以在请求管道中设置验证器,而不要在控制器中编写验证逻辑。这会使你的控制器变得不必要的臃肿,并让它负责它不应该做的任务。

更喜欢依赖注入而不是组合

你应该更喜欢在控制器中使用依赖项注入来管理依赖项。依赖注入是控制反转(IoC)原则的一个子集。它用于通过允许从外部注入的依赖项删除内部依赖项。

通过利用依赖注入,你不必关心对象的实例化、初始化等。你可以有一个返回所需类型实例的工厂,然后可以使用构造函数注入来使用该实例。下面的代码片段说明了如何使用构造函数将IAuthorService类型的实例注入到AuthorController。(假设IAuthorService是AuthorService类扩展的接口。)

public class AuthorController : Controller
{
  private IAuthorService authorService = new AuthorService();
  public AuthorController(IAuthorService authorService)
  {
    this.authorService = authorService;
  }
}

使用action过滤器来消除重复的代码

可以在asp.net core中使用action过滤器在请求管道中的特定点执行定制代码。例如,你可以使用action过滤器在操作action方法执行之前和之后执行自定义代码。你可以从控制器的action方法中删除验证逻辑,并将其写入action过滤器中,而不是在控制器中编写验证逻辑。下面的代码片段显示了如何实现这一点。

[ValidateModelState]
[HttpPost]
public ActionResult Create(AuthorRequest request)
{
  AuthorService authorService = new AuthorService();
  authorService.Save(request);
  return RedirectToAction("Home");
}

你将多个职责分配给了一个控制器,那么也会有多个原因导致控制器更改。因此,这违反了单一责任原则,该原则规定类应该有且只有一个变更的理由。

标签:
ASP.NET,Core,控制器

免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
桃源资源网 Design By www.nqtax.com