Try   HackMD
tags: Docker, .NET Core 3.1, Synology, MVC, Oracle

在 .NET Core 3.1 應用程式中整合 Oracle 資料庫

本文由 Grok AI 協助編輯

本文假設讀者已完成第一篇「在 Synology NAS 的 Docker 環境中部署 .NET Core 3.1 應用程式」的步驟,並將進一步介紹 Oracle 資料庫整合的開發與部署流程。我們將使用一個簡單的「書籍清單」範例來說明。



實驗環境

  • Synology NAS: RS3614RPxs / DS720P
  • DSM: 6.2.4-25556 Update 2
  • Windows 開發環境: .NET Core 3.1 SDK, Visual Studio 2019
  • 資料庫: Oracle(版本自定,假設運行於獨立伺服器)

第一部分:應用程式擴展概述

在第一篇文章中,我們部署了一個基本的 ASP.NET Core 3.1 MVC 應用程式(SimpleWeb),包含預設的首頁和隱私頁面。本文將擴展此應用程式,新增一個「書籍清單」功能:

  • 書籍管理:連線 Oracle 資料庫,查詢並展示書籍資料。

此功能將透過新增控制器、模型和資料庫上下文實現,並最終部署至 Synology NAS 的 Docker 環境。

檔案結構樹狀圖

完成本部分後,專案的檔案結構如下:

SimpleWeb/
├── Controllers/
│   ├── BooksController.cs        // 書籍控制器
│   ├── HomeController.cs         // 首頁控制器
├── Data/
│   ├── OracleDbContext.cs        // Oracle 資料庫上下文
├── Models/
│   ├── Book.cs                   // 書籍模型
│   ├── ErrorViewModel.cs         // 錯誤頁面模型
├── Views/
│   ├── Books/
│   │   ├── Index.cshtml          // 書籍清單視圖
│   ├── Home/
│   │   ├── Index.cshtml          // 首頁
│   │   ├── Privacy.cshtml        // 隱私頁面
│   ├── Shared/
│   │   ├── _Layout.cshtml        // 共用佈局
│   │   ├── Error.cshtml          // 錯誤頁面
├── wwwroot/
│   ├── css/
│   │   └── site.css              // 自訂樣式
│   ├── js/
│   │   └── site.js               // 自訂腳本
│   ├── lib/
│   │   ├── bootstrap/            // Bootstrap 框架
│   │   ├── jquery/               // jQuery 庫
│   │   ├── jquery-validation/    // 表單驗證
├── appsettings.json              // 應用程式配置(包含連線字串)
├── Program.cs                    // 程式進入點
├── Startup.cs                    // 服務與中介軟體配置
├── SimpleWeb.csproj              // 專案檔案

第二部分:開發 Oracle 資料庫整合功能

步驟 1:安裝必要的 NuGet 套件

在 Visual Studio 2019 中,打開 SimpleWeb 專案,透過「工具 > NuGet 套件管理員 > 管理解決方案的 NuGet 套件」安裝以下套件:

  1. Oracle 支援
    • 安裝 Oracle.EntityFrameworkCore(版本與 .NET Core 3.1 相容,例如 3.19.0)。
  2. Entity Framework Core
    • 安裝 Microsoft.EntityFrameworkCore(版本 3.1.x,例如 3.1.10)。

或者,在專案根目錄下使用命令列:

dotnet add package Oracle.EntityFrameworkCore --version 3.19.0
dotnet add package Microsoft.EntityFrameworkCore --version 3.1.10

步驟 2:定義模型與資料庫上下文

  1. 新增模型

    • Models 資料夾中新增 Book.cs
      ​​​​​namespace SimpleWeb.Models
      ​​​​​{
      ​​​​​    public class Book
      ​​​​​    {
      ​​​​​        public int Id { get; set; }
      ​​​​​        public string Title { get; set; }
      ​​​​​        public string Author { get; set; }
      ​​​​​    }
      ​​​​​}
      
  2. 建立 Data 資料夾與 OracleDbContext

    • 在專案根目錄下新增一個名為 Data 的資料夾。
    • Data 資料夾中新增 OracleDbContext.cs
      ​​​​​using Microsoft.EntityFrameworkCore;
      ​​​​​using SimpleWeb.Models;
      
      ​​​​​namespace SimpleWeb.Data
      ​​​​​{
      ​​​​​    public class OracleDbContext : DbContext
      ​​​​​    {
      ​​​​​        public OracleDbContext(DbContextOptions<OracleDbContext> options) : base(options) { }
      
      ​​​​​        public DbSet<Book> Books { get; set; }
      ​​​​​    }
      ​​​​​}
      

    Data 資料夾與 OracleDbContext 的用途解釋

    • Data 資料夾

      • 這是一個慣用的資料夾名稱,用於存放與資料存取相關的程式碼,例如資料庫上下文(DbContext)、資料庫遷移檔案(若使用 EF Core 遷移)或其他資料存取邏輯。
      • 在本範例中,Data 資料夾的作用是集中管理與 Oracle 資料庫交互的類別,使專案結構更清晰。它分離了模型(Models 資料夾)和控制器(Controllers 資料夾),遵循 MVC 架構的關注點分離原則。
      • 若未來需要新增其他資料庫或資料存取服務,可以繼續在此資料夾中擴展。
    • OracleDbContext 的用途

      • OracleDbContext 是 Entity Framework Core(EF Core)中的一個類別,繼承自 DbContext,用於管理與 Oracle 資料庫的連線和資料操作。
      • 它充當應用程式與 Oracle 資料庫之間的橋樑,提供查詢、新增、更新和刪除資料的能力。例如,透過 DbSet<Book> Books 屬性,應用程式可以直接操作 BOOKS 表格的資料。
      • 在建構函數中,OracleDbContext 接收 DbContextOptions<OracleDbContext>,這是用來配置連線字串和其他資料庫設定的參數(稍後在 Startup.cs 中設定)。

