C# 参考 v1.0

C#客户端库

supabase在 GitHub 上查看

本参考文档记录了 Supabase 的 C# 库中可用的每个对象和方法,supabase。您可以使用 Supabase 与您的 Postgres 数据库交互,监听数据库更改,调用 Deno Edge 函数,构建登录和用户管理功能,以及管理大型文件。


安装

从 NuGet 安装#

您可以从 nuget.org 安装 Supabase 包

1
dotnet add package supabase

初始化

初始化新的客户端非常简单。从管理面板中找到您的项目 URL 和公共密钥,并将其传递给您的客户端初始化函数。

Supabase 严重依赖从 BaseModel 派生的模型。要与 API 交互,必须指定关联的模型(参见示例)。

利用 TablePrimaryKeyColumn 属性来指定与 C# 版本不同的类/属性名称。

1
var url = Environment.GetEnvironmentVariable("SUPABASE_URL");
2
var key = Environment.GetEnvironmentVariable("SUPABASE_KEY");
3
4
var options = new Supabase.SupabaseOptions
5
{
6
AutoConnectRealtime = true
7
};
8
9
var supabase = new Supabase.Client(url, key, options);
10
await supabase.InitializeAsync();

获取数据

执行使用 SELECT 的垂直过滤。

  • LINQ 表达式当前不支持解析嵌入式资源列。在这种情况下,需要使用 string
  • 在使用字符串列名选择时,它们必须与数据库中的名称匹配,而不是模型属性上指定的名称。
  • 有关建模 + 查询联接和内联接的更多信息,请参见 postgrest-csharp README
  • 默认情况下,Supabase 项目将返回最多 1,000 行。 可以在项目 API 设置中更改此设置。 建议将其保持较低,以限制意外或恶意请求的有效负载大小。 您可以使用 range() 查询来分页您的数据。
  • From() 可以与 修饰符 结合使用
  • From() 可以与 过滤器 结合使用
  • 如果使用 Supabase 托管平台,apikey 在技术上是一个保留关键字,因为 API 网关会将其提取出来用于身份验证。 应避免将其用作列名
1
// Given the following Model (City.cs)
2
[Table("cities")]
3
class City : BaseModel
4
{
5
[PrimaryKey("id")]
6
public int Id { get; set; }
7
8
[Column("name")]
9
public string Name { get; set; }
10
11
[Column("country_id")]
12
public int CountryId { get; set; }
13
14
//... etc.
15
}
16
17
// A result can be fetched like so.
18
var result = await supabase.From<City>().Get();
19
var cities = result.Models

插入数据

执行表中的 INSERT。

1
[Table("cities")]
2
class City : BaseModel
3
{
4
[PrimaryKey("id", false)]
5
public int Id { get; set; }
6
7
[Column("name")]
8
public string Name { get; set; }
9
10
[Column("country_id")]
11
public int CountryId { get; set; }
12
}
13
14
var model = new City
15
{
16
Name = "The Shire",
17
CountryId = 554
18
};
19
20
await supabase.From<City>().Insert(model);

更新数据

执行表中的 UPDATE。

  • Update() 通常使用模型作为参数或从已填充的模型调用。
1
var update = await supabase
2
.From<City>()
3
.Where(x => x.Name == "Auckland")
4
.Set(x => x.Name, "Middle Earth")
5
.Update();

更新或插入数据

执行表中的 UPSERT。

  • 为了使更新正常工作,数据负载中应包含主键。
  • 主键必须是自然主键,而不是代理主键。但是,存在代理主键的解决方法。
1
var model = new City
2
{
3
Id = 554,
4
Name = "Middle Earth"
5
};
6
7
await supabase.From<City>().Upsert(model);

删除数据

执行表中的 DELETE。

  • Delete() 应该始终与 过滤器 结合使用,以定位您想要删除的项目。
1
await supabase
2
.From<City>()
3
.Where(x => x.Id == 342)
4
.Delete();

调用 Postgres 函数

您可以将存储过程作为“远程过程调用”来调用。

