1. 程式人生 > >如何在 ASP.NET Core 中寫出更乾淨的 Controller

如何在 ASP.NET Core 中寫出更乾淨的 Controller

你可以遵循一些最佳實踐來寫出更乾淨的 Controller,一般我們稱這種方法寫出來的 Controller 為瘦Controller,瘦 Controller 的好處在於擁有更少的程式碼,更加單一的職責,也便於閱讀和維護,而且隨著時間的推移也容易做 Controller 的多版本。 這篇文章我們一起討論那些讓 Controler 變胖變臃腫的一些壞味道,並且一起探索讓 Controller 變瘦的手段,雖然我的一些在 Controller 上的最佳實踐可能不是專業的,但我每一步都提供相關原始碼來進行優化,接下來的章節中,我們會討論什麼是 `胖Controller`,什麼是 `壞味道`,什麼是 `瘦Controller`,它能帶給我們什麼福利? 並且如何讓 Controller 變瘦,變簡單,利測試,易維護。 ## 從 Controller 中移除資料層程式碼 當在寫 Controller 的時候,你應該遵守 `單一職責`,也就意味著你的 Controller 只需做一件事情,換句話說,只有一個因素或者唯一一個因素能讓你修改 Controller 中的程式碼,如果有點懵的話,考慮下面的程式碼片段,它將 資料訪問程式碼 糅進了 Controller 。 ``` C# 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); } //Other action methods } ``` 請注意上面的程式碼在 Action 中使用了 dataContext 從資料庫讀取資料,這就違反了單一職責原則,並直接導致了 Controller 的臃腫。 假如後續你需要修改 `資料訪問層` 程式碼,可能基於更好的效能或者你能想到的原因,這時候只能被迫在 Controller 中修改,舉個例子吧:假如你想把上面的 EF 改成 Dapper 去訪問底層的 Database,更好的做法應該是單獨拎出來一個 `repository` 類來操控 `資料訪問` 相關的程式碼,下面是更新後的 AuthorController。 ``` C# public class AuthorController : Controller { private AuthorRepository authorRepository = new AuthorRepository(); public ActionResult Index(int authorId) { var authors = authorRepository.GetAuthor(authorId); return View(authors); } //Other action methods } ``` 現在 AuthorController 看起來是不是精簡多了,上面的程式碼是不是就是最佳實踐呢? 不完全是,為什麼這麼說呢?上面這種寫法導致 Controller 變成了 `資料訪問元件`,取出資料後必然少不了一些業務邏輯處理,這就讓 Controller 違反了 `單一職責`,對吧,更通用的做法應該是將 `資料訪問邏輯` 封裝在一個 service 層,下面是優化之後的 AuthorController 類。 ``` C# public class AuthorController : Controller { private AuthorService authorService = new AuthorService(); public ActionResult Index(int authorId) { var authors = authorService.GetAuthor(authorId); return View(authors); } //Other action methods } ``` 再看一下 AuthorService 類,可以看到它利用了 AuthorRepository 去做 CURD 操作。 ``` C# public class AuthorService { private AuthorRepository authorRepository = new AuthorRepository(); public Author GetAuthor (int authorId) { return authorRepository.GetAuthor(authorId); } //Other methods } ``` ## 避免寫大量程式碼做物件之間對映 在 DDD 開發中,經常會存在 DTO 和 Domain 物件,在資料 Input 和 Output 的過程中會存在這兩個物件之間的 mapping,按照普通的寫法大概就是這樣的。 ``` C# 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; //Other code ...... } ``` 可以看到,這種一一對映的寫法讓 Controller 即時膨脹,同時也讓 Controller 增加了額外的功能,那如何把這種 `模板式` 程式碼規避掉呢?可以使用專業的 `物件對映框架 AutoMapper` 去解決,下面的程式碼展示瞭如何做 AutoMapper 的配置。 ``` C# public class AutoMapping { public static void Initialize() { Mapper.Initialize(cfg => { cfg.C