步驟 3:配置服務與連線字串

  1. 編輯 appsettings.json

    • 在專案根目錄的 appsettings.json 中新增連線字串:
      ​​​​​{
      ​​​​​    "ConnectionStrings": {
      ​​​​​        "Oracle": "User Id=<user>;Password=<password>;Data Source=<oracle_host>:<port>/<service_name>"
      ​​​​​    },
      ​​​​​    "Logging": {
      ​​​​​        "LogLevel": {
      ​​​​​            "Default": "Information",
      ​​​​​            "Microsoft": "Warning",
      ​​​​​            "Microsoft.Hosting.Lifetime": "Information"
      ​​​​​        }
      ​​​​​    },
      ​​​​​    "AllowedHosts": "*"
      ​​​​​}
      
    • <user><password><oracle_host><port><service_name> 替換為實際值。例如:"User Id=scott;Password=tiger;Data Source=192.168.1.100:1521/ORCL"
  2. 編輯 Startup.cs

    • ConfigureServices 方法中註冊資料庫上下文:
      ​​​​​using SimpleWeb.Data;
      ​​​​​using Microsoft.EntityFrameworkCore;
      
      ​​​​​public void ConfigureServices(IServiceCollection services)
      ​​​​​{
      ​​​​​    services.AddControllersWithViews();
      
      ​​​​​    services.AddDbContext<OracleDbContext>(options =>
      ​​​​​        options.UseOracle(Configuration.GetConnectionString("Oracle")));
      ​​​​​}
      

步驟 4:新增控制器與視圖

  1. 新增 BooksController
    • Controllers 資料夾中新增 BooksController.cs
      ​​​​​using Microsoft.AspNetCore.Mvc;
      ​​​​​using SimpleWeb.Data;
      ​​​​​using SimpleWeb.Models;
      ​​​​​using System.Linq;
      
      ​​​​​namespace SimpleWeb.Controllers
      ​​​​​{
      ​​​​​    public class BooksController : Controller
      ​​​​​    {
      ​​​​​        private readonly OracleDbContext _oracleDbContext;
      
      ​​​​​        public BooksController(OracleDbContext oracleDbContext)
      ​​​​​        {
      ​​​​​            _oracleDbContext = oracleDbContext;
      ​​​​​        }
      
      ​​​​​        public IActionResult Index()
      ​​​​​        {
      ​​​​​            var books = _oracleDbContext.Books.ToList();
      ​​​​​            return View(books);
      ​​​​​        }
      
      ​​​​​        [HttpPost]
      ​​​​​        public IActionResult Add(string title, string author)
      ​​​​​        {
      ​​​​​            var book = new Book { Title = title, Author = author };
      ​​​​​            _oracleDbContext.Books.Add(book);
      ​​​​​            _oracleDbContext.SaveChanges();
      ​​​​​            return RedirectToAction("Index");
      ​​​​​        }
      ​​​​​    }
      ​​​​​}
      
    • Views/Books 資料夾中新增 Index.cshtml
      ​​​​​@model IEnumerable<SimpleWeb.Models.Book>
      
      ​​​​​<h2>Book List</h2>
      
      ​​​​​<form asp-action="Add" method="post">
      ​​​​​    <input name="title" placeholder="Book Title" />
      ​​​​​    <input name="author" placeholder="Author" />
      ​​​​​    <button type="submit">Add Book</button>
      ​​​​​</form>
      
      ​​​​​<table class="table">
      ​​​​​    <thead>
      ​​​​​        <tr>
      ​​​​​            <th>ID</th>
      ​​​​​            <th>Title</th>
      ​​​​​            <th>Author</th>
      ​​​​​        </tr>
      ​​​​​    </thead>
      ​​​​​    <tbody>
      ​​​​​        @foreach (var item in Model)
      ​​​​​        {
      ​​​​​            <tr>
      ​​​​​                <td>@item.Id</td>
      ​​​​​                <td>@item.Title</td>
      ​​​​​                <td>@item.Author</td>
      ​​​​​            </tr>
      ​​​​​        }
      ​​​​​    </tbody>
      ​​​​​</table>
      
