iT邦幫忙

0

Day 15|ASP.NET Core:依賴注入(DI)與 Service 層

c#
  • 分享至 

  • xImage
  •  

今日學習目標

  • 理解 Controller 與 Service 的差別
  • 了解依賴注入 DI 的用途
  • 建立 ITaskService.cs 和 TaskService.cs
  • 在 Program.cs 註冊 Service
  • 測試重構後的 CRUD API

Controller 與 Service 有什麼不同

以前我們的 TaskFlow 是所有功能集中在 Controller 裡,雖然可以運作,但未來會不容易管理
我們要改成
前端 / Swagger UI發送 HTTP 請求
TasksController接收請求、決定回傳什麼 HTTP 結果
TaskService負責任務的新增、查詢、修改、刪除
**List TaskItem **暫時儲存任務資料

什麼是依賴注入(DI)

假設 TasksController 需要使用 TaskService
一般情況可能會自己建立物件

TaskService service = new TaskService();

但使用 DI 後,我們只需要告訴 ASP.NET Core:
「這個 Controller 需要一個 ITaskService。」
ASP.NET Core 就會根據我們的設定提供對應的 TaskService 物件。
例如:

private readonly ITaskService _taskService;

public TasksController(ITaskService taskService)
{
    _taskService = taskService;
}

這叫做建構函式注入(Constructor Injection)。
其中:

  • ITaskService:定義 Service 應該具備哪些功能。
  • TaskService:真正實作這些功能。
  • taskService:Controller 用來操作任務的物件。

建立 ITaskService 介面

在昨天的專案新增 Services 資料夾。
1.對 TaskFlow3.Api 按右鍵。
2.選擇「新增資料夾」。
3.輸入 Services。
4.在 Services 裡新增 ITaskService.cs
5.在ITaskService.cs打底下這段程式碼

using TaskFlow3.Api.Models;

namespace TaskFlow3.Api.Services
{
    public interface ITaskService
    {
        List<TaskItem> GetAll();

        TaskItem? GetById(int id);

        TaskItem Create(string title);

        bool Update(int id, string title, bool isCompleted);

        bool Delete(int id);
    }
}

建立 TaskService

在 Services 裡建立第二個檔案:TaskService.cs

using TaskFlow3.Api.Models;

namespace TaskFlow3.Api.Services
{
    public class TaskService : ITaskService
    {
        private readonly List<TaskItem> _tasks = new()
        {
            new TaskItem
            {
                Id = 1,
                Title = "完成 C# 作業",
                IsCompleted = false
            },
            new TaskItem
            {
                Id = 2,
                Title = "複習英文",
                IsCompleted = true
            }
        };

        private int _nextId = 3;

        // 取得所有任務
        public List<TaskItem> GetAll()
        {
            return _tasks;
        }

        // 取得指定任務
        public TaskItem? GetById(int id)
        {
            return _tasks.Find(t => t.Id == id);
        }

        // 新增任務![https://ithelp.ithome.com.tw/upload/images/20261020/20184430V7ANyrEcur.png](https://ithelp.ithome.com.tw/upload/images/20261020/20184430V7ANyrEcur.png)
        public TaskItem Create(string title)
        {
            TaskItem task = new TaskItem
            {
                Id = _nextId++,
                Title = title,
                IsCompleted = false
            };

            _tasks.Add(task);

            return task;
        }

        // 修改任務
        public bool Update(
            int id,
            string title,
            bool isCompleted)
        {
            TaskItem? task = GetById(id);

            if (task == null)
            {
                return false;
            }

            task.Title = title;
            task.IsCompleted = isCompleted;

            return true;
        }

        // 刪除任務
        public bool Delete(int id)
        {
            TaskItem? task = GetById(id);

            if (task == null)
            {
                return false;
            }

            _tasks.Remove(task);

            return true;
        }
    }
}

在 Program.cs 註冊 DI

using TaskFlow3.Api.Services;
var builder = WebApplication.CreateBuilder(args);

// OpenAPI
builder.Services.AddOpenApi();

// Controller
builder.Services.AddControllers();
builder.Services.AddSingleton<ITaskService, TaskService>();

var app = builder.Build();

// 開發環境才啟用 OpenAPI 和 Swagger UI
if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();

    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/openapi/v1.json", "v1");
    });
}

app.UseHttpsRedirection();

// 啟用 Controller 路由
app.MapControllers();

// 測試路由
app.MapGet("/test", () => "伺服器正常運作!");

app.Run();

修改 TasksController.cs

using Microsoft.AspNetCore.Mvc;
using TaskFlow2.Api.DTOs;
using TaskFlow2.Api.Models;
using TaskFlow2.Api.Services;

namespace TaskFlow2.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class TasksController : ControllerBase
    {
        private readonly ITaskService _taskService;

        // 建構函式注入
        public TasksController(ITaskService taskService)
        {
            _taskService = taskService;
        }

        // GET /api/tasks
        [HttpGet]
        public ActionResult<List<TaskDto>> GetTasks()
        {
            var tasks = _taskService.GetAll();

            var result = tasks.Select(t => ToDto(t)).ToList();

            return Ok(result);
        }

        // GET /api/tasks/1
        [HttpGet("{id:int}")]
        public ActionResult<TaskDto> GetTask(int id)
        {
            TaskItem? task = _taskService.GetById(id);

            if (task == null)
            {
                return NotFound("找不到這筆任務");
            }

            return Ok(ToDto(task));
        }

        // POST /api/tasks
        [HttpPost]
        public ActionResult<TaskDto> CreateTask(
            [FromBody] CreateTaskDto input)
        {
            if (input == null ||
                string.IsNullOrWhiteSpace(input.Title))
            {
                return BadRequest("任務名稱不可空白");
            }

            TaskItem task = _taskService.Create(input.Title);

            return CreatedAtAction(
                nameof(GetTask),
                new { id = task.Id },
                ToDto(task)
            );
        }

        // PUT /api/tasks/1
        [HttpPut("{id:int}")]
        public IActionResult UpdateTask(
            int id,
            [FromBody] UpdateTaskDto input)
        {
            if (input == null ||
                string.IsNullOrWhiteSpace(input.Title))
            {
                return BadRequest("任務名稱不可空白");
            }

            bool success = _taskService.Update(
                id,
                input.Title,
                input.IsCompleted
            );

            if (!success)
            {
                return NotFound("找不到這筆任務");
            }

            return NoContent();
        }

        // DELETE /api/tasks/1
        [HttpDelete("{id:int}")]
        public IActionResult DeleteTask(int id)
        {
            bool success = _taskService.Delete(id);

            if (!success)
            {
                return NotFound("找不到這筆任務");
            }

            return NoContent();
        }

        // Model 轉 DTO
        private static TaskDto ToDto(TaskItem task)
        {
            return new TaskDto
            {
                Id = task.Id,
                Title = task.Title,
                IsCompleted = task.IsCompleted
            };
        }
    }
}

結果
https://ithelp.ithome.com.tw/upload/images/20261020/20184430s8fU9oNyjb.png
https://ithelp.ithome.com.tw/upload/images/20261020/20184430XiDw04LvUM.png


*提醒邦友,使用第三方服務/API 時,請務必評估資安風險與隱私保護
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言