在说明什麽是 Dependency Injection(DI, 依赖注入)前,要先来介绍一下什麽是「依赖」。依赖简单的说就是当前这段程序(class 或 function)所需要的外部功能。而「依赖注入」就是,不要让这段程序主动建立「依赖」的实体,被动的从外部接收这个「依赖」。以 .NET 为例,就是不要自己 new class 实体来用,而是让 Startup.cs 里的服务容器把「依赖」拿给你。
记得以前学生时代写扣的时候,根本不懂软件工程(谜之声:你以为你现在懂吗?),写了几个 class,哪里要用就在哪里呼叫建构式产生实体。当时因为是一人专案而且专案规模小,所以也没碰上什麽大麻烦。甚至曾经看着 Stack Overflow 上的这个回答呵呵笑。
後来毕业工作之後,痛苦就开始了,当时的同事跟当时的我走同样的风格,大家一起煮义大利面,曾经有一个 function 将近一千行,里面有我的 class 也有他的 class。当我们各自的 class 需又有所变更的时候,就会发现,完了!改不动!一改要嘛会出错,要嘛一大堆地方都要改!
後来实际尝试使用 DI,才真正体会到他的美好,笔者个人觉得 DI 真的是软件工程里 CP 值很高的一种设计,学起来不会很难,但是对将来的维护帮助很大。
如果想要了解更多关於依赖注入的知识,可以参考这篇文章
通常我们要注入的东西,会以一个「Service」为单位,一个 Service 提供某个工作分类的服务,我们可以在开发中的程序里叫用这些服务来达成目的。例如一个下订单的 API,可能会需要几个 Service 来完成任务
所以在我们实作 DI 之前,我们先来把 Controller 里的处理逻辑抽出来放到一个 Service。笔者习惯在专案底下新增一个 Services 资料夹,再把我们的 Service 加进去
public class UserService
{
private static List<User> _users = new List<User>()
{
new User() {UserId = 0, UserName = "Alice", Email="[email protected]"},
new User() {UserId = 1, UserName = "Bob", Email="[email protected]"},
new User() {UserId = 2, UserName = "Cathy", Email="[email protected]"},
};
public List<User> GetAllUsers()
{
return _users;
}
public User GetUserById(int id)
{
return _users.FirstOrDefault(x => x.UserId == id);
}
public void CreateUser(User model)
{
model.UserId = _users.Max(x => x.UserId) + 1;
_users.Add(model);
}
public void UpdateUser(int id, User model)
{
var existingUser = _users.FirstOrDefault(x => x.UserId == id);
if (existingUser != null)
{
existingUser.UserName = model.UserName;
existingUser.Email = model.Email;
}
}
public void DeleteUser(int id)
{
var existingUser = _users.FirstOrDefault(x => x.UserId == id);
if (existingUser != null)
{
_users.Remove(existingUser);
}
}
}
接着,在 Startup.cs 把这个 Service 注册到服务容器。.NET 的 DI 框架会帮我们管理 Service 的生命周期,在注册 Service 的时候就会决定他们的生命周期:
与资料库操作相关的 Service 通常会用 AddScoped,所以我们在 ConfigureServices 这个 method 里注册刚刚写的 Service
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Ithome_2021_API", Version = "v1" });
});
// 加入这一行
services.AddScoped<UserService>();
}
再来,在 Controller 加入建构式,只要 .NET 发现建构式需要东西当参数,.NET就会尝试从服务容器里找出这个东西,并把他「注入」给这个 Controller。注入之後用一个 private 变数存起来,後面的程序就能使用这个 Service,最後把抽掉程序码造成的 error 修一修,简单的 DI 实作就完成了
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
private readonly UserService _user;
public UserController(UserService user)
{
_user = user;
}
// GET: api/<UserController>
[HttpGet]
public IEnumerable<User> Get()
{
return _user.GetAllUsers();
}
// GET api/<UserController>/5
[HttpGet("{id}")]
public User Get(int id)
{
var user = _user.GetUserById(id);
if (user == null)
{
throw new Exception("找不到 user");
}
return user;
}
// POST api/<UserController>
[HttpPost]
public void Post(User user)
{
_user.CreateUser(user);
}
// PUT api/<UserController>/5
[HttpPut("{id}")]
public void Put(int id, User newUserData)
{
_user.UpdateUser(id, newUserData);
}
// DELETE api/<UserController>/5
[HttpDelete("{id}")]
public void Delete(int id)
{
_user.DeleteUser(id);
}
}
今天的 DI 范例注入了一个明确指定的 Service class,其实这样并没有充分发挥 DI 的好处。明天我们会再优化我们的 DI 作法,让 Controller 依赖「介面」而不是明确指定的 class
Laravel是基於MVC架构设计出来的框架, 什麽是MVC(Model–View–Controll...
前言 第三天要延续介绍如何处理一笔数据。 对於数据分析的用途跟前几个步骤 请看上一篇文章 数据分析...
为何选择Python ?而不是其他的语言。 每个程序语言都有属於它们的专长,Python是一种高阶语...
回到昨天留下的问题 card数太长要怎麽办 TextFiled 送出後怎麽清除里面的字 其实只要将 ...
一、前言 网站该写些什麽内容?这点对於部落格新手来说,刚开始因为有许多想分享的东西,有什麽写什麽,...