模擬網頁輸出樣子

以下是訪問 http://<NAS_IP>:62005/Books 時的模擬網頁輸出(假設已新增兩本書並包含初始化資料):

Book List

[輸入框: Book Title] [輸入框: Author] [Add Book 按鈕]

ID | Title                 | Author
---------------------------------------------------------
1  | The Great Gatsby      | F. Scott Fitzgerald
2  | 1984                  | George Orwell
3  | To Kill a Mockingbird | Harper Lee      (假設剛新增)
當「Add Book」按鈕按下後的程式運作與模型綁定說明

當使用者按下「Add Book」按鈕時,會觸發以下流程:

  1. 表單提交

    • 使用者在表單中輸入標題(例如 "To Kill a Mockingbird")和作者(例如 "Harper Lee"),按下按鈕後,瀏覽器發送 HTTP POST 請求至 /Books/Add,攜帶表單資料:
      ​​​​​title=To Kill a Mockingbird&author=Harper Lee
      
  2. 路由處理

    • ASP.NET Core 的路由系統根據 URL(/Books/Add)和 HTTP 方法(POST),將請求導向 BooksController.Add 方法。
  3. 模型綁定(Model Binding)

    • 什麼是模型綁定
      • 模型綁定是 ASP.NET Core 的內建機制,用於將 HTTP 請求中的資料(例如表單欄位)自動映射到控制器方法的參數。
      • 在此例中,表單提交的資料為 title=To Kill a Mockingbird&author=Harper LeeASP.NET Core 會解析這些鍵值對,並根據參數名稱進行匹配。
    • 綁定過程
      • Add 方法的參數 string titlestring author 與表單的 <input name="title"><input name="author"> 名稱一致。
      • ASP.NET Core 將 title 的值 ("To Kill a Mockingbird") 綁定到 title 參數,author 的值 ("Harper Lee") 綁定到 author 參數。
    • 名稱匹配的關鍵
      • 若將參數改為 string titleName, string authorName,但表單仍使用 name="title"name="author",則綁定失敗,titleNameauthorName 會收到 null
      • 解決方法是同步修改表單為 <input name="titleName"><input name="authorName">,或使用 [FromForm(Name = "title")] 屬性明確指定綁定來源。
  4. 控制器執行

    • var book = new Book { Title = title, Author = author }; 創建一個新的 Book 物件,TitleAuthor 分別設為 "To Kill a Mockingbird" 和 "Harper Lee"。
    • _oracleDbContext.Books.Add(book)book 加入資料庫上下文,標記為待新增。
    • _oracleDbContext.SaveChanges() 生成並執行 SQL:
      ​​​​​INSERT INTO BOOKS (TITLE, AUTHOR) VALUES ('To Kill a Mockingbird', 'Harper Lee');
      
    • Oracle 自動生成 ID(例如 3),並由 EF Core 更新至 book.Id
    • RedirectToAction("Index") 重新導向至 /Books,觸發 Index 方法顯示更新後的清單。
  5. 資料庫交互

    • EF Core 透過連線字串(來自 appsettings.json 或環境變數)與 Oracle 資料庫通訊,完成資料插入。
  6. 頁面更新

    • 瀏覽器收到重新導向指令,載入更新後的 Index 頁面,顯示包含新書籍的清單。

第三部分:更新 Dockerfile

由於使用了 Oracle 資料庫,需確保 Dockerfile 包含必要的依賴。以下是更新後的 Dockerfile

FROM mcr.microsoft.com/dotnet/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80

# 設置時區
ENV TZ=Asia/Taipei
RUN apt-get update && apt-get install -y tzdata
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 安裝 Oracle 客戶端依賴
RUN apt-get install -y libaio1

# 設置 Oracle 語言環境
ENV NLS_LANG=AMERICAN_AMERICA.AL32UTF8

# 預設命令
ENTRYPOINT ["dotnet"]

更新說明

  • Oracle 依賴:包含 libaio1NLS_LANG 配置,確保容器內可連線 Oracle 資料庫。
  • 其他部分與第一篇一致。