这是一种花哨的说法,即您可以将一些逻辑放入数据库中,然后从任何地方调用它。当逻辑很少更改时,这尤其有用——例如密码重置和更新。

1
await supabase.Rpc("hello_world", null);

使用过滤器

过滤器允许你仅返回匹配特定条件的行。

过滤器可用于 Select()Update()Delete() 查询。

注意:LINQ 表达式当前不支持解析嵌入式资源列。在这种情况下,需要使用 string

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Where(x => x.Name == "The Shire")
4
.Single();

列等于一个值

查找指定 column 上的值与指定的 value 完全匹配的所有行。

1
var result = await supabase.From<City>()
2
.Where(x => x.Name == "Bali")
3
.Get();

列不等于一个值

查找所有 column 的值与指定的 value 不匹配的行。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Where(x => x.Name != "Bali")
4
.Get();

列大于一个值

查找所有 column 的值大于指定的 value 的行。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Where(x => x.CountryId > 250)
4
.Get();

列大于或等于一个值

查找所有 column 的值大于或等于指定的 value 的行。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Where(x => x.CountryId >= 250)
4
.Get();

列小于一个值

查找所有 column 的值小于指定的 value 的行。

1
var result = await supabase.From<City>()
2
.Select("name, country_id")
3
.Where(x => x.CountryId < 250)
4
.Get();

列小于或等于一个值

查找所有 column 的值小于或等于指定的 value 的行。

1
var result = await supabase.From<City>()
2
.Where(x => x.CountryId <= 250)
3
.Get();

列匹配一个模式

查找所有 column 的值与提供的 pattern 匹配的行(区分大小写)。

1
var result = await supabase.From<City>()
2
.Filter(x => x.Name, Operator.Like, "%la%")
3
.Get();

列匹配一个不区分大小写的模式

查找所有 column 的值与提供的 pattern 匹配的行(不区分大小写)。

1
await supabase.From<City>()
2
.Filter(x => x.Name, Operator.ILike, "%la%")
3
.Get();

列是一个值

用于精确相等性(null、true、false)的检查,查找所有 column 的值与指定的 value 完全匹配的行。

1
var result = await supabase.From<City>()
2
.Where(x => x.Name == null)
3
.Get();

列在一个数组中

查找所有 column 的值在指定的 values 中找到的行。

1
var result = await supabase.From<City>()
2
.Filter(x => x.Name, Operator.In, new List<object> { "Rio de Janiero", "San Francisco" })
3
.Get();

列包含数组中的每个元素

1
var result = await supabase.From<City>()
2
.Filter(x => x.MainExports, Operator.Contains, new List<object> { "oil", "fish" })
3
.Get();

包含在值中

1
var result = await supabase.From<City>()
2
.Filter(x => x.MainExports, Operator.ContainedIn, new List<object> { "oil", "fish" })
3
.Get();

匹配一个字符串

查找指定 column 上的 tsvector 值与 to_tsquery(query) 匹配的所有行。


匹配关联值

  • 查找给定类别的模型(在填充模型和与数据库关联时很有用)
  • 查找其列与指定的 Dictionary<string, string> 对象匹配的所有行。
1
var city = new City
2
{
3
Id = 224,
4
Name = "Atlanta"
5
};
6
7
var model = supabase.From<City>().Match(city).Single();

不匹配过滤器

查找不满足过滤器的所有行。

1
var result = await supabase.From<Country>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Where(x => x.Name != "Paris")
4
.Get();

匹配至少一个过滤器

查找满足至少一个过滤器的所有行。

1
var result = await supabase.From<Country>()
2
.Where(x => x.Id == 20 || x.Id == 30)
3
.Get();

使用修饰符

过滤器在行级别起作用——它们允许您仅返回匹配特定条件的行,而不会更改行的形状。修饰符是所有不符合该定义的内容——允许您更改响应的格式(例如,设置限制或偏移量)。


对结果进行排序

使用指定的列对结果进行排序。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Order(x => x.Id, Ordering.Descending)
4
.Get();

限制返回的行数

使用指定的计数限制结果。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Limit(10)
4
.Get();

将查询限制在一个范围内

将结果限制在指定的范围内,包括边界。

1
var result = await supabase.From<City>()
2
.Select("name, country_id")
3
.Range(0, 3)
4
.Get();

检索一行数据

从结果中检索一行。结果必须是一行(例如,使用限制),否则将导致错误。

1
var result = await supabase.From<City>()
2
.Select(x => new object[] { x.Name, x.CountryId })
3
.Single();

创建新用户

创建一个新用户。

  • 默认情况下,用户需要在登录前验证其电子邮件地址。要关闭此功能,请在 您的项目 中禁用 **确认电子邮件**。
  • **确认电子邮件** 确定用户在注册后是否需要确认其电子邮件地址。
    • 如果启用了 **确认电子邮件**,则会返回一个 `user`,但 `session` 为 null。
    • 如果禁用了 **确认电子邮件**,则会同时返回 `user` 和 `session`。
  • 当用户确认其电子邮件地址时,默认情况下他们将被重定向到 SITE_URL。您可以在 您的项目中修改 SITE_URL 或添加其他重定向 URL。
  • 如果调用了已确认的现有用户的 SignUp()
    • 当在 您的项目 中同时启用 **确认电子邮件** 和 **确认电话**(即使电话提供程序已禁用)时,将返回一个混淆/虚假的用户对象。
    • 当 **确认电子邮件** 或 **确认电话**(即使电话提供程序已禁用)中的任何一个被禁用时,将返回错误消息 `User already registered`。
1
var session = await supabase.Auth.SignUp(email, password);

监听认证事件

每当发生身份验证事件时,都会收到通知。

  • 身份验证事件类型:AuthState.SignedInAuthState.SignedOutAuthState.UserUpdatedAuthState.PasswordRecoveryAuthState.TokenRefreshed
1
supabase.Auth.AddStateChangedListener((sender, changed) =>
2
{
3
switch (changed)
4
{
5
case AuthState.SignedIn:
6
break;
7
case AuthState.SignedOut:
8
break;
9
case AuthState.UserUpdated:
10
break;
11
case AuthState.PasswordRecovery:
12
break;
13
case AuthState.TokenRefreshed:
14
break;
15
}
16
});

登录用户

使用电子邮件或电话号码和密码登录现有用户。

  • 需要电子邮件和密码或电话号码和密码。
1
var session = await supabase.Auth.SignIn(email, password);

通过 OTP 登录用户

  • 需要电子邮件或电话号码。
  • 此方法用于无密码登录,其中 OTP 会发送到用户的电子邮件或电话号码。
  • 如果您使用的是电子邮件,您可以配置是否希望用户接收 magiclink 或 OTP。
  • 如果您使用的是电话,您可以配置是否希望用户接收 OTP。
  • magic link 的目标 URL 由 SITE_URL 确定。您可以在 您的项目中修改 SITE_URL 或添加其他重定向 URL。
1
var options = new SignInOptions { RedirectTo = "http://myredirect.example" };
2
var didSendMagicLink = await supabase.Auth.SendMagicLink("joseph@supabase.io", options);

通过 OAuth 登录用户

使用第三方 OAuth 提供商登录用户。

  • 此方法用于使用第三方提供程序登录。
  • Supabase 支持许多不同的 第三方提供程序
1
var signInUrl = supabase.Auth.SignIn(Provider.Github);

注销用户

注销当前用户(如果有已登录用户)。

  • 为了使用 SignOut() 方法,用户需要先登录。
1
await supabase.Auth.SignOut();

通过 OTP 验证并登录

  • VerifyOtp 方法接受不同类型的验证。如果使用电话号码,则类型可以是 smsphone_change。如果使用电子邮件地址,则类型可以是以下之一:signupmagiclinkrecoveryinviteemail_change
  • 应根据在调用 VerifyOtp 注册/登录用户之前调用的相应身份验证方法来确定使用的验证类型。
1
var session = await supabase.Auth.VerifyOTP("+13334445555", TOKEN, MobileOtpType.SMS);

获取会话

如果存在活动会话,则返回会话数据。

1
var session = supabase.Auth.CurrentSession;

获取用户

如果存在已登录用户,则返回用户数据。

1
var user = supabase.Auth.CurrentUser;

更新用户

更新用户数据(如果有已登录用户)。

  • 为了使用 UpdateUser() 方法,用户需要先登录。
  • 默认情况下,电子邮件更新会向用户的当前电子邮件和新电子邮件发送确认链接。要仅向用户的新的电子邮件发送确认链接,请在您的项目的 电子邮件身份验证提供程序设置中禁用 安全电子邮件更改
1
var attrs = new UserAttributes { Email = "new-email@example.com" };
2
var response = await supabase.Auth.Update(attrs);

调用 Supabase 边缘函数。

调用 Supabase 函数。请参阅 指南 以了解有关编写函数的详细信息。

  • 需要 Authorization 标头。
  • Invoke 参数通常与 Fetch API 规范相匹配。
1
var options = new InvokeFunctionOptions
2
{
3
Headers = new Dictionary<string, string> {{ "Authorization", "Bearer 1234" }},
4
Body = new Dictionary<string, object> { { "foo", "bar" } }
5
};
6
7
await supabase.Functions.Invoke("hello", options: options);

订阅频道

订阅数据库中的实时更改。

  • 实时默认情况下为新项目禁用,以提高数据库性能和安全性。您可以通过 管理复制 来启用它。
  • 如果您想接收更新和删除的“先前”数据,则需要将 REPLICA IDENTITY 设置为 FULL,如下所示:ALTER TABLE your_table REPLICA IDENTITY FULL;
1
class CursorBroadcast : BaseBroadcast
2
{
3
[JsonProperty("cursorX")]
4
public int CursorX {get; set;}
5
6
[JsonProperty("cursorY")]
7
public int CursorY {get; set;}
8
}
9
10
var channel = supabase.Realtime.Channel("any");
11
var broadcast = channel.Register<CursorBroadcast>();
12
broadcast.AddBroadcastEventHandler((sender, baseBroadcast) =>
13
{
14
var response = broadcast.Current();
15
});
16
17
await channel.Subscribe();
18
19
// Send a broadcast
20
await broadcast.Send("cursor", new CursorBroadcast { CursorX = 123, CursorY = 456 });

取消订阅频道

取消订阅并从实时客户端中删除实时通道。

  • 删除频道是维护项目实时服务性能以及数据库性能的好方法(如果你正在侦听 Postgres 更改)。Supabase 会在客户端断开连接 30 秒后自动处理清理,但未使用的频道可能会导致更多客户端同时订阅而导致性能下降。
1
var channel = await supabase.From<City>().On(ChannelEventType.All, (sender, change) => { });
2
channel.Unsubscribe();
3
4
// OR
5
6
var channel = supabase.Realtime.Channel("realtime", "public", "*");
7
channel.Unsubscribe()

获取所有频道

返回所有实时通道。

1
var channels = supabase.Realtime.Subscriptions;

文件存储

本节包含处理文件存储桶的方法。


列出所有存储桶

检索现有产品中的所有存储桶的详细信息。

  • 策略权限要求
    • buckets 权限:select
    • objects 权限:无
1
var buckets = await supabase.Storage.ListBuckets();

检索存储桶

检索现有的存储存储桶的详细信息。

  • 策略权限要求
    • buckets 权限:select
    • objects 权限:无
1
var bucket = await supabase.Storage.GetBucket("avatars");

创建存储桶

创建一个新的存储存储桶

  • 策略权限要求
    • buckets 权限:insert
    • objects 权限:无
1
var bucket = await supabase.Storage.CreateBucket("avatars");

清空存储桶

删除单个存储桶中的所有对象。

  • 策略权限要求
    • buckets 权限:select
    • objects 权限:selectdelete
1
var bucket = await supabase.Storage.EmptyBucket("avatars");

更新存储桶

更新新的存储桶

  • 策略权限要求
    • buckets 权限:update
    • objects 权限:无
