Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
### Mkdocs Site ###
site/
.venv-*/
/docs/plan-*-*.md

# Ignore codespaces / C# Dev Kit files
.mono
Expand Down Expand Up @@ -508,4 +509,5 @@ FodyWeavers.xsd
# Additional files built by Visual Studio
workload-install.ps1

# End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,rider
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,visualstudiocode,rider
.idea/
16 changes: 16 additions & 0 deletions docs/STACK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Project Stack

- Language: C# with nullable reference types enabled.
- Runtime/SDK target: .NET 10.0.
- Web framework: ASP.NET Core with OData controllers.
- Data providers: Entity Framework Core, Cosmos DB SDK, MongoDB, LiteDB, and in-memory repositories.
- API documentation: OpenAPI, Swashbuckle, and NSwag integration projects.
- Serialization: System.Text.Json with Datasync-specific converters.
- Tests: xUnit v3 with AwesomeAssertions and NSubstitute.
- Build shape: `Datasync.Toolkit.sln`, central package versions in `Directory.Packages.props`, shared project settings in `src/Directory.Build.props` and `tests/Directory.Build.props`.

## Considerations

- Samples require platform-specific SDKs and should be validated through the documented GitHub Actions sample workflow instead of local workload installation.
- Unit tests live under `tests/CommunityToolkit.Datasync.*.Test`.
- Generated outputs under `bin/` and `obj/` are ignored and should not be edited.
10 changes: 9 additions & 1 deletion docs/in-depth/server/db/cosmos-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ Azure Cosmos DB is a fully managed NoSQL database for high-performance applicati
builder.Services.AddSingleton<ICosmosTableOptions<TodoItem>>(new CosmosSharedTableOptions<TodoItem>("TodoDb", "TodoContainer"));
builder.Services.AddSingleton<ICosmosTableOptions<TodoList>>(new CosmosSharedTableOptions<TodoList>("TodoDb", "TodoContainer"));

If your entity uses custom CLR property names for Datasync metadata, pass the same `TableDataPropertyMap` that you configured for Datasync services:

builder.Services.AddSingleton<ICosmosTableOptions<TodoItem>>(services =>
{
IDatasyncServiceOptions options = services.GetRequiredService<IDatasyncServiceOptions>();
return new CosmosSharedTableOptions<TodoItem>("TodoDb", "TodoContainer", tableDataProperties: options.TableDataProperties);
});

5. Add the Cosmos repositories to the services collection within `Program.cs` with the following code:

builder.Services.AddSingleton(typeof(IRepository<>), typeof(CosmosTableRepository<>));
Expand Down Expand Up @@ -125,4 +133,4 @@ Azure Cosmos DB is supported in the `Microsoft.AspNetCore.Datasync.CosmosDb` NuG