重新建構映像

  1. 將更新後的 Dockerfile 儲存至 /volume1/docker/dotnet-runtime/
  2. 在 Synology DSM 的「控制面板 > 工作排程器」中,執行第一篇中配置的 Build Dotnet Runtime 任務(或重新執行 sh /volume1/docker/dotnet-runtime/build.sh)。
  3. 驗證新映像 dotnet-runtime:3.1 已更新。

第四部分:部署流程

步驟 1:初始化 Oracle 資料庫

在 Oracle 伺服器上建立表格:

CREATE TABLE BOOKS (
    ID NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    TITLE VARCHAR2(255),
    AUTHOR VARCHAR2(255)
);
  • 若需要測試資料,可插入範例:
    ​​INSERT INTO BOOKS (TITLE, AUTHOR) VALUES ('The Great Gatsby', 'F. Scott Fitzgerald');
    ​​INSERT INTO BOOKS (TITLE, AUTHOR) VALUES ('1984', 'George Orwell');
    ​​COMMIT;
    

步驟 2:發佈應用程式

在 Visual Studio 中:

  1. 右鍵點擊 SimpleWeb 專案,選擇「發佈」。
  2. 選擇「資料夾」,設置輸出路徑為 ./publish
  3. 點擊「發佈」。

或者,使用命令直接發佈至 NAS:

dotnet publish -c Release -r linux-x64 -f netcoreapp3.1 --self-contained false -o "\\172.19.48.55\docker\SimpleWeb"

步驟 3:將發佈檔案傳輸到 NAS

若未使用命令直接發佈,手動將 ./publish 目錄上傳至 /volume1/docker/mywebapp/。若已使用命令,則檔案已位於 NAS 的共享路徑。

步驟 4:將連線字串移至環境變數

為提高安全性,可將連線字串從 appsettings.json 移至環境變數:

  1. 移除 appsettings.json 中的連線字串
    • 編輯 appsettings.json,移除或註解掉 ConnectionStrings 部分:
      ​​​​​{
      ​​​​​    "ConnectionStrings": {
      ​​​​​        // "Oracle": "User Id=<user>;Password=<password>;Data Source=<oracle_host>:<port>/<service_name>"
      ​​​​​    },
      ​​​​​    "Logging": {
      ​​​​​        "LogLevel": {
      ​​​​​            "Default": "Information",
      ​​​​​            "Microsoft": "Warning",
      ​​​​​            "Microsoft.Hosting.Lifetime": "Information"
      ​​​​​        }
      ​​​​​    },
      ​​​​​    "AllowedHosts": "*"
      ​​​​​}
      
  2. 在 Docker 容器中配置環境變數
    • 在 DSM 的「Docker」套件中,編輯容器設定。
    • 在「環境」標籤中新增:
      • 變數:ConnectionStrings__Oracle
      • 值:User Id=<user>;Password=<password>;Data Source=<oracle_host>:<port>/<service_name>(例如 User Id=scott;Password=tiger;Data Source=192.168.1.100:1521/ORCL)。
  3. 運作原理
    • ASP.NET Core 會從環境變數中讀取 ConnectionStrings__Oracle,並映射到 Configuration.GetConnectionString("Oracle"),確保應用程式能正常連線。

步驟 5:啟動容器

  1. 打開 DSM 的「Docker」套件。
  2. 在「容器」標籤中,點擊「建立」:
    • 映像:選擇 dotnet-runtime:3.1
    • 容器名稱simpleweb-container
    • 進階設定
      • 端口設定:本地端口 62005,容器端口 5000
      • 卷冊:本地路徑 /volume1/docker/mywebapp/,容器路徑 /app
      • 環境(若使用環境變數):新增 ConnectionStrings__Oracle
      • 命令dotnet /app/SimpleWeb.dll
  3. 點擊「套用」啟動容器。

步驟 6:驗證部署

  • 訪問 http://<NAS_IP>:62005/Books 查看書籍清單。
  • 輸入書名和作者,新增書籍並確認資料正確儲存至 Oracle 資料庫。

第五部分:修復常見問題

  1. Oracle 連線失敗

    • 檢查 appsettings.json 或環境變數中的連線字串是否正確。
    • 確保 NAS 的 Docker 網路可訪問 Oracle 伺服器。
  2. 資料庫操作異常

    • 確認 libaio1 已正確安裝於容器中。
    • 檢查表格結構與模型是否匹配。

注意事項

  1. 資料庫存取:確保 Oracle 伺服器可從容器內訪問,可能需配置網路橋接。
  2. 安全性:建議使用環境變數儲存連線字串,避免硬編碼。
  3. 效能:若書籍數量增加,可考慮新增分頁查詢。

結論

本文展示了如何為 .NET Core 3.1 應用程式整合 Oracle 資料庫,並成功部署至 Synology NAS 的 Docker 環境。基於第一篇的基礎,讀者現在可以處理基本的 Oracle 資料庫操作,並根據需求進一步擴展功能。