如何在ASP.NET Core中编写高效的控制器

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

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

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

在编写控制器时,你应该坚持单一责任原则,这意味着控制器应该有“一个责任”或“有且只有一个缘由能够更改”。换句话说,你但愿将更改控制器代码的缘由减至最少。下面的代码显示了具备数据访问逻辑的典型控制器。asp.net

在.NET生态系统中使用特定的技术堆栈会产生一些困惑,由于有不少选择,好比应该使用哪一种类型的运行时?在这篇文章中,咱们将试图把这些要点都说清楚。函数

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链接到底层数据库该怎么办?更好的方法是使用repository类来封装数据访问逻辑(尽管我不太喜欢repository模式)。让咱们用如下代码更新AuthorController。性能

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

控制器如今看起来更瘦了。那么这是编写这个控制器的最佳方法吗?不是。若是你的控制器正在访问数据访问组件,那么它将作太多的事情,所以违反了单一职责原则。控制器不该该有直接访问数据访问组件的数据访问逻辑或代码。下面是AuthorController类的改进版本。测试

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操做。this

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

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

你常常须要映射数据传输对象(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是如何被用来映射两个不兼容的类型AuthorAuthorDTO的。

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");}

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

 

 

 

本文分享自微信公众号 - 码农译站(gh_c0d62fbb4bda)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。