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

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

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

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

在.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操作。

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

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

原文链接:https://www.infoworld.com/article/3404472/how-to-write-efficient-controllers-in-aspnet-core.html

如何在ASP.NET Core中编写高效的控制器的更多相关文章

  1. 如何在ASP.NET Core中自定义Azure Storage File Provider

    文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...

  2. 如何在 ASP.Net Core 中使用 Lamar

    ASP.Net Core 自带了一个极简的 开箱即用 的依赖注入容器,实际上,你还可以使用第三方的 依赖注入容器 来替代它,依赖注入是一种设计模式,它能够有效的实现对象之间的解耦并有利于提高单元测试和 ...

  3. 如何在ASP.NET Core中实现CORS跨域

    注:下载本文的完整代码示例请访问 > How to enable CORS(Cross-origin resource sharing) in ASP.NET Core 如何在ASP.NET C ...

  4. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  5. 如何在ASP.NET Core中应用Entity Framework

    注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...

  6. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  7. 如何在ASP.NET Core中使用Azure Service Bus Queue

    原文:USING AZURE SERVICE BUS QUEUES WITH ASP.NET CORE SERVICES 作者:damienbod 译文:如何在ASP.NET Core中使用Azure ...

  8. 如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

  9. [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置

    [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置 原文: USING CONSUL FOR STORING THE CONFIGURATION IN ASP.NET COR ...

随机推荐

  1. HTMl5 特点 标签语法 以及标签

    知识点关于HTML5 的特点以及其标签和标签语法.. <!-- [HTMl5 特点] 向下兼容 用户至上 化繁为简 无插件范围 访问通用性 引入语义 引入原生媒体支持--> <!-- ...

  2. python——模块、标准库、第三方模块安装

    模块(module)简介 模块化--指将一个完整的程序分解为一个一个小的模块,通过将模块组合,来搭建出一个完整的程序. 模块化的特点: ① 方便开发 ② 方便维护 ③ 模块可以复用! 在Python中 ...

  3. H3C交换机端口聚合配置

    1.接入交换机: interface Ten-GigabitEthernet1/0/21 port link-mode bridge port link-type trunk port trunk p ...

  4. sh 脚本名字和./脚本名字有什么区别

    sh xxx用 sh 这个shell (sh一般指系统默认shell,比如 bash, ksh, Csh 等都有可能) 来解释和运行 xxx 这个脚本.xxx 文件不必具有可执行属性(chmod +x ...

  5. Codeforces Round #690 (Div. 3)

    第一次 ak cf 的正式比赛,不正式的是寒假里 div4 的 Testing Round,好啦好啦不要问我为什么没有 ak div4 了,差一题差一题 =.= 不知不觉已经咕了一个月了2333. 比 ...

  6. 【noi 2.2_7891】一元三次方程求解(二分枚举+输出程序运行时间)

    对于noi上的题有2种解法: 1.数据很小(N=100),可以直接打for循环枚举和判断. 2.不会"三分",便用二分.利用"两根相差>=1"和 f(x1 ...

  7. c语言实现--双向循环链表操作

    1,双向链表相当于两个单向循环链表. 2,双向链表的结点定义. 1 struct DULNode 2 { 3 int data; 4 struct DULNode * prior; 5 struct ...

  8. codeforces Looksery Cup 2015 C. The Game Of Parity

    There are n cities in Westeros. The i-th city is inhabited by ai people. Daenerys and Stannis play t ...

  9. Codeforces Testing Round #16 C.Skier

    题意: 一个人在雪地上滑雪,每次可以向上下左右四个方向移动一个单位,如果这条路径没有被访问过,则需要5秒的时间,如果被访问过,则需要1秒(注意:判断的是两点之间的距离,不是单纯的点).给你他的行动轨迹 ...

  10. entity framwork修改指定字段

    1.ef修改时指修改指定字段public void ChangePassword(int userId, string password) { var user = new User() { Id = ...