![]()
Supabase .NET SDK 是您 Supabase 專案的 C# 用戶端。
它透過單一 API 提供 Auth、Database、Storage、Realtime 和 Edge Functions,並處理與 Supabase 平台的通訊。
版本 8.0.0 已發佈,同時也推出了新的 Supabase.Extensions.DependencyInjection 套件。
本文將說明如何設定用戶端,並逐步介紹基本操作。
如果您是從先前的主要版本升級,有些部分已有所變動。
您不需要了解所有細節就能跟隨本教學,但值得知道以下事項:
System.Text.Json。自訂轉換器與 [JsonProperty] 屬性需改用 System.Text.Json 的對應功能。HttpClient,並支援選擇性啟用重試與退避機制。如果您想查看完整的變更清單,請查閱 發行說明。
遷移指南 列出了您可能遇到的所有重大變更以及處理方式。
將 meta-package 新增至您的專案。
它會引入所有五個服務套件(Auth、Postgrest、Storage、Realtime 和 Functions):
_10
dotnet add package Supabase
如果您只需要其中一個服務,請直接參考該套件,例如 Supabase.Gotrue 用於 Auth,或 Supabase.Postgrest 用於資料庫。
SDK 會將資料表對應至 C# 類別。
模型繼承自 BaseModel,並使用屬性將其屬性與欄位連結。
以下是我們將在後續範例中使用的 todos 資料表,包含 id、task 和 is_complete:
_15
using Supabase.Postgrest.Attributes;
_15
using Supabase.Postgrest.Models;
_15
_15
[Table("todos")]
_15
public class Todo : BaseModel
_15
{
_15
[PrimaryKey("id")]
_15
public int Id { get; set; }
_15
_15
[Column("task")]
_15
public string Task { get; set; }
_15
_15
[Column("is_complete")]
_15
public bool IsComplete { get; set; }
_15
}
安裝相依性注入套件:
_10
dotnet add package Supabase.Extensions.DependencyInjection
在 Program.cs 中註冊用戶端:
_14
using Supabase.Core.Http;
_14
using Supabase.Extensions.DependencyInjection;
_14
_14
var builder = WebApplication.CreateBuilder(args);
_14
builder.Services.AddSupabase(
_14
supabaseUrl: builder.Configuration["Supabase:Url"]!,
_14
supabaseKey: builder.Configuration["Supabase:Key"]!,
_14
configureOptions: options =>
_14
{
_14
options.AutoConnectRealtime = true;
_14
options.PostgrestRetry = new RetryOptions { MaxRetries = 3 };
_14
});
_14
_14
var app = builder.Build();
AddSupabase 會將 Supabase.Client 註冊為 scoped 服務,因此您可以在需要的地方注入它:minimal API endpoint、controller 或您自己的服務。
_13
using Supabase;
_13
_13
public class TodoService(Client supabase)
_13
{
_13
public async Task<List<Todo>> GetOpen()
_13
{
_13
var result = await supabase
_13
.From<Todo>()
_13
.Where(t => t.IsComplete == false)
_13
.Get();
_13
return result.Models;
_13
}
_13
}
它也會分別註冊每個子用戶端(IPostgrestClient、IGotrueClient<User, Session>、IStorageClient<Bucket, FileObject>、IFunctionsClient、IRealtimeClient<RealtimeSocket, RealtimeChannel>),因此只需碰觸單一服務的類別就能只依賴該服務。
我們建置這個擴充套件,讓您能輕鬆設定用戶端,而無需自己手動連接。
Supabase.Client 在建構時會自行建立 HttpClient。如果您以 scoped 或 transient 方式註冊,每次解析都會建立新的 HttpClient(以及新的 socket),在負載下會耗盡連線池。
AddSupabase 透過 IHttpClientFactory 來連接用戶端,讓處理程式能被共用與重用。
為了保持一致性,它會取代您透過 configureOptions 傳入的任何 HttpClient;您的 proxy、重試、Realtime 與其他選項仍然有效。
在 ASP.NET Core 之外的 Console 應用程式、Desktop、Mobile 或 Unity 中,請自行建構用戶端並只初始化一次:
_13
using Supabase;
_13
_13
var url = Environment.GetEnvironmentVariable("SUPABASE_URL")!;
_13
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY")!;
_13
_13
var options = new SupabaseOptions
_13
{
_13
AutoRefreshToken = true,
_13
AutoConnectRealtime = true,
_13
};
_13
_13
var supabase = new Client(url, key, options);
_13
await supabase.InitializeAsync();
InitializeAsync 會還原已儲存的工作階段,並在開啟 AutoConnectRealtime 時建立 Realtime 連線。
預設的工作階段處理程式不會儲存任何資料,因此若要在重新啟動後還原登入,請將 SupabaseOptions.SessionHandler 設定為您自己實作的版本,用來儲存與載入工作階段。
請只建立一次用戶端並在應用程式中重複使用。
每次操作都建立新用戶端意味著每次都會建立新的 HttpClient,這與 DI 套件在伺服器上解決的問題相同。
以下範例會使用 supabase 變數,它是一個 Supabase.Client 實例。
From<T>() 會針對 T 所對應的資料表開啟查詢。
您可以使用 Where 進行篩選,然後呼叫 Get 來執行查詢。
Get 會回傳 ModeledResponse<T>,您可以在 Models 中找到資料列:
_10
ModeledResponse<Todo> result = await supabase
_10
.From<Todo>()
_10
.Where(t => t.IsComplete == false)
_10
.Get();
_10
List<Todo> todos = result.Models;
Insert 可以寫入單一模型或清單:
_10
await supabase.From<Todo>().Insert(new Todo { Task = "Write the launch notes", IsComplete = false });
更新實體有兩種方式,兩者都有效。
這取決於您是否已取得該資料列。
如果您已經取得模型,請修改其屬性並呼叫 Update。
從 Get 回傳的資料列會帶有自己的識別資訊,因此它知道要寫入哪一筆記錄:
_10
var result = await supabase.From<Todo>().Where(t => t.Id == 1).Get();
_10
var todo = result.Models.First();
_10
_10
todo.IsComplete = true;
_10
await todo.Update<Todo>();
_10
_10
await todo.Delete<Todo>();
如果您沒有模型,可以使用查詢建置器,這能省去一次 Get 查詢。
設定新的值、篩選資料列,然後執行:
_10
await supabase
_10
.From<Todo>()
_10
.Set(t => t.IsComplete, true)
_10
.Where(t => t.IsComplete == false)
_10
.Update();
_10
_10
await supabase
_10
.From<Todo>()
_10
.Where(t => t.IsComplete == true)
_10
.Delete();
雖然這看起來與 Entity Framework 的建置方式非常相似,但行為並不相同。
這裡沒有DbContext、沒有變更追蹤器,也沒有SaveChanges。
修改模型的屬性不會有任何作用,直到您呼叫Update,而且每次呼叫都是獨立的 HTTP 請求。
驗證功能位於 supabase.Auth。
使用電子郵件與密碼註冊並登入使用者:
_10
await supabase.Auth.SignUp("[email protected]", "a-secure-password");
_10
_10
var session = await supabase.Auth.SignIn("[email protected]", "a-secure-password");
當 AutoRefreshToken 啟用時,用戶端會在存取權杖到期前自動重新整理,並將其附加至您的資料庫與 Storage 呼叫,因此 Row-Level-Security 會套用至已登入的使用者。
相同的 supabase.Auth 介面也涵蓋 OAuth、magic links、電話與 SSO。
_10
await supabase.Storage
_10
.From("avatars")
_10
.Upload("./local/photo.png", "maria/photo.png");
版本 8.0.0 新增了可發佈的金鑰與秘密 API 金鑰,與現有的 anon 和 service 金鑰並存。
在任何會發佈給使用者的應用程式中使用可發佈金鑰或 anon 金鑰。
請將 secret 和 service 金鑰保留在伺服器上。
Service 金鑰會繞過 Row Level Security,因此不應放在用戶端應用程式中。
當您需要此等級的存取權時,請為其使用獨立的用戶端。
您現在已設定好用戶端,可以從 .NET 執行查詢、驗證與 Storage 操作。接下來您可以:
https://supabase.com/blog/getting-started-with-the-supabase-dotnet-sdk
https://www.worldprogramming.org/posts/getting-started-with-the-supabase-net-sdk-gquayn
![]()
過去幾個月,我花了更多時間與 AI 程式碼代理合作。它們速度快、實用,而且在許多情況下,它們能比手動操作更快地修復小錯誤或實作特定變更。
但在使用它們的過程中,我一直回到同一個問題:我如何知道圍繞修復的證據是否真的有意義?
很容易要求代理修改一些程式碼、執行測試,然後回傳綠燈結果。問題在於綠燈測試只告訴我最終版本通過了。它並不一定能告訴我該測試是否能夠偵測到原始的錯誤。
這個差異聽起來很小,但它非常重要。
想像一個 AI 代理修復了一個錯誤並新增了一個迴歸測試。最終測試執行通過,所以變更看起來不錯。現在想像我們拿完全相同的測試去對壞掉的程式碼版本執行,它在那裡也通過了。
在這種情況下,測試是綠燈的,但它並沒有真正證明錯誤已被修復。
這就是我想要更系統化處理的那種問題。
原則很簡單:
壞掉的基準 + 迴歸探測 = 失敗
修復的結果 + 相同的迴歸探測 = 通過
如果相同的探測在壞掉的版本上失敗,而在修復的版本上通過,證據就會強得多。
如果它在兩個版本上都通過,那麼測試可能並沒有真正測試我們關心的錯誤。
這個想法成為我一直在建構的一個小型工具的基礎,叫做 AI Coding Agent Reliability Kit。
它是一個以 Linux 為優先的本地工作流程,用來以更強的機械化證據審查 AI 輔助的錯誤修復。
目標不是取代程式碼審查、安全審查或工程判斷。目標是讓圍繞 AI 生成修復所產生的證據更容易檢查且更難被誤解。
當我開始研究這個時,我不想要另一個只是執行指令並印出成功或失敗的包裝工具。
我想要一個能回答關於變更幾個問題的工作流程。
代理被允許修改什麼?
它是否停留在該範圍內?
最終驗證指令是否真的通過?
迴歸探測是否在壞掉的基準上失敗?
結果是否能在乾淨的重播中重現?
另一個人是否能在不需手動重建一切的情況下審查證據?
這些是基本問題,但一旦程式碼代理開始進行更大或更頻繁的變更,它們就會變得重要。
這個工具所做的事情之一,是在程式碼工作開始前定義任務和允許的路徑。
這很有用,因為代理有時能在修復請求問題的同時也修改不相關的檔案。最終結果可能仍然能運作,但變更範圍會變得比必要的大。
對於一個聚焦的錯誤修復,我通常想要一個清楚的邊界。
例如,如果任務只需要在一個來源檔案和一個迴歸測試中進行變更,那麼對不相關的 README、設定檔或其他套件的修改應該立即可見。
這個工具會追蹤宣告的範圍,並檢查最終變更是否停留在其中。
如果有東西在允許的邊界之外被改變,則會被回報,而不是默默接受結果。
一般的工作流程通常在類似以下的情況後結束:
pytest tests/test_bug.py
Enter fullscreen mode
Exit fullscreen mode
指令成功退出,所以任務被視為完成。
這很有用,但對於迴歸修復,我想要多一項資訊。
我想要知道當相同的探測對已知的壞掉基準執行時會發生什麼。
一個好的迴歸探測通常應該能區分這兩個狀態。
例如:
Broken version: FAIL
Fixed version: PASS
Enter fullscreen mode
Exit fullscreen mode
這是比以下情況強得多的訊號:
Broken version: PASS
Fixed version: PASS
Enter fullscreen mode
Exit fullscreen mode
第二個結果並不一定表示程式碼有錯。它表示證據很弱。
這個區別很重要。
工作流程不應該假裝一個測試證明了它並沒有證明的事情。
這是我建構這個工具的主要原因之一。
一個測試看起來可能完全有效,卻仍然無法區分壞掉和修復的版本。
例如,想像一個函數應該要將兩個數字相乘,但目前卻是相加。
壞掉的程式碼可能看起來像這樣:
def calculate(a, b):
return a + b
Enter fullscreen mode
Exit fullscreen mode
修復將它改成:
def calculate(a, b):
return a * b
Enter fullscreen mode
Exit fullscreen mode
現在想像測試只檢查這個:
def test_calculate_exists():
assert callable(calculate)
Enter fullscreen mode
Exit fullscreen mode
這個測試在修復後是綠燈的,但它在修復前也是綠燈的。
它證明了函數存在。它並沒有證明錯誤已被修復。
這種結果不應該被視為與真正的迴歸測試相同。
這個工具會偵測這種情況,並將證據回報為非區分性,而不是將最終的綠燈結果視為足夠。
我想要處理的另一種情況是,實際的程式碼變更是正確的,但允許範圍之外的東西被修改了。
這可能意外發生,或是因為自動化工作流程在執行期間碰觸到另一個檔案。
假設代理被允許修改:
src/formatter.py
tests/test_formatter.py
Enter fullscreen mode
Exit fullscreen mode
來源修復和測試可能都是正確的,但如果最終變更也修改了:
README.md
Enter fullscreen mode
Exit fullscreen mode
那麼工作流程應該另外回報這一點。
這裡的重要觀點是,正確性和範圍是不同的事情。
一個正確的來源修復並不會自動讓不相關的變更變得可接受。
這個工具會將這些關注點分開,讓審查者能清楚看到到底發生了什麼。
我想要的另一個功能是能夠在需要時從乾淨狀態重現結果。
有時候變更會通過,是因為工作環境中留下了某些東西:產生的檔案、快取、本地狀態,或另一個未提交的變更。
乾淨重播有助於回答一個簡單的問題:
相同的結果是否能在不依賴目前工作目錄狀態的情況下被重現?
並非每個任務都需要完整的乾淨重播,但對於重現性很重要的變更,它能給審查者另一個有用的證據。
自動化的其中一個問題是它可能產生太多輸出。
原始日誌在調查某些事情時很有用,但它們並不總是審查變更的最佳方式。
這個工具會產生一個結構化的審查摘要,讓主要結果更容易檢查。
審查者可以看到以下項目:
Scope: PASS
Final verification: PASS
Broken baseline probe: FAIL
Fixed probe: PASS
Differential evidence: PASS
Replay: REPRODUCIBLE
Enter fullscreen mode
Exit fullscreen mode
如果有東西失敗,摘要應該清楚顯示,而不是隱藏在成功的最終指令後面。
這個想法是要減少審查者需要做的重建工作量。
工作流程也可以匯出證明材料並驗證其完整性。
當證據需要被移動、封存或稍後審查時,這很有用。
工作流程不僅僅說某個指令在某個時間點通過,它還能保留相關的結果記錄,並驗證預期的材料沒有改變。
這並不是要將工具變成安全認證系統。它只是一種讓每次執行的證據更易檢查和重現的方式。
我想要保持非常清楚的一件事是最終狀態的意義。
當工具回報:
READY_FOR_REVIEW
Enter fullscreen mode
Exit fullscreen mode
它並不表示:
The code is definitely correct.
Enter fullscreen mode
Exit fullscreen mode
它也不表示:
The code is secure.
Enter fullscreen mode
Exit fullscreen mode
它同樣不表示:
This is automatically production-ready.
Enter fullscreen mode
Exit fullscreen mode
它的意思是設定的機械化證據閘門已通過,結果已準備好讓人類審查者檢查。
這個區別是故意的。
我不認為自動化工作流程應該默默地把機械化證據升級成語義化的工程判斷。
人類審查者仍然需要去看實際的變更,並決定它對系統來說是否正確。
人類開發者在進行變更時,通常腦中會有一些脈絡。他們知道自己碰了什麼、試圖修復什麼,以及他們做了什麼假設。
使用 AI 程式碼代理時,更多的工作可以自動且快速地發生。
這種速度很有用,但這也意味著更容易僅僅因為代理說任務完成且最終測試是綠燈就接受結果。
實作變得越快,就越有必要讓驗證流程變得明確。
我並不把這視為反對程式碼代理的論點。我有在使用它們。
重點是,如果代理要為我們進行更多變更,那麼圍繞這些變更的證據也應該變得更好。
工具的第一個版本在環境假設上刻意保持簡單。
它被設計用於 Linux,並使用本地工作流程,包含:
我希望第一個版本能保持專注,而不是宣稱支援我還沒有適當驗證的平台。
Windows 和 macOS 不是已驗證的 V1 環境的一部分。
這可能在之後會改變,但我寧願明確說明實際測試過的內容。
這個工具並不試圖證明一個程式是正確的。
它不會取代單元測試、整合測試、靜態分析、安全審查,或良好的人類程式碼審查。
它也不會自動讓弱測試變強。
如果迴歸探測設計不佳,工作流程可以偵測某些類型的弱證據,例如一個在壞掉和修復版本上都通過的探測,但它無法理解每個專案的每一個語義需求。
它所做的是提供一個圍繞現有證據的更嚴格流程。
對我來說,那就是有用的部分。
一個典型流程大致看起來像這樣:
它刻意不是一個完全自主的核准系統。
最後一步仍然是審查。
假設錯誤出在一個 slug 函數。
壞掉的實作是:
def slugify(text):
return text.strip().lower().replace(" ", "")
Enter fullscreen mode
Exit fullscreen mode
對於這個輸入:
Hello World
Enter fullscreen mode
Exit fullscreen mode
壞掉的結果是:
helloworld
Enter fullscreen mode
Exit fullscreen mode
要求的結果是:
hello-world
Enter fullscreen mode
Exit fullscreen mode
修復可能是:
def slugify(text):
return "-".join(text.lower().split())
Enter fullscreen mode
Exit fullscreen mode
而迴歸探測是:
def test_slugify():
assert slugify("Hello World") == "hello-world"
assert slugify("Two Words") == "two-words"
Enter fullscreen mode
Exit fullscreen mode
現在有用的部分不僅是這個測試在修復後通過。
更強的證據是:
Broken baseline: FAIL
Fixed result: PASS
Enter fullscreen mode
Exit fullscreen mode
這告訴審查者,探測實際上偵測到了正在被修正的行為。
主要原因是,我想要更多而不是更少地使用程式碼代理。
但如果我打算委派更多實作工作給它們,我想要在「代理說它完成了」和「有足夠證據讓我認真審查結果」之間有更好的界線。
這就是這個專案試圖填補的差距。
它仍然是第一個版本,我預期隨著我在更多專案上使用它並發現更多邊緣案例,工作流程會繼續演進。
目前,焦點很窄:有邊界的變更、更強的迴歸證據、重現性,以及人類審查。
如果這聽起來對你使用程式碼代理的方式有幫助,我已在這裡釋出第一個版本:
https://sovereign-system.itch.io/ai-coding-agent-reliability-kit
https://dev.to/rabeh_arch/why-green-tests-are-not-enough-for-ai-coding-agents-app
https://www.worldprogramming.org/posts/why-green-tests-are-not-enough-for-ai-coding-agents-h3x7u8
![]()
專案狀態 — 2026 年 10 月 22 日: 這是 Cognee 議題 #6222 的設計提案。維護者指派尚未確定。連接器尚未實作或測試,也還沒有 PR。本文說明提出的範圍與驗證計畫。
專案 wiki 所保存的不只是文件。它記錄了團隊為何選擇某種做法、哪個服務擁有某項責任,以及事件發生時的經過。一個有用的記憶整合應該保留這些筆記及其來源連結,同時也要忘記作者已刪除的內容。
在 Mergetober 期間,提出的貢獻是一個 TiddlyWiki 與 Cognee 之間的唯讀連接器。初始目標是 TiddlyWiki 的官方 Node.js 伺服器,選取的文字筆記會透過 Cognee 現有的 document-source 路徑被攝取為文件。
規劃的示範使用一個專用的本地 wiki,包含關於服務所有權、部署決策和事件的合成筆記。它應該能支援以下問題:「誰擁有帳單?」以及「這個專案為什麼選擇目前的部署方式?」
接著會修改所有權筆記、執行另一次同步,並檢查更新後的答案。最後會刪除一則筆記,並確認過時資訊已被遺忘。這些是規劃的檢查項目,並非目前已取得的結果。
TiddlyWiki 提供了 筆記清單端點 與 單一筆記端點。連接器提案使用允許的預設清單功能,並在本機進行標題或標籤篩選。
每份文件應保留筆記標題、選取的標籤、文字內容,以及來源永久連結。第一版會排除系統/設定筆記與二進位附件。它會保留筆記文字,而不執行 wiki 巨集或追隨連結檔案。
提出的同步方式是所選筆記的完整快照。Cognee 可利用穩定的語意內容避免重新處理未變更的文件,而成功的快照則提供調和已刪除或重新命名筆記所需的清冊。
這也帶來一項責任:驗證失敗、格式錯誤的回應或筆記擷取失敗都必須中止執行。將部分擷取視為完整清冊,可能導致仍存在的內容被移除。
在清單與擷取之間消失的筆記需要有限次的重試或明確標記為失敗的執行。刪除最後一則筆記也需要真實的整合測試;僅通過 HTTP 單元測試無法確認圖譜清理是否正常運作。
| 檢查項目 | 目前狀態 |
|---|---|
| HTTP 驗證、標題編碼、標籤剖析與錯誤處理 | 已規劃 |
| 真實的 dlt 管線與 Cognee 文件攝取 | 已規劃 |
| 在 loopback 上執行的官方 TiddlyWiki 伺服器 | 已規劃 |
| 未變更的同步、編輯、重新命名、範圍變更與刪除 | 已規劃 |
| 最後一則筆記的刪除與失敗執行保留 | 已規劃 |
| 端到端搜尋答案與來源出處 | 已規劃 |
完成的逐步說明將包含已測試的套件版本、可重現的設定指令、實際的 PR 連結,以及觀察到的結果。模擬測試與即時服務檢查將另外報告。
提案與指派請求 包含套件佈局與驗收計畫。實作正等待指派以及澄清 Mergetober 對 AI 協助的期望。
AI 揭露: 本提案與文章是依我的要求由 Codex 研究與起草。文章標記為 Fully Autonomous 以反映此作者身分。實作細節與測試結果將在對應工作完成後才會報告。
https://dev.to/saif_modan/tiddlywiki-x-cognee-a-design-proposal-for-searchable-project-notes-4cdo
![]()
精美的動畫預覽是很有用的靈感來源。要將它轉變成你自己的專案,你還需要提示詞、輸入內容,以及渲染設定。
我們的團隊維護了一個 Claude Opus 5.5 範例的視覺收藏。其中一個條目是由 @twoclipping 分享的循環 UI 動畫。所附的 GIF 是創作者成果的摘錄,並非我們團隊的重製。
這個提示詞一開始要求 8–12 個 UI 狀態、黑白調色盤或單一強調色,以及一段約 120 BPM 的免版稅音樂軌道。接著它描述了一個在這些狀態之間變形的連續形狀,而不是在不相關的畫面之間切換。
如果你要改編這個範例,請先決定那些輸入內容。一個按鈕、載入器、播放器、滑桿和圖表是好的起點;加入足夠的狀態以符合提示詞要求的範圍。然後閱讀完整的提示詞以了解製作限制,並在要求代理渲染之前提供其餘的輸入內容。
這就是我們希望這個函式庫能支援的瀏覽流程:檢查結果、閱讀公開的提示詞、確認設定,並加以改編。這個收藏橫跨七個類別,我們持續新增附有原始創作者連結的範例。
函式庫:https://github.com/LeaddeOpenLab/awesome-opus-5-5-video-prompts
原始範例與提示詞來源:https://x.com/twoclipping/status/2103273003555402193
我們並未獨立重製每一個條目。當你嘗試使用分享的創意程式設計提示詞時,通常會發現哪些設定資訊是缺失的?
披露:我協助維護這個收藏。本文是在 AI 協助下起草的。
![]()
OpenAI 的廣告收集器位於 bzr.openai.com,它設定了一個名為 __obi 的 cookie。此 cookie 的作用域限定在 .openai.com,其值是在你使用 ChatGPT 時寫入,並與你的 ChatGPT 帳戶綁定,之後會從你造訪的一般網站發送回 OpenAI。任何在 ChatGPT 上購買廣告的公司,都會在其自家網站安裝一段 OpenAI 程式碼,就像零售商早已執行 Meta 和 Google 的標籤一樣。當該程式碼載入時,__obi 就會連同你在該頁面上的行為一起傳送給 OpenAI:