Skip to content

Commit 9f00591

Browse files
Add Cosmos DB integration and repository implementation
Introduced new projects and configurations to support Cosmos DB integration in a .NET web application. This includes: - Adding new project references and updating solution configurations. - Setting up necessary services, controllers, models, and configuration files. - Implementing a Cosmos DB repository with options, extensions, and tests. - Adding license information to several files. - Creating a test project to ensure repository functionality and correctness.
1 parent 4dc3bb8 commit 9f00591

24 files changed

+1044
-0
lines changed

Datasync.Toolkit.sln

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{75F7
6464
EndProject
6565
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Datasync.Server", "samples\datasync-server\src\Sample.Datasync.Server\Sample.Datasync.Server.csproj", "{A9967817-2A2C-4C6D-A133-967A6062E9B3}"
6666
EndProject
67+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Datasync.Server.CosmosDb", "src\CommunityToolkit.Datasync.Server.CosmosDb\CommunityToolkit.Datasync.Server.CosmosDb.csproj", "{D9356867-0A30-4B17-BD4C-0F7EF70984C6}"
68+
EndProject
69+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.Datasync.Server.SingleContainer", "samples\datasync-server\datasync-server-cosmosdb\Sample.Datasync.Server.SingleContainer\Sample.Datasync.Server.SingleContainer.csproj", "{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE}"
70+
EndProject
6771
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Datasync.Server.MongoDB", "src\CommunityToolkit.Datasync.Server.MongoDB\CommunityToolkit.Datasync.Server.MongoDB.csproj", "{DC20ACF9-12E9-41D9-B672-CB5FD85548E9}"
6872
EndProject
6973
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Datasync.Server.MongoDB.Test", "tests\CommunityToolkit.Datasync.Server.MongoDB.Test\CommunityToolkit.Datasync.Server.MongoDB.Test.csproj", "{4FC45D20-0BA9-484B-9040-641687659AF6}"
7074
EndProject
75+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommunityToolkit.Datasync.Server.CosmosDb.Test", "tests\CommunityToolkit.Datasync.Server.CosmosDb.Test\CommunityToolkit.Datasync.Server.CosmosDb.Test.csproj", "{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2}"
76+
EndProject
7177
Global
7278
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7379
Debug|Any CPU = Debug|Any CPU
@@ -158,6 +164,18 @@ Global
158164
{A9967817-2A2C-4C6D-A133-967A6062E9B3}.Debug|Any CPU.Build.0 = Debug|Any CPU
159165
{A9967817-2A2C-4C6D-A133-967A6062E9B3}.Release|Any CPU.ActiveCfg = Release|Any CPU
160166
{A9967817-2A2C-4C6D-A133-967A6062E9B3}.Release|Any CPU.Build.0 = Release|Any CPU
167+
{D9356867-0A30-4B17-BD4C-0F7EF70984C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
168+
{D9356867-0A30-4B17-BD4C-0F7EF70984C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
169+
{D9356867-0A30-4B17-BD4C-0F7EF70984C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
170+
{D9356867-0A30-4B17-BD4C-0F7EF70984C6}.Release|Any CPU.Build.0 = Release|Any CPU
171+
{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
172+
{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
173+
{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
174+
{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE}.Release|Any CPU.Build.0 = Release|Any CPU
175+
{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
176+
{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
177+
{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
178+
{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2}.Release|Any CPU.Build.0 = Release|Any CPU
161179
{DC20ACF9-12E9-41D9-B672-CB5FD85548E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
162180
{DC20ACF9-12E9-41D9-B672-CB5FD85548E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
163181
{DC20ACF9-12E9-41D9-B672-CB5FD85548E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -192,6 +210,9 @@ Global
192210
{D3B72031-D4BD-44D3-973C-2752AB1570F6} = {84AD662A-4B9E-4E64-834D-72529FB7FCE5}
193211
{2889E6B2-9CD1-437C-A43C-98CFAFF68B99} = {D59F1489-5D74-4F52-B78B-88037EAB2838}
194212
{A9967817-2A2C-4C6D-A133-967A6062E9B3} = {75F709FD-8CC2-4558-A802-FE57086167C2}
213+
{D9356867-0A30-4B17-BD4C-0F7EF70984C6} = {84AD662A-4B9E-4E64-834D-72529FB7FCE5}
214+
{60C73E92-9A45-4EE6-8DCF-48206CD0E5FE} = {75F709FD-8CC2-4558-A802-FE57086167C2}
215+
{FC15CF34-2C1A-4B15-85CC-A99EA25C47D2} = {D59F1489-5D74-4F52-B78B-88037EAB2838}
195216
{DC20ACF9-12E9-41D9-B672-CB5FD85548E9} = {84AD662A-4B9E-4E64-834D-72529FB7FCE5}
196217
{4FC45D20-0BA9-484B-9040-641687659AF6} = {D59F1489-5D74-4F52-B78B-88037EAB2838}
197218
EndGlobalSection

Directory.Packages.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@
1111
<PackageVersion Include="LiteDB" Version="5.0.21" />
1212
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.1" />
1313
<PackageVersion Include="Microsoft.AspNetCore.OData" Version="9.1.3" />
14+
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.46.1" />
1415
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
1516
<PackageVersion Include="Microsoft.EntityFrameworkCore.Cosmos" Version="9.0.1" />
1617
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.1" />
1718
<PackageVersion Include="Microsoft.EntityFrameworkCore.Proxies" Version="9.0.1" />
1819
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
1920
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
2021
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="9.0.1" />
22+
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
2123
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.1" />
2224
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.1" />
2325
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
2426
<PackageVersion Include="Microsoft.OData.Core" Version="8.2.3" />
2527
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
2628
<PackageVersion Include="Microsoft.Spatial" Version="8.2.3" />
29+
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
2730
<PackageVersion Include="MongoDB.Driver" Version="3.1.0" />
2831
<PackageVersion Include="NSubstitute" Version="5.3.0" />
2932
<PackageVersion Include="NSwag.AspNetCore" Version="14.2.0" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server;
6+
using Microsoft.AspNetCore.Mvc;
7+
using Sample.Datasync.Server.SingleContainer.Models;
8+
9+
namespace Sample.Datasync.Server.Controllers;
10+
11+
[Route("tables/[controller]")]
12+
public class TodoItemController : TableController<TodoItem>
13+
{
14+
public TodoItemController(IRepository<TodoItem> repository)
15+
: base(repository)
16+
{
17+
}
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server;
6+
using Microsoft.AspNetCore.Mvc;
7+
using Sample.Datasync.Server.SingleContainer.Models;
8+
9+
namespace Sample.Datasync.Server.Controllers;
10+
11+
[Route("tables/[controller]")]
12+
public class TodoListController : TableController<TodoList>
13+
{
14+
public TodoListController(IRepository<TodoList> repository) : base(repository)
15+
{
16+
Options = new TableControllerOptions { EnableSoftDelete = true };
17+
}
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.CosmosDb;
6+
using System.ComponentModel.DataAnnotations;
7+
8+
namespace Sample.Datasync.Server.SingleContainer.Models;
9+
10+
public class TodoItem : CosmosTableData<TodoItem>
11+
{
12+
[Required, MinLength(1)]
13+
public string Title { get; set; } = string.Empty;
14+
15+
public bool IsComplete { get; set; }
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server.CosmosDb;
6+
using System.ComponentModel.DataAnnotations;
7+
8+
namespace Sample.Datasync.Server.SingleContainer.Models;
9+
10+
public class TodoList : CosmosTableData<TodoList>
11+
{
12+
[Required, MinLength(1)]
13+
public string Title { get; set; } = string.Empty;
14+
15+
public string? ListId { get; set; }
16+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using CommunityToolkit.Datasync.Server;
6+
using CommunityToolkit.Datasync.Server.Abstractions.Json;
7+
using CommunityToolkit.Datasync.Server.CosmosDb;
8+
using CommunityToolkit.Datasync.Server.Swashbuckle;
9+
using Microsoft.Azure.Cosmos;
10+
using Sample.Datasync.Server.SingleContainer.Models;
11+
using System.Collections.ObjectModel;
12+
using System.Text.Json;
13+
using System.Text.Json.Serialization;
14+
15+
var builder = WebApplication.CreateBuilder(args);
16+
17+
string connectionString = builder.Configuration.GetConnectionString("DefaultConnection")
18+
?? throw new ApplicationException("DefaultConnection is not set");
19+
20+
CosmosClient cosmosClient = new CosmosClient(connectionString,
21+
new CosmosClientOptions()
22+
{
23+
UseSystemTextJsonSerializerWithOptions = new()
24+
{
25+
Converters =
26+
{
27+
new JsonStringEnumConverter(),
28+
new DateTimeOffsetConverter(),
29+
new DateTimeConverter(),
30+
new TimeOnlyConverter(),
31+
new SpatialGeoJsonConverter()
32+
},
33+
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
34+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull | JsonIgnoreCondition.WhenWritingDefault,
35+
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
36+
ReferenceHandler = ReferenceHandler.Preserve
37+
}
38+
});
39+
40+
builder.Services.AddSingleton(cosmosClient);
41+
builder.Services.AddSingleton<ICosmosTableOptions<TodoItem>>(new CosmosSharedTableOptions<TodoItem>("TodoDb", "TodoContainer"));
42+
builder.Services.AddSingleton<ICosmosTableOptions<TodoList>>(new CosmosSharedTableOptions<TodoList>("TodoDb", "TodoContainer"));
43+
builder.Services.AddSingleton(typeof(IRepository<>), typeof(CosmosTableRepository<>));
44+
// Add services to the container.
45+
46+
builder.Services.AddDatasyncServices();
47+
48+
builder.Services.AddControllers();
49+
50+
_ = builder.Services
51+
.AddEndpointsApiExplorer()
52+
.AddSwaggerGen(options => options.AddDatasyncControllers());
53+
54+
var app = builder.Build();
55+
56+
// Configure the HTTP request pipeline.
57+
if (app.Environment.IsDevelopment())
58+
{
59+
_ = app.UseSwagger().UseSwaggerUI();
60+
_ = app.UseDeveloperExceptionPage();
61+
62+
Database database = await cosmosClient.CreateDatabaseIfNotExistsAsync("TodoDb");
63+
64+
_ = await database.CreateContainerIfNotExistsAsync(new ContainerProperties("TodoContainer", "/entity")
65+
{
66+
IndexingPolicy = new()
67+
{
68+
CompositeIndexes =
69+
{
70+
new Collection<CompositePath>()
71+
{
72+
new CompositePath() { Path = "/updatedAt", Order = CompositePathSortOrder.Ascending },
73+
new CompositePath() { Path = "/id", Order = CompositePathSortOrder.Ascending }
74+
}
75+
}
76+
}
77+
});
78+
}
79+
80+
app.UseHttpsRedirection();
81+
82+
app.UseAuthorization();
83+
84+
app.MapControllers();
85+
86+
app.Run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": false,
8+
"applicationUrl": "http://localhost:5024",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": false,
17+
"applicationUrl": "https://localhost:7284;http://localhost:5024",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<UserSecretsId>4fb4ad03-5b6a-43b8-8e71-90b9d8252f94</UserSecretsId>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.CosmosDb\CommunityToolkit.Datasync.Server.CosmosDb.csproj" />
12+
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server.Swashbuckle\CommunityToolkit.Datasync.Server.Swashbuckle.csproj" />
13+
<ProjectReference Include="..\..\..\..\src\CommunityToolkit.Datasync.Server\CommunityToolkit.Datasync.Server.csproj" />
14+
</ItemGroup>
15+
16+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@Sample.Datasync.Server.SingleContainer_HostAddress = https://localhost:7284
2+
3+
POST {{Sample.Datasync.Server.SingleContainer_HostAddress}}/tables/TodoItem
4+
Content-Type: application/json
5+
6+
{
7+
"id": "2",
8+
"title": "Second item",
9+
"isComplete": false
10+
}
11+
12+
###
13+
14+
GET {{Sample.Datasync.Server.SingleContainer_HostAddress}}/tables/TodoItem
15+
Accept: application/json
16+
17+
###
18+
19+
POST {{Sample.Datasync.Server.SingleContainer_HostAddress}}/tables/TodoList
20+
Content-Type: application/json
21+
22+
{
23+
"id": "1",
24+
"title": "First List",
25+
"listId": "My List"
26+
}
27+
28+
###
29+
30+
GET {{Sample.Datasync.Server.SingleContainer_HostAddress}}/tables/TodoList
31+
Accept: application/json
32+
33+
###

0 commit comments

Comments
 (0)