* [Azure Cosmos DB .NET SDK](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/quickstart-dotnet) documentation.
* [Cosmos DB index policy](https://learn.microsoft.com/azure/cosmos-db/index-policy) documentation.
* [Cosmos DB single container reference sample](https://github.com/CommunityToolkit/Datasync/tree/main/samples/datasync-server-cosmosdb-singlecontainer)
* [Cosmos DB single container reference sample](https://github.com/CommunityToolkit/Datasync/tree/main/samples/datasync-server-cosmosdb-singlecontainer)
8 changes: 8 additions & 0 deletions docs/in-depth/server/db/in-memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ You can create an in-memory repository with no persistent storage by adding a si
IEnumerable<Model> seedData = GenerateSeedData();
builder.Services.AddSingleton<IRepository<Model>>(new InMemoryRepository<Model>(seedData));

If your entity uses custom CLR property names for Datasync metadata, pass the same `TableDataPropertyMap` that you configured for Datasync services:

builder.Services.AddSingleton<IRepository<Model>>(services =>
{
IDatasyncServiceOptions options = services.GetRequiredService<IDatasyncServiceOptions>();
return new InMemoryRepository<Model>(seedData, options.TableDataProperties);
});

Set up your table controller as follows:

[Route("tables/[controller]")]
Expand Down
7 changes: 7 additions & 0 deletions docs/in-depth/server/db/litedb.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ To use LiteDb with on-disk persistent storage:
}
}

If your entity uses custom CLR property names for Datasync metadata, pass the same `TableDataPropertyMap` that you configured for Datasync services:

public TodoItemController(LiteDatabase db, IDatasyncServiceOptions options) : base()
{
Repository = new LiteDbRepository<TodoItem>(db, "todoitems", options.TableDataProperties);
}

## Support and further information

For more information, review the [LiteDb documentation](https://www.litedb.org/docs/).
14 changes: 13 additions & 1 deletion docs/in-depth/server/db/mongodb.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Your entity should inherit from `MongoTableData`:
In your table controller:

[Route("tables/[controller]")]
public class MyEntityController : TableController<Entity>
public class MyEntityController : TableController<MyEntity>
{
public MyEntityController(MongoClient client)
{
Expand All @@ -30,6 +30,18 @@ In your table controller:
}
}

If your entity uses custom CLR property names for Datasync metadata, pass the same `TableDataPropertyMap` that you configured for Datasync services:

[Route("tables/[controller]")]
public class MyEntityController : TableController<MyEntity>
{
public MyEntityController(MongoClient client, IDatasyncServiceOptions options)
{
IMongoDatabase database = client.GetDatabase("mydatabase");
Repository = new MongoDBRepository<MyEntity>(database.GetCollection<MyEntity>("entities"), options.TableDataProperties);
}
}

## Known issues

The MongoDB implementation within Cosmos is split into vCore and RU format (also known as dedicated and serverless SKUs). Do not use the "serverless" or RU SKU. Ensure you are using Azure Cosmos DB for MongoDB (vCore). For more information, see [the Azure documentation](https://learn.microsoft.com/en-us/azure/cosmos-db/mongodb/overview).
Expand Down
49 changes: 49 additions & 0 deletions docs/in-depth/server/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,54 @@ The `ITableData` interfaces provides:

The Datasync libraries or the database maintains these properties. Do not modify these properties in your own code.

## Configure metadata property names

By default, Datasync uses the `ITableData` property names (`Id`, `UpdatedAt`, `Version`, and `Deleted`) when it reads or writes system metadata. If your server entity exposes that metadata through different CLR property names, configure a `TableDataPropertyMap`.

The mapped properties must be public read/write properties with the following types:

* `id`: `string`
* `updatedAt`: `DateTimeOffset` or `DateTimeOffset?`
* `version`: `byte[]`
* `deleted`: `bool`

The map uses CLR property names. With the default JSON options, those names are converted to camelCase in OpenAPI schema output.

builder.Services.AddDatasyncServices(options =>
{
options.TableDataProperties.Map(
id: "Key",
updatedAt: "ChangedOn",
version: "Token",
deleted: "Removed");
});

Repositories that are created manually must use the same map. The simplest option is to resolve `IDatasyncServiceOptions` when creating the repository:

builder.Services.AddScoped<IRepository<TodoItem>>(services =>
{
AppDbContext context = services.GetRequiredService<AppDbContext>();
IDatasyncServiceOptions options = services.GetRequiredService<IDatasyncServiceOptions>();
return new EntityTableRepository<TodoItem>(context, options.TableDataProperties);
});

You can override the metadata map for a single controller with `TableControllerOptions.TableDataProperties`:

[Route("tables/[controller]")]
public class TodoItemController : TableController<TodoItem>
{
public TodoItemController(AppDbContext context) : base()
{
TableDataPropertyMap tableDataProperties = new TableDataPropertyMap()
.Map(id: "Key", updatedAt: "ChangedOn", version: "Token", deleted: "Removed");

Repository = new EntityTableRepository<TodoItem>(context, tableDataProperties);
Options = new TableControllerOptions { TableDataProperties = tableDataProperties };
}
}

Use one metadata map consistently for the controller, repository, and OpenAPI generator. If they use different names, conditional requests, soft-delete, ordering, and generated schemas can refer to different properties.

## Update the DbContext

Each model in the database must be registered in the `DbContext`. For example:
Expand Down Expand Up @@ -131,6 +179,7 @@ The options you can set include:
* `MaxTop` (int, default: 512000) is the maximum number of items a user can request in a single operation.
* `EnableSoftDelete` (bool, default: false) enables soft-delete, which marks items as deleted instead of deleting them from the database. Soft delete allows clients to update their offline cache, but requires that deleted items are purged from the database separately.
* `UnauthorizedStatusCode` (int, default: 401 Unauthorized) is the status code returned when the user isn't allowed to do an action. The value must be a client error (4xx) status code in the range 400-499.
* `TableDataProperties` (`TableDataPropertyMap`, default: global Datasync service options) controls which CLR properties are used for Datasync metadata on this controller.
* `UnsafeEntityLogging` (bool, default: false) controls how much entity data is written to the logs. When `false`, only the entity ID is logged at `Information` level. When `true`, the entity ID is logged at `Information` level and the full (serialized) entity contents are logged at `Debug` level. Entity contents may include personally identifiable information (PII), secrets, or other sensitive business data, so only enable this option when the additional diagnostic detail is required and the log sink is appropriately secured.

## Configure access permissions
Expand Down
2 changes: 2 additions & 0 deletions docs/in-depth/server/openapi/net10.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Follow [the basic instructions for OpenApi integration](https://learn.microsoft.
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddOpenApi(options => options.AddDatasyncTransformers());

If you configured custom Datasync metadata property names with `AddDatasyncServices()`, the transformer uses those names when it marks system properties in generated schemas.

4. Enable the middleware for serving the generated JSON document and the Swagger UI, also in `Program.cs`:

app.MapOpenApi();
Expand Down
12 changes: 11 additions & 1 deletion docs/in-depth/server/openapi/nswag.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,17 @@ Follow [the basic instructions for NSwag integration](https://github.com/RicoSut

builder.Services.AddOpenApiDocument(options =>
{
options.AddDatasyncProcessors();
options.AddDatasyncProcessor();
});

If you configured custom Datasync metadata property names, pass a `TableDataPropertyMap` with the same property names to the processor so generated schemas use the same JSON property names:

TableDataPropertyMap tableDataProperties = new TableDataPropertyMap()
.Map(id: "Key", updatedAt: "ChangedOn", version: "Token", deleted: "Removed");

builder.Services.AddOpenApiDocument(options =>
{
options.AddDatasyncProcessor(tableDataProperties);
});

4. Enable the middleware for serving the generated JSON document and the Swagger UI, also in `Program.cs`:
Expand Down
12 changes: 11 additions & 1 deletion docs/in-depth/server/openapi/swashbuckle.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@ Follow the [basic instructions for Swashbuckle integration](https://learn.micros
options.AddDatasyncControllers();
});

If you configured custom Datasync metadata property names, pass a `TableDataPropertyMap` with the same property names to the document filter so generated schemas use the same JSON property names:

builder.Services.AddSwaggerGen(options =>
{
TableDataPropertyMap tableDataProperties = new TableDataPropertyMap()
.Map(id: "Key", updatedAt: "ChangedOn", version: "Token", deleted: "Removed");

options.AddDatasyncControllers(tableDataProperties);
});

!!! tip
The `AddDatasyncControllers()` method takes an optional `Assembly` that corresponds to the assembly that contains your table controllers. The `Assembly` parameter is only required if your table controllers are in a different project to the service.
`AddDatasyncControllers()` uses the calling assembly when it searches for table controllers. If your table controllers are in a different project, register `DatasyncDocumentFilter` directly and pass the controller assembly.

3. Enable the middleware for serving the generated JSON document and the Swagger UI, also in `Program.cs`:

Expand Down
Loading