### Install Datasync Server Template Source: https://github.com/communitytoolkit/datasync/blob/main/README.md Use this command to install the C# template for creating a new Datasync server project. ```dotnetcli dotnet new -i CommunityToolkit.Datasync.Server.Template.CSharp ``` -------------------------------- ### TodoItemsController Setup Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-3.md Defines a controller for TodoItems that inherits from TableController and initializes the TodoItemRepository. ```csharp [Route("tables/todoitems")] public class TodoItemsController : TableController { public TodoItemsController(AppDbContext context) : base() { Repository = new TodoItemRepository(context); } } ``` -------------------------------- ### Create New Datasync Server Project Source: https://github.com/communitytoolkit/datasync/blob/main/docs/index.md Creates a new Datasync server project named 'My.Datasync.Server' using the installed template. ```bash dotnet new datasync-server -n My.Datasync.Server ``` -------------------------------- ### Configure Service Collection for Authentication Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Register IPublicClientApplication as a singleton in the service collection. This setup is typically done in the application's startup code. ```csharp Services = new ServiceCollection() .AddSingleton((_) => CreateIdentityClient()) .AddSingleton() .AddTransient() .AddScoped() .AddScoped() .BuildServiceProvider(); ``` -------------------------------- ### Create New Datasync Server Project Source: https://github.com/communitytoolkit/datasync/blob/main/README.md These commands create a new directory, navigate into it, and generate a new Datasync server project using the installed template. ```dotnetcli mkdir My.Datasync.Server cd My.Datasync.Server dotnet new datasync-server ``` -------------------------------- ### Add Swashbuckle Package Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Install the Swashbuckle package to enable OpenAPI document generation for Datasync controllers. ```bash dotnet add package CommunityToolkit.Datasync.Server.Swashbuckle ``` -------------------------------- ### Add NuGet Packages for Datasync and EF Core Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Installs the required NuGet packages for using Entity Framework Core with SQL Server and the Community Toolkit.Datasync.Server. ```bash cd Sample.WebAPI dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools dotnet add package CommunityToolkit.Datasync.Server dotnet add package CommunityToolkit.Datasync.Server.EntityFrameworkCore ``` -------------------------------- ### Shared Asset File Naming Conventions Source: https://github.com/communitytoolkit/datasync/blob/main/samples/todoapp/TodoApp.Uno/TodoApp.Uno/Assets/SharedAssets.md Examples of how to name shared asset files to support different scales. Place images in the Assets directory and set their build action to Content. ```text \Assets\Images\logo.scale-100.png \Assets\Images\logo.scale-200.png \Assets\Images\logo.scale-400.png \Assets\Images\scale-100\logo.png \Assets\Images\scale-200\logo.png \Assets\Images\scale-400\logo.png ``` -------------------------------- ### Configure Datasync Client with Serilog Logging Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-3.md This example shows how to configure the Datasync client to use Serilog for HTTP request logging. Ensure Serilog is set up in your application before using this configuration. ```csharp using Serilog.HttpClient; HttpClientOptions options = new() { Endpoint = new Uri("https://myserver/"), HttpPipeline = [ new LoggingDelegatingHandler(new RequestLoggingOptions()), ] }; DatasyncServiceClient serviceClient = new(options); ``` -------------------------------- ### Initialize Datasync and Database Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Initializes both the database and the Datasync repository immediately after the web application is built. This ensures that all necessary data and configurations are in place before the application starts serving requests. ```csharp TimeSpan allowedInitializationTime = TimeSpan.FromMinutes(5); CancellationTokenSource cts = new(); using (AsyncServiceScope scope = app.Services.CreateAsyncScope()) { IDbInitializer dbInitializer = scope.ServiceProvider.GetRequiredService(); IDatasyncInitializer datasyncInitializer = scope.ServiceProvider.GetRequiredService(); cts.CancelAfter(allowedInitializationTime); try { CancellationToken ct = cts.Token; await dbInitializer.InitializeAsync(ct); await datasyncInitializer.InitializeAsync(ct); } catch (OperationCanceledException) { ``` -------------------------------- ### Initialize SQLite Database with Triggers Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/sqlite.md Ensure the SQLite database is created and update triggers are installed only if the database is newly created. This method should be called during application startup. ```csharp public void InitializeDatabase(DbContext context) { bool created = context.Database.EnsureCreated(); if (created && context.Database.IsSqlite()) { InstallUpdateTriggers(context); } context.Database.SaveChanges(); } ``` -------------------------------- ### TodoItem Model for EF Core Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-3.md An example of a typical web-based EF Core model. Note the auto-incrementing integer Id, lack of a Version property, and the presence of CreatedAt and UpdatedAt. ```csharp public class TodoItem { [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; public DateTimeOffset? UpdatedAt { get; set; } = DateTimeOffset.UtcNow; [Required, StringLength(128, MinimumLength=1)] public string Text { get; set; } = string.Empty; public bool IsComplete { get; set; } } ``` -------------------------------- ### Register AutoMapper with Services Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Add the AutoMapper setup to your application's service collection. Ensure the profile is discoverable by specifying its namespace or type. ```csharp builder.Services.AddAutoMapper(typeof(TodoItemProfile)); ``` -------------------------------- ### Configure DefaultConnection in appsettings.Development.json Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Specifies the connection string for the SQL Server database, using LocalDb in this example. This configuration is essential for the application to connect to its data store. ```json { "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\mssqllocaldb;Database=TodoApp;Trusted_Connection=True" } } ``` -------------------------------- ### Add OpenAPI Document Service Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/openapi/nswag.md Add this service to your Program.cs to enable OpenAPI document generation with Datasync processors. Ensure NSwag.AspNetCore and CommunityToolkit.Datasync.Server.NSwag packages are installed. ```csharp builder.Services.AddOpenApiDocument(options => { options.AddDatasyncProcessors(); }); ``` -------------------------------- ### AppDbContext Configuration for TodoItems Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-3.md Configures the DbContext for the TodoItem entity, including setting up a trigger for UpdatedAt and default values for CreatedAt. This is a standard EF Core 7+ setup. ```csharp public class AppDbContext(DbContextOptions options) : DbContext(options) { public DbSet TodoItems => Set(); protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity() .ToTable(tb => tb.HasTrigger("trg_TodoItems_UpdatedAt")); modelBuilder.Entity().Property(p => p.CreatedAt) .HasDefaultValueSql("getdate()"); modelBuilder.Entity().Property(p => p.UpdatedAt) .ValueGeneratedOnAddOrUpdate() .Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Save); } } ``` -------------------------------- ### Generated R Class for Android Resources Source: https://github.com/communitytoolkit/datasync/blob/main/samples/todoapp/TodoApp.Avalonia/TodoApp.Avalonia.Android/Resources/AboutResources.txt This is an example of the 'R' class generated by the build system, which contains tokens for each resource included in the application. Use these tokens to reference resources programmatically. ```csharp public class R { public class drawable { public const int icon = 0x123; } public class layout { public const int main = 0x456; } public class strings { public const int first_string = 0xabc; public const int second_string = 0xbcd; } } ``` -------------------------------- ### Create DataSync Sample Project with .NET CLI Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Use these commands to create a new solution, gitignore file, and a Web API project, then add the project to the solution. ```bash mkdir datasync-sample cd datasync-sample dotnet new sln -n datasync-sample dotnet new gitignore dotnet new webapi -n Sample.WebAPI dotnet sln ./datasync-sample.sln add ./Sample.WebAPI/Sample.WebAPI.csproj ``` -------------------------------- ### Configure HttpClient with HttpClientOptions Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/index.md Set up HttpClient using a collection of options, including endpoint, pipeline, and timeout. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { HttpClientOptions clientOptions = new() { Endpoint = new Uri("https://MYENDPOINT.azurewebsites.net"), HttpPipeline = [ new AuthenticatationDelegatingHandler(), new LoggingHandler(), new ApiKeyRequestHandler("X-API-Key", "my-api-key"), new CustomHttpClientHandler() ], Timeout = TimeSpan.FromSeconds(120) }; optionsBuilder.UseHttpClientOptions(clientOptions); } ``` -------------------------------- ### Install SQLite Update Triggers Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/sqlite.md Dynamically create and install SQL triggers for timestamp properties on SQLite tables to automatically update them on row modification. This function should be called once during database initialization. ```csharp internal static void InstallUpdateTriggers(DbContext context) { foreach (var table in context.Model.GetEntityTypes()) { var props = table.GetProperties().Where(prop => prop.ClrType == typeof(byte[]) && prop.ValueGenerated == ValueGenerated.OnAddOrUpdate); foreach (var property in props) { var sql = $"\ CREATE TRIGGER s_{table.GetTableName()}_{prop.Name}_UPDATE AFTER UPDATE ON {table.GetTableName()} BEGIN UPDATE {table.GetTableName()} SET {prop.Name} = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE rowid = NEW.rowid; END "; context.Database.ExecuteSqlRaw(sql); } } } ``` -------------------------------- ### Set up InMemoryRepository in Program.cs Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/in-memory.md Register the InMemoryRepository as a singleton service in Program.cs. This is useful for testing and static data scenarios. ```csharp IEnumerable seedData = GenerateSeedData(); builder.Services.AddSingleton>(new InMemoryRepository(seedData)); ``` -------------------------------- ### Install JsonSerializerContext in MauiProgram Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/advanced/maui-aot.md Set the TypeInfoResolver for DatasyncSerializer in your MauiProgram.cs file to use the default instance of your custom JsonSerializerContext. ```csharp DatasyncSerializer.JsonSerializerOptions.TypeInfoResolver = MySerializerContext.Default; ``` -------------------------------- ### Update DbContext for TodoItem Model Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/index.md Register your model entities in the DbContext. This example shows how to register the TodoItems DbSet for the AppDbContext. ```csharp public class AppDbContext(DbContextOptions options) : DbContext(options) { public DbSet TodoItems => Set(); } ``` -------------------------------- ### Implement Article Table Controller Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-4.md Sets up a basic TableController for the Article model, including repository and logger initialization. ```csharp public class ArticleController : TableController
{ public ArticleController(AppDbContext context, ILogger logger) : base() { Repository = new EntityRepository
(context); Logger = logger; } } ``` -------------------------------- ### Configure CosmosClient in Program.cs Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/cosmos-sdk.md Instantiate and configure `CosmosClient` with `SystemTextJsonSerializer` options, including custom converters and property naming policies. Add this client as a singleton to the services collection. ```csharp CosmosClient cosmosClient = new CosmosClient(connectionString, new CosmosClientOptions() { UseSystemTextJsonSerializerWithOptions = new() { Converters = { new JsonStringEnumConverter(), new DateTimeOffsetConverter(), new DateTimeConverter(), new TimeOnlyConverter(), new SpatialGeoJsonConverter() }, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull | JsonIgnoreCondition.WhenWritingDefault, NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, ReferenceHandler = ReferenceHandler.Preserve } }); ``` -------------------------------- ### Get Single Todo Item Asynchronously Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Retrieves a single TodoItem by its ID. The ReturnOrThrow extension method is used for concise error handling. ```csharp public async Task GetTodoItemAsync(string id, CancellationToken cancellationToken = default) => (await client.GetAsync(id, cancellationToken)).ReturnOrThrow(); ``` -------------------------------- ### Configure HttpClient with a Pre-configured HttpClient Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/index.md Integrate an existing HttpClient instance for Data Sync communication. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { optionsBuilder.UseHttpClient(Services.GetHttpClient()); } ``` -------------------------------- ### Configure Datasync Server - Program.cs Source: https://github.com/communitytoolkit/datasync/blob/main/docs/samples/todoapp/server.md Sets up the ASP.NET Core application with Datasync services and Entity Framework Core for Azure SQL. Ensure the 'DefaultConnection' is configured in appsettings.json. ```csharp using CommunityToolkit.Datasync.Server; using Microsoft.EntityFrameworkCore; using Sample.Datasync.Server.Db; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new ApplicationException("DefaultConnection is not set"); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDatasyncServices(); builder.Services.AddControllers(); WebApplication app = builder.Build(); // Initialize the database using (IServiceScope scope = app.Services.CreateScope()) { AppDbContext context = scope.ServiceProvider.GetRequiredService(); await context.InitializeDatabaseAsync(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); ``` -------------------------------- ### Implement TodoItemRepository Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-3.md Create a repository implementation for TodoItemDTO, handling data transfer between the database and DTOs. This includes methods for querying, creating, deleting, and replacing entities. ```csharp public class TodoItemRepository(AppDbContext dbContext) : IRepository { public static readonly Func ToDTO = (x, idx) => new TodoItemDTO { Id = x.MobileId, Deleted = x.Deleted, UpdatedAt = x.UpdatedAt, Version = [..x.Version], Text = x.Text, IsComplete = x.IsComplete }; public ValueTask> AsQueryableAsync(CancellationToken cancellationToken = default) => ValueTask.FromResult(dbContext.TodoItems.AsNoTracking().Select(ToDTO).AsQueryable()); public async ValueTask CreateAsync(TodoItemDTO entity, CancellationToken cancellationToken = default) { TodoItem dbEntity = new() { // Don't set properties that are set by the database. CreatedAt = DateTimeOffset.UtcNow, Text = entity.Text, IsComplete = entity.IsComplete, // MobileId is **NOT** set by the database. MobileId = string.IsNullOrEmpty(entity.Id) ? Guid.NewGuid().ToString("N") : entity.Id }; await WrapExceptionAsync(dbEntity.MobileId, async () => { if (dbContext.TodoItems.Any(x => x.MobileId == dbEntity.MobileId)) { throw new HttpException((int)HttpStatusCode.Conflict) { Payload = await GetEntityAsync(dbEntity.MobileId, cancellationToken).ConfigureAwait(false) }; } _ = dbContext.TodoItems.Add(dbEntity); _ = await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); CopyBack(dbEntity, entity); }, cancellationToken).ConfigureAwait(false); } public async ValueTask DeleteAsync(string id, byte[]? version = null, CancellationToken cancellationToken = default) { ThrowIfNullOrEmpty(id); await WrapExceptionAsync(id, async () => { TodoItem storedEntity = await dbContext.TodoItems.SingleOrDefaultAsync(x => x.MobileId == id, cancellationToken).ConfigureAwait(false) ?? throw new HttpException((int)HttpStatusCode.NotFound); if (version?.Length > 0 && !storedEntity.Version.SequenceEqual(version)) { throw new HttpException((int)HttpStatusCode.PreconditionFailed) { Payload = await GetEntityAsync(id, cancellationToken).ConfigureAwait(false) }; } _ = dbContext.TodoItems.Remove(storedEntity); _ = await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); }, cancellationToken).ConfigureAwait(false); } public async ValueTask ReadAsync(string id, CancellationToken cancellationToken = default) { ThrowIfNullOrEmpty(id); TodoItem storedEntity = await dbContext.TodoItems.SingleOrDefaultAsync(x => x.MobileId == id, cancellationToken).ConfigureAwait(false) ?? throw new HttpException((int)HttpStatusCode.NotFound); return ToDTO(storedEntity, 0); } public async ValueTask ReplaceAsync(TodoItemDTO entity, byte[]? version = null, CancellationToken cancellationToken = default) { ThrowIfNullOrEmpty(entity.Id); await WrapExceptionAsync(entity.Id, async () => { ``` -------------------------------- ### Create HttpClient with GenericAuthenticationProvider Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Demonstrates how to create an HttpClient instance with a GenericAuthenticationProvider for use with non-datasync APIs, leveraging the same authentication mechanism. ```csharp var clientFactory = new HttpClientFactory(clientOptions); var httpClient = clientFactory.CreateClient(); ``` -------------------------------- ### Example Event Data Structure Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-5.md This JSON structure represents an event that can be sent over the communication channel. It includes operation type, entity name, and the entity details. ```json { "operation": "ADD", "entityName": "TodoItem", "entity": { "id": "1234", "createdAt": "2024-12-24T03:00:00.123Z", "updatedAt": "2024-12-24T03:00:00.123Z", "version": "AAAAAAAB=", "title": "Santa Claus is coming!", "completed": false } } ``` -------------------------------- ### Configure OpenAPI in Program.cs Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Configure services and middleware in Program.cs to enable Swagger UI and Datasync controller integration. ```csharp // Where you set up your services: builder.Services .AddEndpointsApiExplorer() .AddSwaggerGen(options => options.AddDatasyncControllers()); // Right after the .UseHttpsRedirection() call app.UseSwagger().UseSwaggerUI(); ``` -------------------------------- ### Create MSAL PublicClientApplication Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Builds and returns an IPublicClientApplication instance using MSAL. Ensure Constants.ApplicationId is correctly set. ```csharp public IPublicClientApplication CreateIdentityClient() { var client = PublicClientApplicationBuilder.Create(Constants.ApplicationId) .WithAuthority(AzureCloudInstance.AzurePublic, "common") .WithRedirectUri("http://localhost") .WithWindowsEmbeddedBrowserSupport() .Build(); return client; } ``` -------------------------------- ### Example of Allowed JSON Entity for MongoDB Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Demonstrates a flat JSON entity structure that is compatible with the Datasync Community Toolkit when using MongoDB. Nested objects are not permitted. ```json { "name": "Joe Smith", "address": { "line1": "1 Main Street", "city": "New York City", "state": "NY" } } ``` -------------------------------- ### Create MongoDB Client Factory Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Instantiate a MongoClient with connection settings. Use a singleton pattern for efficient connection management. ```csharp public class MongoClientFactory { private readonly MongoClient client; public MongoClientFactory(string connectionString) { MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(connectionString); this.client = new MongoClient(clientSettings); } public MongoClient Client { get => this.client; } } ``` -------------------------------- ### Configure AutoMapper Profile Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Define a mapping profile for AutoMapper to transform between your database model (TodoItem) and its DTO (TodoItemDTO). This example sets a default value for UpdatedAt if it's null. ```csharp using AutoMapper; namespace InMemoryDatasyncService.Models; public class TodoItemProfile : Profile { public TodoItemProfile() { CreateMap() .ForMember(dest => dest.UpdatedAt, opt => opt.NullSubstitute(DateTimeOffset.UnixEpoch)) .ReverseMap(); } } ``` -------------------------------- ### Configuring HttpClientFactory with Datasync Options Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Set up an HttpClientFactory with detailed HttpClientOptions, including endpoint, HTTP pipeline handlers, timeout, and user agent. This configuration is added to the service collection. ```csharp HttpClientOptions options = new() { Endpoint = new Uri("https://myserver/"), HttpPipeline = [ new LoggingHandler(), new AuthenticationHandler() ], Timeout = TimeSpan.FromSeconds(120), UserAgent = "Enterprise/Datasync-myserver-service" }; Services = new ServiceCollection() .AddSingleton(options) .AddSingleton . /* the rest of your services here */ .BuildServiceProvider(); ``` -------------------------------- ### Implementing Data Paging with Skip and Take Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/online.md Use .Skip() and .Take() methods to implement data paging, retrieving a specific subset of data for each page. ```csharp var pageOfItems = await remoteTable.Skip(100).Take(10).ToListAsync(); ``` -------------------------------- ### Get All Entities with Query Options Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Retrieves a paginated list of entities. Supports OData query parameters for filtering, selecting properties, sorting, skipping, and including deleted entities. ```APIDOC ## GET /tables/items ### Description Retrieves the first page of items from the repository. This endpoint supports OData query parameters for flexible data retrieval, including filtering, selecting specific properties, server-side sorting, pagination, and including deleted entities. ### Method GET ### Endpoint /tables/items ### Parameters #### Query Parameters - **$filter** (string) - Optional - Specifies an OData filter expression to filter the results. - **$select** (string) - Optional - Specifies a comma-separated list of properties to return. - **$orderby** (string) - Optional - Specifies properties for server-side sorting (e.g., `propertyName asc`, `propertyName desc`). - **$skip** (integer) - Optional - Specifies the number of matching entities to skip from the beginning. - **$top** (integer) - Optional - Specifies the maximum number of matching entities to return after the `$skip` value. - **__includedeleted** (boolean) - Optional - If true, includes entities marked as deleted in the response. ### Response #### Success Response (200) - **items** (array) - An array of entity objects matching the query. #### Response Example ```json { "items": [ { "id": "entity-guid-1", "propertyName": "value1", "updatedAt": "timestamp1", "version": "version-string1" }, { "id": "entity-guid-2", "propertyName": "value2", "updatedAt": "timestamp2", "version": "version-string2" } ] } ``` ``` -------------------------------- ### Create MongoClient Instance Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/mongodb.md Instantiate a MongoClient using a connection string. It is recommended to wrap this in a pooled or singleton service to prevent connection exhaustion. ```csharp MongoClientSettings clientSettings = MongoClientSettings.FromConnectionString(connectionString); MongoClient client = new MongoClient(clientSettings); ``` -------------------------------- ### Basic DatasyncServiceClient Initialization Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Instantiate a DatasyncServiceClient with HttpClientOptions, setting the endpoint. This is typically done within a service constructor. ```csharp public OnlineTodoService() { var clientOptions = new HttpClientOptions() { Endpoint = new Uri(baseUrl) }; client = new DatasyncServiceClient(clientOptions); } ``` -------------------------------- ### Include Raw Assets with MauiAsset Source: https://github.com/communitytoolkit/datasync/blob/main/samples/todoapp/TodoApp.MAUI/Resources/Raw/AboutAssets.txt Configure your `.csproj` file to include raw assets from the Resources\Raw directory and its subdirectories. The `LogicalName` ensures assets are deployed with their original directory structure. ```xml ``` -------------------------------- ### Custom Conflict Resolver for Movies Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/index.md Example of a typed conflict resolver that preserves the server's movie title while allowing other client-side updates. It returns the modified client object as the resolved entity. ```csharp public class CustomConflictResolver : IConflictResolver { public async Task ResolverConflictAsync(Movie? clientObject, Movie? serverObject, CancellationToken cancellationToken = default) { clientObject.Movie = serverObject.Movie; return new ConflictResolution { Result = ConflictResolutionResult.Client, Entity = clientObject }; } } ``` -------------------------------- ### Get Entity by ID Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Retrieves a JSON representation of a specific entity by its ID. Supports including deleted entities via a query parameter. Returns a 404 if not found or 410 if soft-deleted and not explicitly included. ```APIDOC ## GET /tables/items/{id} ### Description Retrieves a JSON representation of a specific entity by its unique identifier. This returns the same representation as POST or PUT operations. If soft-delete is enabled and the entity is marked as deleted, a `410 Gone` response is returned unless `__includedeleted=true` is used. ### Method GET ### Endpoint /tables/items/{id} ### Parameters #### Path Parameters - **id** (string) - Required - The unique identifier of the entity. #### Query Parameters - **__includedeleted** (boolean) - Optional - If true, includes entities marked as deleted in the response. ### Response #### Success Response (200) - **entity** (object) - The JSON representation of the entity. #### Error Response (404) - Not Found: If the entity does not exist in the database. #### Error Response (410) - Gone: If the entity is marked as deleted and `__includedeleted` is not true. #### Response Example ```json { "id": "entity-guid", "propertyName": "value", "updatedAt": "timestamp", "version": "version-string" } ``` ``` -------------------------------- ### Configure Datasync HttpClient Options Source: https://github.com/communitytoolkit/datasync/blob/main/docs/samples/todoapp/avalonia.md Implement the OnDatasyncInitialization method to configure the HttpClient options for datasync operations. This includes setting the service endpoint and adding a logging handler to the HTTP pipeline. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { HttpClientOptions clientOptions = new() { Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"), HttpPipeline = [new LoggingHandler()] }; _ = optionsBuilder.UseHttpClientOptions(clientOptions); } ``` -------------------------------- ### Define a TodoItem Entity for PostgreSQL Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/index.md Define a model entity class that implements ITableData. This example uses EntityTableData for PostgreSQL. Ensure your entity has a string-based primary key, an automatically updated timestamp, and an automatically updated version. ```csharp public class TodoItem : EntityTableData { public required string Title { get; set; } public bool IsComplete { get; set; } } ``` -------------------------------- ### Set up TableController for InMemoryRepository Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/in-memory.md Configure a TableController to use the IRepository. Ensure the controller inherits from TableController and injects the repository in its constructor. ```csharp [Route("tables/[controller]")] public class ModelController : TableController { public MovieController(IRepository repository) : base(repository) { } } ``` -------------------------------- ### Implement Table Controller for MongoDB Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/mongodb.md Set up a TableController for your entity, injecting a MongoClient and initializing the MongoDBRepository with the appropriate database and collection. ```csharp [Route("tables/[controller]")] public class MyEntityController : TableController { public MyEntityController(MongoClient client) { IMongoDatabase database = client.GetDatabase("mydatabase"); Repository = new MongoDBRepository(database.GetCollection("entities")); } } ``` -------------------------------- ### Add JWT Bearer and Microsoft Identity Web to Services Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Install the Microsoft.Identity.Web NuGet package and add the necessary using statements to your Program.cs file. This configures the server to handle JWT Bearer authentication and integrate with Microsoft Entra ID. ```csharp using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; ``` ```csharp builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration); ``` -------------------------------- ### Add LiteDatabase Singleton Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/litedb.md Configure the LiteDatabase as a singleton service in Program.cs. Ensure the connection string is retrieved from configuration. ```csharp const connectionString = builder.Configuration.GetValue("LiteDb:ConnectionString"); builder.Services.AddSingleton(new LiteDatabase(connectionString)); ``` -------------------------------- ### Configure Datasync Services and DbContext in WebApplication Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Wires up the Entity Framework Core DbContext and Datasync services into the WebApplication's service collection. It also configures the application to use HTTPS and authorization. ```csharp using CommunityToolkit.Datasync.Server; using Microsoft.EntityFrameworkCore; using Sample.WebAPI; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new ApplicationException("DefaultConnection is not set"); builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); builder.Services.AddDatasyncServices(); builder.Services.AddControllers(); WebApplication app = builder.Build(); // TODO: Initialize the database app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run(); ``` -------------------------------- ### Create GenericAuthenticationProvider Instance Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/auth.md Instantiate the GenericAuthenticationProvider by passing your token retrieval method. This provider will handle token requests when needed. ```csharp GenericAuthenticationProvider authProvider = new(GetTokenAsync); ``` -------------------------------- ### Configure HttpClientOptions for DataSync Source: https://github.com/communitytoolkit/datasync/blob/main/docs/samples/todoapp/wpf.md Implement the OnDatasyncInitialization method to configure the HttpClientOptions, including the DataSync service endpoint and an optional logging handler. Ensure the Endpoint URI is correctly set to your deployed DataSync service. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { HttpClientOptions clientOptions = new() { Endpoint = new Uri("https://YOURSITEHERE.azurewebsites.net/"), HttpPipeline = [new LoggingHandler()] }; _ = optionsBuilder.UseHttpClientOptions(clientOptions); } ``` -------------------------------- ### Create HttpClientFactory with Custom Pipeline Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/online.md Configure an HttpClientFactory with a custom HTTP pipeline, including authentication, logging, and API key handlers. The CustomHttpClientHandler should be the last element if used. ```csharp using CommunityToolkit.Datasync.Client.Http; public IHttpClientFactory GetClientFactory() { HttpClientOptions options = new() { Endpoint = new Uri("https://MYENDPOINT.azureversions.net"), HttpPipeline = [ new AuthenticatationDelegatingHandler(), new LoggingHandler(), new ApiKeyRequestHandler("X-API-Key", "my-api-key"), new CustomHttpClientHandler() ], Timeout = TimeSpan.FromSeconds(120) }; HttpClientFactory factory = new(options); } ``` -------------------------------- ### Expansive TodoItem Table Controller with Options Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md An expanded version of the TodoItem controller demonstrating various configuration options like soft delete, max top, page size, and unauthorized status code. ```csharp using CommunityToolkit.Datasync.Server; using CommunityToolkit.Datasync.Server.EntityFrameworkCore; using Microsoft.AspNetCore.Mvc; using Sample.WebAPI; namespace Sample.WebAPI.Controllers; [Route("tables/[controller]")] public class TodoItemController : TableController { public TodoItemController(AppDbContext context, ILogger logger) : base() : base(new EntityTableRepository(context)) { Repository = new EntityTableRepository(context); Logger = logger; Options = new TableControllerOptions { EnableSoftDelete = false, MaxTop = 128000, PageSize = 100, UnauthorizedStatusCode = 401 }; } } ``` -------------------------------- ### Create AppDbContext for Entity Framework Core Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-1.md Sets up the application's DbContext for Entity Framework Core, defining a DbSet for the TodoItem entity. This context is used to interact with the SQL Server database. ```csharp using Microsoft.EntityFrameworkCore; namespace Sample.WebAPI; public class AppDbContext(DbContextOptions options) : DbContext(options) { public DbSet TodoItems => Set(); } ``` -------------------------------- ### Register In-Memory Repository and Initializer Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/server/part-2.md Configures the application services to use a singleton instance of the in-memory repository and a scoped instance of the Datasync initializer. Use a singleton for the in-memory repository to ensure data persistence across requests. ```csharp builder.Services.AddSingleton, InMemoryRepository>(); builder.Services.AddScoped(); ``` -------------------------------- ### Configure Datasync Client with API Key and Authentication Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-3.md This client configuration demonstrates adding a logging handler, an API key handler for Azure API Management, and a generic authentication provider to the HTTP pipeline. The order is important to prevent sensitive information like authorization tokens from being logged. ```csharp string apiKey = GetApiKeyFromConfiguration(); HttpClientOptions options = new() { Endpoint = new Uri("https://myserver/"), HttpPipeline = [ new LoggingHandler(), new AzureApiManagementSubscriptionKey(apiKey), new GenericAuthenticationProvider(GetAuthenticationTokenAsync) ] }; DatasyncServiceClient serviceClient = new(options); ``` -------------------------------- ### Add Entity with Options Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Demonstrates adding an entity with custom service options. The ServiceResponse object provides detailed HTTP response information. ```csharp ServiceResponse response = await client.AddAsync(clientSideItem, options); ``` -------------------------------- ### Create LiteDbRepository Controller Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/litedb.md Implement a controller that inherits from TableController and uses LiteDbRepository for data operations. The repository is initialized with the LiteDatabase instance and a collection name. ```csharp [Route("tables/[controller]")] public class TodoItemController : TableController { public TodoItemController(LiteDatabase db) : base() { Repository = new LiteDbRepository(db, "todoitems"); } } ``` -------------------------------- ### Create Default HttpClientFactory Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/online.md Instantiate a default HttpClientFactory with basic endpoint configuration. Ensure the Endpoint is set to your Datasync service URI. ```csharp using CommunityToolkit.Datasync.Client.Http; public IHttpClientFactory GetClientFactory() { HttpClientOptions options = new() { Endpoint = new Uri("https://MYENDPOINT.azureversions.net") }; HttpClientFactory factory = new(options); } ``` -------------------------------- ### Configure Datasync Client with Authentication Provider Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Initializes the Datasync client with HttpClientOptions, including a GenericAuthenticationProvider that uses the GetAuthenticationToken method to provide authentication tokens for requests. ```csharp public OnlineTodoService(IPublicClientApplication identityClient) { var clientOptions = new HttpClientOptions() { Endpoint = new Uri(Constants.ServiceUri), HttpPipeline = [ new GenericAuthenticationProvider(GetAuthenticationToken) ] }; client = new(clientOptions); IdentityClient = identityClient; } ``` -------------------------------- ### Register OnlineTodoService with Dependency Injection Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Wire in the OnlineTodoService implementation to your application's dependency injection container. This is typically done in the App.xaml.cs file. ```csharp Services = new ServiceCollection() .AddSingleton() .AddTransient() .AddScoped() .AddScoped() .BuildServiceProvider(); ``` -------------------------------- ### Configure HTTP Pipeline for Authentication and Authorization Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-2.md Add UseAuthentication() and UseAuthorization() to the HTTP pipeline in Program.cs. This ensures that authentication middleware is executed before authorization checks. ```csharp var app = builder.Build(); app.UseHttpsRedirection(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run(); ``` -------------------------------- ### Add Datasync Repositories and Services Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/cosmos-sdk.md Register the generic `CosmosTableRepository` and the Datasync services with the dependency injection container. ```csharp builder.Services.AddSingleton(typeof(IRepository<>), typeof(CosmosTableRepository<>)); builder.Services.AddDatasyncServices(); ``` -------------------------------- ### Configure OpenAPI Driver - appsettings.json Source: https://github.com/communitytoolkit/datasync/blob/main/docs/samples/todoapp/server.md Sets the OpenAPI driver for the Datasync server. The value can be 'nswag' or 'swashbuckle' based on preference. ```json "Swagger": { "Driver": "nswag" } ``` -------------------------------- ### Implement OnlineTodoService Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Implement the ITodoService interface for online operations. Initialize the DatsyncServiceClient with the base URL of your service. This class is intended to be used with the CommunityToolkit.Datasync.Client NuGet package. ```csharp public class OnlineTodoService : ITodoService { private const string baseUrl = ""; private readonly DatsyncServiceClient client; public OnlineTodoService() { // Initialize your client here. } public async Task AddTodoItemAsync(string title, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public async Task> GetAllTodoItemsAsync(CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public async Task GetTodoItemAsync(string id, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } public async Task ReplaceTodoItemAsync(TodoItem updatedItem, CancellationToken cancellationToken = default) { throw new NotImplementedException(); } } ``` -------------------------------- ### DatasyncServiceClient with Relative Uri and Shared HttpClient Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Initialize a DatasyncServiceClient using a relative Uri and a shared HttpClient. The HttpClient's BaseAddress must include the server root and have a trailing slash for correct URI combination. ```csharp HttpClient httpClient = new HttpClient() { BaseAddress = new Uri("https://myserver/") }; Uri relativeUri = new Uri("/tables/todoitem", UriKind.Relative); var client = new DatasyncServiceClient(relativeUri, httpClient); ``` -------------------------------- ### Configure HttpClient with an Endpoint Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/index.md Use this method to specify the base URI for the Data Sync service. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { optionsBuilder.UseEndpoint(new Uri("https://YOURSITEHERE.azurewebsites.net/")); } ``` -------------------------------- ### DatasyncServiceClient with Uri and HttpClient Source: https://github.com/communitytoolkit/datasync/blob/main/docs/tutorial/client/part-1.md Create a DatasyncServiceClient by providing a table controller Uri and an existing HttpClient instance. Ensure the HttpClient's BaseAddress is correctly set if using relative URIs. ```csharp Uri tableControllerUri = new Uri("https://myserver/tables/todoitem"); var client = new DatasyncServiceClient(tableControllerUri, new HttpClient()); ``` -------------------------------- ### Enable Swagger Middleware for Development Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/openapi/swashbuckle.md Enable the Swagger middleware in `Program.cs` for serving the generated JSON document and Swagger UI when the application is in a development environment. This allows browsing the API documentation at the root of the web service. ```csharp if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1"); options.RoutePrefix = string.Empty; }); } ``` -------------------------------- ### Configure HttpClient with an IHttpClientFactory Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/client/index.md Utilize an IHttpClientFactory, which can be shared with online access configurations. ```csharp protected override void OnDatasyncInitialization(DatasyncOfflineOptionsBuilder optionsBuilder) { optionsBuilder.UseHttpClientFactory(Services.GetHttpClientFactory()); } ``` -------------------------------- ### Create Datasync Entity Controller Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/db/cosmos-sdk.md Create a controller for each datasync entity that inherits from `TableController` and injects the appropriate `IRepository`. ```csharp [Route("tables/[controller]")] public class TodoItemController : TableController { public TodoItemController(IRepository repository) : base(repository) { } } ``` -------------------------------- ### Enable OpenAPI Middleware Source: https://github.com/communitytoolkit/datasync/blob/main/docs/in-depth/server/openapi/nswag.md Enable the OpenAPI middleware in your Program.cs for development environments. This serves the generated JSON document and the Swagger UI at the /swagger endpoint. ```csharp if (app.Environment.IsDevelopment()) { app.UseOpenApi(); app.UseSwaggerUI3(); } ```