

如何在 ASP.NET Core 中写出更干净的 Controller
source link: https://mp.weixin.qq.com/s?__biz=MzU4Mjc4NzgyOQ%3D%3D&%3Bmid=2247488154&%3Bidx=1&%3Bsn=59871af1c978d817e9f8dfff899c70f3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

你可以遵循一些最佳实践来写出更干净的 Controller,一般我们称这种方法写出来的 Controller 为瘦Controller,瘦 Controller 的好处在于拥有更少的代码,更加单一的职责,也便于阅读和维护,而且随着时间的推移也容易做 Controller 的多版本。
这篇文章我们一起讨论那些让 Controler 变胖变臃肿的一些坏味道,并且一起探索让 Controller 变瘦的手段,虽然我的一些在 Controller 上的最佳实践可能不是专业的,但我每一步都提供相关源代码来进行优化,接下来的章节中,我们会讨论什么是 胖Controller
,什么是 坏味道
,什么是 瘦Controller
,它能带给我们什么福利?并且如何让 Controller 变瘦,变简单,利测试,易维护。
从 Controller 中移除数据层代码
当在写 Controller 的时候,你应该遵守 单一职责
,也就意味着你的 Controller 只需做一件事情,换句话说,只有一个因素或者唯一一个因素能让你修改 Controller 中的代码,如果有点懵的话,考虑下面的代码片段,它将 数据访问代码 糅进了 Controller 。
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。
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 类。
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 操作。
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,按照普通的写法大概就是这样的。
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 的配置。
public class AutoMapping
{
public static void Initialize()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Author, AuthorDTO>();
//Other code
});
}
}
接下来可以在 Global.asax
中调用 Initialize()
初始化,如下代码所示:
protected void Application_Start()
{
AutoMapping.Initialize();
}
最后,可以将 mapping 逻辑放在 service 层中,请注意下面的代码是如何使用 AutoMapper 实现两个不兼容对象之间的映射。
public class AuthorService
{
private AuthorRepository authorRepository = new AuthorRepository();
public AuthorDTO GetAuthor (int authorId)
{
var author = authorRepository.GetAuthor(authorId);
return Mapper.Map<AuthorDTO>(author);
}
//Other methods
}
避免在 Controller 中写业务逻辑
尽量避免在 Controller 中写 业务逻辑
或者 验证逻辑
, Controller 中应该仅仅是接收一个请求,然后被下一个 action 执行,别无其它,回到刚才的问题,这两种逻辑该怎么处理呢?
-
业务逻辑
这些逻辑可以封装 XXXService 类中,比如之前创建的 AuthorService。
-
验证逻辑
这些逻辑可以用 AOP 的操作手法,比如将其塞入到 Request Pipeline 中处理。
使用依赖注入而不是硬组合
推荐在 Controller 中使用依赖注入的方式来实现对象之间的管理,依赖注入是 控制反转 的一个子集,它通过外部注入对象之间的依赖从而解决内部对象之间的依赖,很拗口是吧!
一旦你用了依赖注入方式,就不需要关心对象是怎么实例化的,怎么初始化的,下面的代码展示了如何在 AuthorController 下的构造函数中实现 IAuthorService 对象的注入。
public class AuthorController : Controller
{
private IAuthorService authorService = new AuthorService();
public AuthorController(IAuthorService authorService)
{
this.authorService = authorService;
}
// Action methods
}
使用 action filer 消除 Controller 中的重复代码
可以利用 action filter 在 Request pipeline 这个管道的某些点上安插一些你的自定义代码,举个例子,可以使用 ActionFilter 在 Action 的执行前后安插一些自定义代码,而不是将这些业务逻辑放到 Controller 中,让 Controller 不必要的膨胀,下面的代码展示了如何去实现。
[ValidateModelState]
[HttpPost]
public ActionResult Create(AuthorRequest request)
{
AuthorService authorService = new AuthorService();
authorService.Save(request);
return RedirectToAction("Home");
}
总的来说,如果一个 Controller 被赋予了几个职责,那么只要是其中任何一个职责的原因,你都必须对 Controller 进行修改,总的来说,一定要坚守 单一原则
。
译文链接:https://www.infoworld.com/article/3404472/how-to-write-efficient-controllers-in-aspnet-core.html
往期 精彩 回顾
.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
【.NET Core微服务实战-统一身份认证】开篇及目录索引
Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)
.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
用abp vNext快速开发Quartz.NET定时任务管理界面
Recommend
-
20
在项目开发中常常会需要做发送 Email 的功能,在 ASP.NET Core 中你可以用 MailKit 来实现 Email 的发送, MailKit 是一个开源的客户端库,可用在 Windows,Linux 或者 Mac 上,本篇文章就来讨论在...
-
12
ASP.NET Core Controller与IOC的羁绊 前言
-
7
ASP.NET Core 是一个轻量级,模块化的框架,常用来在 Windows,Linux 和 MacOS 上构建高性能,现代化的web框架,不像过去的 Asp.NET,在 ASP.NE...
-
13
ASP.Net Core 是微软开源的跨平台、可扩展、轻量级的模块化框架,可用于构建高性能的web应用程序。中间件组件可以注入到 ASP.Net Core 请求管道中...
-
13
ASP.NET Core 是一个开源的、跨平台的、轻量级模块化框架,可用于构建高性能、可伸缩的web应用程序,你也许不知道 ASP.NET Core 中有一个藏得很深...
-
11
ASP.NET Core 中内置了对依赖注入的支持,可以使用 依赖注入 的方式在运行时实现组件注入,这样可以让代码更加灵活,测试和可维护,通常有三种方式可以实现依赖注入。 构造函数注入 属性注入 方法注入 构造函数 这种注...
-
12
本文转载自微信公众号「码农读书」,作者码农读书 。转载本文请联系码农读书公众号。 记录日志的一个作用就是方便对应用程序进行跟踪和...
-
13
利用IHttpClientFactory可以无缝创建HttpClient实例,避免手动管理它们的生命周期。 当使用ASP.Net Core开发应用程序时,可能经常需要通过HttpClient调用WebAPI的方法以检查终结点是否正常工作。要实现这一点,通常需要实例化HttpClient...
-
1
《代码规范》如何写出干净的代码(一) ...
-
7
降低阅读负担,启发创作心智,轻松学习 JavaScript 技巧,日拱一卒,jym,冲~
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK