|
1 | 1 | ---
|
2 |
| -title: ".NET (closed alpha)" |
| 2 | +title: ".NET (alpha)" |
3 | 3 | description: "SDK reference for using PowerSync in .NET clients."
|
4 | 4 | sidebarTitle: Overview
|
5 | 5 | ---
|
6 | 6 |
|
7 |
| -<Note> |
8 |
| - Our .NET client SDK is currently in a closed alpha release for early testing with select customers. You can explore the repo [here](https://github.com/powersync-ja/powersync-dotnet), however it is not yet officially published so expect breaking changes and instability as development continues. |
9 |
| -</Note> |
| 7 | +<CardGroup> |
| 8 | + <Card title="PowerSync SDK on NuGet" icon="nuget" href="https://www.nuget.org/packages/PowerSync.Common/"> |
| 9 | + This SDK is distributed via NuGet [\[External link\].](https://www.nuget.org/packages/PowerSync.Common/) |
| 10 | + </Card> |
| 11 | + |
| 12 | + <Card title="Source Code" icon="github" href="https://github.com/powersync-ja/powersync-dotnet"> |
| 13 | + Refer to the powersync-dotnet repo on GitHub. |
| 14 | + </Card> |
| 15 | + |
| 16 | + <Card title="API Reference (Coming soon)" icon="book"> |
| 17 | + A full API Reference for this SDK is not yet available. This is planned for a future release. |
| 18 | + </Card> |
| 19 | + |
| 20 | + <Card title="Example App" icon="code" href="https://github.com/powersync-ja/powersync-dotnet/tree/main/demos/CommandLine"> |
| 21 | + A small CLI app showcasing bidirectional sync. |
| 22 | + </Card> |
| 23 | +</CardGroup> |
| 24 | + |
| 25 | +## Supported Frameworks and Targets |
| 26 | + |
| 27 | +The PowerSync .NET SDK supports: |
| 28 | + |
| 29 | +* **.NET Versions**: 6, 8, and 9 |
| 30 | +* **.NET Framework**: Version 4.8 (requires additional configuration) |
| 31 | + |
| 32 | +**Current Limitations**: |
| 33 | +* The SDK in its current state is designed for desktop, server, and traditional binary applications. |
| 34 | +* Web and mobile platforms (Blazor, MAUI) are not yet supported, but are planned. |
| 35 | + |
| 36 | +For more details, please refer to the package [Readme](https://github.com/powersync-ja/powersync-dotnet/tree/main?tab=readme-ov-file#supported-frameworks). |
| 37 | + |
| 38 | +## SDK Features |
| 39 | + |
| 40 | +* Provides real-time streaming of database changes. |
| 41 | +* Offers direct access to the SQLite database, enabling the use of SQL on both client and server sides. |
| 42 | +* Enables subscription to queries for receiving live updates. |
| 43 | +* Eliminates the need for client-side database migrations as these are managed automatically. |
| 44 | + |
| 45 | +## Quickstart |
| 46 | + |
| 47 | +To start using PowerSync in a .NET client, install the package: |
| 48 | + |
| 49 | +```bash |
| 50 | +dotnet add package PowerSync.Common --prerelease |
| 51 | +``` |
| 52 | + |
| 53 | +<Info> |
| 54 | + Add `--prerelease` while this package is in alpha. |
| 55 | +</Info> |
| 56 | + |
| 57 | +Next, make sure that you have: |
| 58 | + |
| 59 | +* Signed up for a PowerSync Cloud account ([here](https://accounts.journeyapps.com/portal/powersync-signup?s=docs)) or [self-host PowerSync](/self-hosting/getting-started). |
| 60 | +* [Configured your backend database](/installation/database-setup) and connected it to your PowerSync instance. |
| 61 | + |
| 62 | +### 1. Define the schema |
| 63 | + |
| 64 | +The first step is defining the schema for the local SQLite database. |
| 65 | + |
| 66 | +This schema represents a "view" of the downloaded data. No migrations are required — the schema is applied directly when the local PowerSync database is constructed (as we'll show in the next step). |
| 67 | +You can use [this example](https://github.com/powersync-ja/powersync-dotnet/blob/main/demos/CommandLine/AppSchema.cs) as a reference when defining your schema. |
| 68 | + |
| 69 | +### 2. Instantiate the PowerSync Database |
| 70 | + |
| 71 | +Next, you need to instantiate the PowerSync database — this is the core managed database. |
| 72 | + |
| 73 | +Its primary functions are to record all changes in the local database, whether online or offline. In addition, it automatically uploads changes to your app backend when connected. |
| 74 | + |
| 75 | +**Example**: |
| 76 | + |
| 77 | +```cs |
| 78 | +using PowerSync.Common.Client; |
| 79 | + |
| 80 | +class Demo |
| 81 | +{ |
| 82 | + static async Task Main() |
| 83 | + { |
| 84 | + var db = new PowerSyncDatabase(new PowerSyncDatabaseOptions |
| 85 | + { |
| 86 | + Database = new SQLOpenOptions { DbFilename = "tododemo.db" }, |
| 87 | + Schema = AppSchema.PowerSyncSchema, |
| 88 | + }); |
| 89 | + await db.Init(); |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +### 3. Integrate with your Backend |
| 95 | + |
| 96 | +The PowerSync backend connector provides the connection between your application backend and the PowerSync client-side managed SQLite database. |
| 97 | + |
| 98 | +It is used to: |
| 99 | + |
| 100 | +1. Retrieve an auth token to connect to the PowerSync instance. |
| 101 | +2. Apply local changes on your backend application server (and from there, to Postgres) |
| 102 | + |
| 103 | +Accordingly, the connector must implement two methods: |
| 104 | + |
| 105 | +1. [PowerSyncBackendConnector.FetchCredentials](https://github.com/powersync-ja/powersync-dotnet/blob/main/demos/CommandLine/NodeConnector.cs#L50) - This is called every couple of minutes and is used to obtain credentials for your app backend API. -> See [Authentication Setup](/installation/authentication-setup) for instructions on how the credentials should be generated. |
| 106 | +2. [PowerSyncBackendConnector.UploadData](https://github.com/powersync-ja/powersync-dotnet/blob/main/demos/CommandLine/NodeConnector.cs#L72) - Use this to upload client-side changes to your app backend. |
| 107 | + -> See [Writing Client Changes](/installation/app-backend-setup/writing-client-changes) for considerations on the app backend implementation. |
| 108 | + |
| 109 | +**Example**: |
| 110 | + |
| 111 | +```cs |
| 112 | +using System; |
| 113 | +using System.Collections.Generic; |
| 114 | +using System.Net.Http; |
| 115 | +using System.Text; |
| 116 | +using System.Text.Json; |
| 117 | +using System.Threading.Tasks; |
| 118 | +using PowerSync.Common.Client; |
| 119 | +using PowerSync.Common.Client.Connection; |
| 120 | +using PowerSync.Common.DB.Crud; |
| 121 | + |
| 122 | +public class MyConnector : IPowerSyncBackendConnector |
| 123 | +{ |
| 124 | + private readonly HttpClient _httpClient; |
| 125 | + |
| 126 | + // User credentials for the current session |
| 127 | + public string UserId { get; private set; } |
| 128 | + |
| 129 | + // Service endpoints |
| 130 | + private readonly string _backendUrl; |
| 131 | + private readonly string _powerSyncUrl; |
| 132 | + private string? _clientId; |
| 133 | + |
| 134 | + public MyConnector() |
| 135 | + { |
| 136 | + _httpClient = new HttpClient(); |
| 137 | + |
| 138 | + // In a real app, this would come from your authentication system |
| 139 | + UserId = "user-123"; |
| 140 | + |
| 141 | + // Configure your service endpoints |
| 142 | + _backendUrl = "https://your-backend-api.example.com"; |
| 143 | + _powerSyncUrl = "https://your-powersync-instance.powersync.journeyapps.com"; |
| 144 | + } |
| 145 | + |
| 146 | + public async Task<PowerSyncCredentials?> FetchCredentials() |
| 147 | + { |
| 148 | + try { |
| 149 | + // Obtain a JWT from your authentication service. |
| 150 | + // See https://docs.powersync.com/installation/authentication-setup |
| 151 | + // If you're using Supabase or Firebase, you can re-use the JWT from those clients, see |
| 152 | + // - https://docs.powersync.com/installation/authentication-setup/supabase-auth |
| 153 | + // - https://docs.powersync.com/installation/authentication-setup/firebase-auth |
| 154 | +
|
| 155 | + var authToken = "your-auth-token"; // Use a development token (see Authentication Setup https://docs.powersync.com/installation/authentication-setup/development-tokens) to get up and running quickly |
| 156 | +
|
| 157 | + // Return credentials with PowerSync endpoint and JWT token |
| 158 | + return new PowerSyncCredentials(_powerSyncUrl, authToken); |
| 159 | + |
| 160 | + } |
| 161 | + catch (Exception ex) |
| 162 | + { |
| 163 | + Console.WriteLine($"Error fetching credentials: {ex.Message}"); |
| 164 | + throw; |
| 165 | + } |
| 166 | + } |
| 167 | + |
| 168 | + public async Task UploadData(IPowerSyncDatabase database) |
| 169 | + { |
| 170 | + // Get the next transaction to upload |
| 171 | + CrudTransaction? transaction; |
| 172 | + try |
| 173 | + { |
| 174 | + transaction = await database.GetNextCrudTransaction(); |
| 175 | + } |
| 176 | + catch (Exception ex) |
| 177 | + { |
| 178 | + Console.WriteLine($"UploadData Error: {ex.Message}"); |
| 179 | + return; |
| 180 | + } |
| 181 | + |
| 182 | + // If there's no transaction, there's nothing to upload |
| 183 | + if (transaction == null) |
| 184 | + { |
| 185 | + return; |
| 186 | + } |
| 187 | + |
| 188 | + // Get client ID if not already retrieved |
| 189 | + _clientId ??= await database.GetClientId(); |
| 190 | + |
| 191 | + try |
| 192 | + { |
| 193 | + // Convert PowerSync operations to your backend format |
| 194 | + var batch = new List<object>(); |
| 195 | + foreach (var operation in transaction.Crud) |
| 196 | + { |
| 197 | + batch.Add(new |
| 198 | + { |
| 199 | + op = operation.Op.ToString(), // INSERT, UPDATE, DELETE |
| 200 | + table = operation.Table, |
| 201 | + id = operation.Id, |
| 202 | + data = operation.OpData |
| 203 | + }); |
| 204 | + } |
| 205 | + |
| 206 | + // Send the operations to your backend |
| 207 | + var payload = JsonSerializer.Serialize(new { batch }); |
| 208 | + var content = new StringContent(payload, Encoding.UTF8, "application/json"); |
| 209 | + |
| 210 | + HttpResponseMessage response = await _httpClient.PostAsync($"{_backendUrl}/api/data", content); |
| 211 | + response.EnsureSuccessStatusCode(); |
| 212 | + |
| 213 | + // Mark the transaction as completed |
| 214 | + await transaction.Complete(); |
| 215 | + } |
| 216 | + catch (Exception ex) |
| 217 | + { |
| 218 | + Console.WriteLine($"UploadData Error: {ex.Message}"); |
| 219 | + throw; |
| 220 | + } |
| 221 | + } |
| 222 | +} |
| 223 | +``` |
| 224 | + |
| 225 | +With your database instantiated and your connector ready, call `connect` to start the synchronization process: |
| 226 | + |
| 227 | +```cs |
| 228 | +await db.Connect(new MyConnector()); |
| 229 | +await db.WaitForFirstSync(); // Optional, to wait for a complete snapshot of data to be available |
| 230 | +``` |
| 231 | + |
| 232 | +## Usage |
| 233 | + |
| 234 | +After connecting the client database, it is ready to be used. You can run queries and make updates as follows: |
| 235 | + |
| 236 | +```cs |
| 237 | +// Use db.Get() to fetch a single row: |
| 238 | +Console.WriteLine(await db.Get<object>("SELECT powersync_rs_version();")); |
| 239 | + |
| 240 | +// Or db.GetAll() to fetch all: |
| 241 | +// Where List result is defined: |
| 242 | +// record ListResult(string id, string name, string owner_id, string created_at); |
| 243 | +Console.WriteLine(await db.GetAll<ListResult>("SELECT * FROM lists;")); |
| 244 | + |
| 245 | +// Use db.Watch() to watch queries for changes (await is used to wait for initialization): |
| 246 | +await db.Watch("select * from lists", null, new WatchHandler<ListResult> |
| 247 | +{ |
| 248 | + OnResult = (results) => |
| 249 | + { |
| 250 | + Console.WriteLine("Results: "); |
| 251 | + foreach (var result in results) |
| 252 | + { |
| 253 | + Console.WriteLine(result.id + ":" + result.name); |
| 254 | + } |
| 255 | + }, |
| 256 | + OnError = (error) => |
| 257 | + { |
| 258 | + Console.WriteLine("Error: " + error.Message); |
| 259 | + } |
| 260 | +}); |
| 261 | + |
| 262 | +// And db.Execute for inserts, updates and deletes: |
| 263 | +await db.Execute( |
| 264 | + "insert into lists (id, name, owner_id, created_at) values (uuid(), 'New User', ?, datetime())", |
| 265 | + [connector.UserId] |
| 266 | +); |
| 267 | +``` |
| 268 | + |
| 269 | +## Logging |
| 270 | + |
| 271 | +Enable logging to help you debug your app. By default, the SDK uses a no-op logger that doesn't output any logs. To enable logging, you can configure a custom logger using .NET's `ILogger` interface: |
| 272 | + |
| 273 | +```cs |
| 274 | +using Microsoft.Extensions.Logging; |
| 275 | +using PowerSync.Common.Client; |
| 276 | + |
| 277 | +// Create a logger factory |
| 278 | +ILoggerFactory loggerFactory = LoggerFactory.Create(builder => |
| 279 | +{ |
| 280 | + builder.AddConsole(); // Enable console logging |
| 281 | + builder.SetMinimumLevel(LogLevel.Information); // Set minimum log level |
| 282 | +}); |
| 283 | + |
| 284 | +var logger = loggerFactory.CreateLogger("PowerSyncLogger"); |
| 285 | + |
| 286 | +var db = new PowerSyncDatabase(new PowerSyncDatabaseOptions |
| 287 | +{ |
| 288 | + Database = new SQLOpenOptions { DbFilename = "powersync.db" }, |
| 289 | + Schema = AppSchema.PowerSyncSchema, |
| 290 | + Logger = logger |
| 291 | +}); |
| 292 | +``` |
0 commit comments