iT邦幫忙

0

Day 19|使用 EF Core 實作真正的資料庫 CRUD

c#
  • 分享至 

  • xImage
  •  

今日學習目標

  • 理解記憶體 CRUD 與資料庫 CRUD 的差別
  • 認識 EF Core 的非同步操作
  • 將 ITaskService 改成非同步方法
  • 修改 TaskService 與 TasksController
  • 將 DI 從 Singleton 改成 Scoped

今天與昨天有什麼不同

List TaskItem(記憶體):重新啟動後,新增資料會消失
taskflow-ef.db(SQLite):儲存在磁碟中,重新啟動後仍能讀取

認識 EF Core 的 CRUD 方法

https://ithelp.ithome.com.tw/upload/images/20261022/20184430ahCN0lDa2W.png

修改 ITaskService.cs

一樣使用昨日專案

using TaskFlow3.Api.Models;

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

        Task<TaskItem?> GetByIdAsync(int id);

        Task<TaskItem> CreateAsync(string title);

        Task<bool> UpdateAsync(
            int id,
            string title,
            bool isCompleted);

        Task<bool> DeleteAsync(int id);
    }
}

修改 TaskService.cs

using Microsoft.EntityFrameworkCore;
using TaskFlow3.Api.Data;
using TaskFlow3.Api.Models;

namespace TaskFlow3.Api.Services
{
    public class TaskService : ITaskService
    {
        private readonly TaskFlowDbContext _db;

        // 透過 DI 取得 DbContext
        public TaskService(TaskFlowDbContext db)
        {
            _db = db;
        }

        // GET:查詢所有任務
        public async Task<List<TaskItem>> GetAllAsync()
        {
            return await _db.Tasks.ToListAsync();
        }

        // GET:依 Id 查詢
        public async Task<TaskItem?> GetByIdAsync(int id)
        {
            return await _db.Tasks.FindAsync(id);
        }

        // POST:新增任務
        public async Task<TaskItem> CreateAsync(string title)
        {
            TaskItem task = new TaskItem
            {
                Title = title,
                IsCompleted = false
            };

            await _db.Tasks.AddAsync(task);

            await _db.SaveChangesAsync();

            return task;
        }

        // PUT:修改任務
        public async Task<bool> UpdateAsync(
            int id,
            string title,
            bool isCompleted)
        {
            TaskItem? task = await _db.Tasks.FindAsync(id);

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

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

            await _db.SaveChangesAsync();

            return true;
        }

        // DELETE:刪除任務
        public async Task<bool> DeleteAsync(int id)
        {
            TaskItem? task = await _db.Tasks.FindAsync(id);

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

            _db.Tasks.Remove(task);

            await _db.SaveChangesAsync();

            return true;
        }
    }
}

修改 Program.cs 的 DI 設定

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

// OpenAPI
builder.Services.AddOpenApi();

// Controller
builder.Services.AddControllers();
builder.Services.AddScoped<ITaskService, TaskService>();
string dbPath = Path.Combine(
    builder.Environment.ContentRootPath,
    "taskflow-ef.db"
);

builder.Services.AddDbContext<TaskFlowDbContext>(options =>
    options.UseSqlite($"Data Source={dbPath}")
);

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.MapGet("/db-check", async (TaskFlowDbContext db) =>
{
    var tasks = await db.Tasks.ToListAsync();

    return Results.Ok(tasks);
});

app.Run();

修改 TasksController.cs

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

namespace TaskFlow3.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 async Task<ActionResult<List<TaskDto>>> GetTasks()
        {
            var tasks = await _taskService.GetAllAsync();

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

            return Ok(result);
        }

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

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

            return Ok(ToDto(task));
        }

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

            TaskItem task =
                await _taskService.CreateAsync(input.Title);

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

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

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

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

            return NoContent();
        }

        // DELETE /api/tasks/1
        [HttpDelete("{id:int}")]
        public async Task<IActionResult> DeleteTask(int id)
        {
            bool success = await _taskService.DeleteAsync(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
            };
        }
    }
}

結果
在 VS Code 終端機執行:dotnet build,接著執行:dotnet run
https://ithelp.ithome.com.tw/upload/images/20261022/20184430XwJx8g2KNV.png


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

尚未有邦友留言

立即登入留言