在 Access 資料庫環境中,新增文章後需立即取得 ArticleID (自增欄位) 以供後續關聯操作 (如上傳圖片、建立分類關聯)。本文展示一套「預存程序 + 手動取得 Max ID + 1」的經典實作模式。
GetAutoID("ArticleID", "Article", connStr) 取得下一可用 ID。AddArticle。public int AddArticle(string title, string author, string from, int creator, int modifier,
string content, int channelId, bool isTop, bool isCommend, bool isCheck,
string keyword, string introduction)
{
int articleId = -1;
// HTML 編碼防 XSS
title = HttpUtility.HtmlEncode(title);
author = HttpUtility.HtmlEncode(author);
keyword = HttpUtility.HtmlEncode(keyword);
using (OleDbConnection conn = new OleDbConnection(Globals.ConnectString))
using (OleDbCommand cmd = new OleDbCommand("AddArticle", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// 關鍵:先取得下一個 ID
articleId = DataProvider.GetAutoID("ArticleID", "Article", Globals.ConnectString);
cmd.Parameters.Add("ArticleID", articleId);
cmd.Parameters.Add("ArticleTitle", title);
cmd.Parameters.Add("Author", author);
cmd.Parameters.Add("ArticleFrom", from);
cmd.Parameters.Add("Creator", creator);
cmd.Parameters.Add("ModifyBy", modifier);
cmd.Parameters.Add("Content", content);
cmd.Parameters.Add("ChannelID", channelId);
cmd.Parameters.Add("IsOnTop", isTop);
cmd.Parameters.Add("IsCommend", isCommend);
cmd.Parameters.Add("IsCheck", isCheck);
cmd.Parameters.Add("Keyword", keyword);
cmd.Parameters.Add("ArticleIntroduction", introduction);
DataProvider.ExecNonQueryOle(cmd);
}
return articleId;
}
public static int GetAutoID(string fieldName, string tableName, string connectString)
{
using (OleDbConnection conn = new OleDbConnection(connectString))
using (OleDbCommand cmd = new OleDbCommand($"Select Max({fieldName}) as MaxID from {tableName}", conn))
{
cmd.CommandType = CommandType.Text;
DataSet ds = GetDataSetOle(cmd);
if (ds.Tables[0].Rows[0][0] == DBNull.Value)
return 1; // 資料表為空
return int.Parse(ds.Tables[0].Rows[0][0].ToString()) + 1;
}
}
public static DataSet GetDataSetOle(OleDbCommand cmd)
{
using (OleDbDataAdapter adapter = new OleDbDataAdapter(cmd))
{
DataSet ds = new DataSet();
if (cmd.Connection.State != ConnectionState.Open) cmd.Connection.Open();
adapter.Fill(ds);
return ds;
}
}
CREATE PROCEDURE AddArticle
(
ArticleID LONG,
ArticleTitle TEXT(255),
Author TEXT(100),
ArticleFrom TEXT(100),
Creator LONG,
ModifyBy LONG,
Content MEMO,
ChannelID LONG,
IsOnTop YESNO,
IsCommend YESNO,
IsCheck YESNO,
Keyword TEXT(100),
ArticleIntroduction MEMO
)
AS
INSERT INTO Article (ArticleID, ArticleTitle, Author, ArticleFrom, Creator, ModifyBy, Content, ChannelID, IsOnTop, IsCommend, IsCheck, Keyword, ArticleIntroduction)
VALUES (ArticleID, ArticleTitle, Author, ArticleFrom, Creator, ModifyBy, Content, ChannelID, IsOnTop, IsCommend, IsCheck, Keyword, ArticleIntroduction);
| 風險 | 說明 | 改進建議 |
|---|---|---|
| 並發衝突 | 多用戶同時新增,Max(ID)+1 可能重複 | 改用 @@IDENTITY 或資料庫層面自增欄位,勿手動算 ID |
| SQL Injection | GetAutoID 字串拼接 Table/Field Name | 白名單驗證欄位/表名,或改用參數化查詢 (Access 不支援參數化識別碼) |
| 效能 | 每次新增需額外查詢 Max(ID) | 單指令 INSERT ...; SELECT @@IDENTITY 完成 |
// 使用 OleDb 單指令取得新增 ID (Access 支援)
cmd.CommandText = "INSERT INTO Article(...) VALUES(...); SELECT @@IDENTITY";
int newId = Convert.ToInt32(cmd.ExecuteScalar());
此做法:
「預存程序 + 手動取 ID」是早期 Access 開發的典型模式,理解其運作邏輯有助於維護舊系統。新專案或重構時,強烈建議採用 @@IDENTITY 或遷移至支援 SCOPE_IDENTITY()、OUTPUT INSERTED.ID 的現代資料庫 (SQL Server, PostgreSQL, SQLite),從根本上消除並發與效能隱憂。