1
var bucket = await supabase.Storage.UpdateBucket("avatars", new BucketUpsertOptions { Public = false });

删除存储桶

删除现有的存储桶。如果存储桶内存在现有对象,则无法删除存储桶。你必须首先 empty() 存储桶。

  • 策略权限要求
    • buckets 权限:selectdelete
    • objects 权限:无
1
var result = await supabase.Storage.DeleteBucket("avatars");

上传文件

将文件上传到现有的存储桶。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:insert
1
var imagePath = Path.Combine("Assets", "fancy-avatar.png");
2
3
await supabase.Storage
4
.From("avatars")
5
.Upload(imagePath, "fancy-avatar.png", new FileOptions { CacheControl = "3600", Upsert = false });

替换现有文件

用新文件替换指定路径处的现有文件。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:updateselect
1
var imagePath = Path.Combine("Assets", "fancy-avatar.png");
2
await supabase.Storage.From("avatars").Update(imagePath, "fancy-avatar.png");

移动现有文件

移动现有文件,同时可以选择重命名它。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:updateselect
1
await supabase.Storage.From("avatars")
2
.Move("public/fancy-avatar.png", "private/fancy-avatar.png");

创建签名 URL

创建签名 URL 以在无需权限的情况下下载文件。此 URL 在设定的秒数内有效。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:select
1
var url = await supabase.Storage.From("avatars").CreateSignedUrl("public/fancy-avatar.png", 60);

检索公共 URL

检索公共存储桶中的资产 URL

  • 存储桶需要设置为公共存储桶,可以通过 UpdateBucket() 或通过转到 supabase.com/dashboard、单击存储桶上的溢出菜单并选择“设为公共存储桶”来实现。
  • 策略权限要求
    • buckets 权限:无
    • objects 权限:无
1
var publicUrl = supabase.Storage.From("avatars").GetPublicUrl("public/fancy-avatar.png");

下载文件

下载文件。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:select
1
var bytes = await supabase.Storage.From("avatars").Download("public/fancy-avatar.png");

删除存储桶中的文件

删除同一存储桶中的文件

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:deleteselect
1
await supabase.Storage.From("avatars").Remove(new List<string> { "public/fancy-avatar.png" });

列出存储桶中的所有文件

列出存储桶中的所有文件。

  • 策略权限要求
    • buckets 权限:无
    • objects 权限:select
1
var objects = await supabase.Storage.From("avatars").List();

发行说明

1.0.0 - 2024-04-21#

  • 程序集名称已更改为 Supabase.dll
  • 更新依赖项:postgrest-csharp@5.0.0
    • [MAJOR] 将命名空间从 Postgrest 移动到 Supabase.Postgrest
    • 回复:#135 更新 nuget 包名称 postgrest-csharpSupabase.Postgrest
  • 更新依赖项:gotrue-csharp@5.0.0
    • 回复:#135 更新 nuget 包名称 gotrue-csharpSupabase.Gotrue
    • 回复:#89,仅当明确声明时才将 access_token 添加到请求正文中。
    • [MINOR] 回复:#89 更新 SignInWithIdToken 的签名,添加可选的 accessToken 参数,更新文档注释,并在方法中调用 DestroySession
    • 回复:#88,添加 IsAnonymous 属性到 User
    • 回复:#90 实现 LinkIdentityUnlinkIdentity
  • 更新依赖项:realtime-csharp@7.0.0
    • 合并 #45 - 更新 Websocket.Client@5.1.1
    • 回复:#135 更新 nuget 包名称 realtime-csharpSupabase.Realtime
  • 更新依赖项:storage-csharp@2.0.0
    • 回复:#135 更新 nuget 包名称 storage-csharpSupabase.Storage
  • 更新依赖项:functions-csharp@2.0.0
    • 回复:#135 更新 nuget 包名称 functions-csharpSupabase.Functions
  • 更新依赖项:core-csharp@1.0.0
    • 回复:#135 更新 nuget 包名称 supabase-coreSupabase.Core
  • 添加对剩余未记录代码的注释。