├── .github └── workflows │ ├── codeql-analysis.yml │ ├── dotnet.yml │ └── nuget.yml ├── .gitignore ├── .ignore └── not implemented │ ├── ManagedCode.Database.EntityFramework.MSSQL │ ├── Extensions │ │ └── ProviderBuilderExtensions.cs │ ├── IMSSQLRepository.cs │ ├── MSSQLDatabaseContext.cs │ ├── MSSQLItem.cs │ ├── MSSQLRepository.cs │ ├── ManagedCode.Database.EntityFramework.MSSQL.csproj │ └── Models │ │ └── MSSQLConnectionOptions.cs │ ├── ManagedCode.Database.EntityFramework.PostgreSQL │ ├── Extensions │ │ └── ProviderBuilderExtension.cs │ ├── IPostgresRepository.cs │ ├── ManagedCode.Database.EntityFramework.PostgreSQL.csproj │ ├── Models │ │ └── PostgresConnectionOptions.cs │ ├── PostgresDatabaseContext.cs │ ├── PostgresItem.cs │ └── PostgresRepository.cs │ ├── ManagedCode.Database.EntityFramework │ ├── EFBaseRepository.cs │ ├── EFDbContext.cs │ ├── EFRepository.cs │ ├── Interfaces │ │ ├── IEFItem.cs │ │ └── IEFRepository.cs │ └── ManagedCode.Database.EntityFramework.csproj │ └── ManagedCode.Database.Realm │ ├── Extensions │ └── ProviderBuilderExtension.cs │ ├── IMongoDbRepository.cs │ ├── ManagedCode.Database.Realm.csproj │ ├── RealmDataBase.cs │ ├── RealmDbItem.cs │ ├── RealmDbRepositoryOptions.cs │ └── RealmRepository.cs ├── Directory.Build.props ├── LICENSE ├── ManagedCode.Database.AzureTables ├── AzureTablesCollectionQueryable.cs ├── AzureTablesDatabase.cs ├── AzureTablesDatabaseCollection.cs ├── AzureTablesItem.cs ├── AzureTablesOptions.cs ├── Extensions │ ├── ProviderBuilderExtension.cs │ └── TableClientExtensions.cs ├── IAzuresDatabaseCollection.cs ├── ManagedCode.Database.AzureTables.csproj └── TableId.cs ├── ManagedCode.Database.Core ├── BaseCollectionQueryable.cs ├── BaseDatabase.cs ├── BaseDatabaseCollection.cs ├── Common │ └── DatabaseNotInitializedException.cs ├── Exceptions │ └── DatabaseException.cs ├── Extensions │ ├── ServiceCollectionExtensions.cs │ ├── ServiceCollectionHolder.cs │ └── TypeExtensions.cs ├── ICollectionQueryable.cs ├── ICollectionQueryableResultAsync.cs ├── IDatabase.cs ├── IDatabaseCollection.cs ├── IItem.cs ├── IOrderedCollectionQueryable.cs ├── InMemory │ ├── InMemoryCollectionQueryable.cs │ ├── InMemoryDatabase.cs │ └── InMemoryDatabaseCollection.cs └── ManagedCode.Database.Core.csproj ├── ManagedCode.Database.Cosmos ├── CosmosCollection.cs ├── CosmosCollectionQueryable.cs ├── CosmosDatabase.cs ├── CosmosItem.cs ├── CosmosOptions.cs ├── Extensions │ └── ProviderBuilderExtension.cs └── ManagedCode.Database.Cosmos.csproj ├── ManagedCode.Database.DynamoDB ├── DynamoDBCollection.cs ├── DynamoDBCollectionQueryable.cs ├── DynamoDBDatabase.cs ├── DynamoDBItem.cs ├── DynamoDBOptions.cs └── ManagedCode.Database.DynamoDB.csproj ├── ManagedCode.Database.LiteDB ├── Extensions │ └── ProviderBuilderExtension.cs ├── LiteDBCollection.cs ├── LiteDBCollectionQueryable.cs ├── LiteDBDatabase.cs ├── LiteDBItem.cs ├── LiteDBOptions.cs └── ManagedCode.Database.LiteDB.csproj ├── ManagedCode.Database.MongoDB ├── Extensions │ └── ProviderBuilderExtension.cs ├── ManagedCode.Database.MongoDB.csproj ├── MongoDBCollection.cs ├── MongoDBCollectionQueryable.cs ├── MongoDBDatabase.cs ├── MongoDBItem.cs └── MongoDBOptions.cs ├── ManagedCode.Database.SQLite ├── Extensions │ ├── ExpressionExtensions.cs │ └── ProviderBuilderExtension.cs ├── ManagedCode.Database.SQLite.csproj ├── SQLiteCollectionQueryable.cs ├── SQLiteDatabase.cs ├── SQLiteDatabaseCollection.cs ├── SQLiteItem.cs └── SQLiteRepositoryOptions.cs ├── ManagedCode.Database.Tests.Benchmark ├── ManagedCode.Database.Tests.Benchmark.csproj ├── Program.cs ├── Various │ └── SerializerBenchmarks.cs └── ZoneTreeBenchmarks.cs ├── ManagedCode.Database.Tests ├── AzureTablesTests │ ├── AzureTablesCollectionTests.cs │ └── AzureTablesQueryableTests.cs ├── BaseTests │ ├── BaseCollectionTests.cs │ ├── BaseMultiThreadingTests.cs │ ├── BaseQueryableTests.cs │ └── BaseTests.cs ├── Common │ ├── IBaseItem.cs │ ├── InMemoryItem.cs │ ├── TestAzureTablesItem.cs │ ├── TestCosmosItem.cs │ ├── TestDynamoDbItem.cs │ ├── TestLiteDBItem.cs │ ├── TestMongoDBItem.cs │ ├── TestSQLiteItem.cs │ └── TestZoneTreeItem.cs ├── CosmosTests │ ├── CosmosCollectionTests.cs │ └── CosmosQueryableTests.cs ├── DynamoDbTests │ ├── DynamoDBQueryableTests.cs │ └── DynamoDbCollectionTests.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── InMemoryTests │ ├── InMemoryCollectionTests.cs │ └── InMemoryQueryableTests.cs ├── LiteDBTests │ ├── LiteDBCollectionTests.cs │ └── LiteDBQueryableTests.cs ├── MSSQL │ ├── MSSQLRepositoryTests.cs │ ├── Models │ │ └── Customer.cs │ └── Repositories │ │ ├── CustomerRepository.cs │ │ └── ICustomerRepository.cs ├── ManagedCode.Database.Tests.csproj ├── MongoDBTests │ ├── MongoDBCollectionTests.cs │ └── MongoDBQueryableTests.cs ├── PostgreSQL │ ├── Models │ │ └── Customer.cs │ ├── PostgreSQLRepositoryTests.cs │ └── Repositories │ │ ├── CustomerRepository.cs │ │ └── ICustomerRepository.cs ├── SQLiteTests │ ├── SQLiteCollectionTests.cs │ └── SQLiteQueryableTests.cs ├── TestContainers │ ├── AzureTablesTestContainer.cs │ ├── CosmosTestContainer.cs │ ├── DynamoDBTestContainer.cs │ ├── ITestContainer.cs │ ├── InMemoryTestContainer.cs │ ├── LiteDBTestContainer.cs │ ├── MongoDBTestContainer.cs │ ├── SQLiteTestContainer.cs │ └── ZoneTreeTestContainer.cs └── ZoneTreeTests │ ├── ZoneTreeCollectionTests.cs │ └── ZoneTreeQueryableTests.cs ├── ManagedCode.Database.ZoneTree ├── IZoneTreeItem.cs ├── JsonSerializer.cs ├── ManagedCode.Database.ZoneTree.csproj ├── StorageType.cs ├── ZoneTreeCollection.cs ├── ZoneTreeCollectionOptions.cs ├── ZoneTreeCollectionQueryable.cs ├── ZoneTreeDatabase.cs ├── ZoneTreeItem.cs ├── ZoneTreeOptions.cs └── ZoneTreeWrapper.cs ├── ManagedCode.Database.sln ├── README.md ├── logo.png └── testEnvironments.json /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '35 11 * * 4' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | 28 | strategy: 29 | fail-fast: false 30 | matrix: 31 | language: [ 'csharp' ] 32 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 33 | # Learn more: 34 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | jobs: 14 | 15 | build-and-test: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v3 21 | with: 22 | dotnet-version: 7.0.x 23 | 24 | # run build and test 25 | - name: Restore dependencies 26 | run: dotnet restore 27 | - name: Build 28 | run: dotnet build --no-restore 29 | - name: Test 30 | run: dotnet test --no-build --verbosity normal /p:CollectCoverage=true /p:CoverletOutputFormat=lcov /p:CoverletOutput=ManagedCode.Repository.Tests/lcov.info 31 | 32 | #- name: coverlet 33 | # uses: b3b00/coverlet-action@1.1.9 34 | # with: 35 | # testProject: 'ManagedCode.Database.Tests/ManagedCode.Database.Tests.csproj' 36 | # output: 'lcov.info' 37 | # outputFormat: 'lcov' 38 | # excludes: '[program]*,[test]test.*' 39 | #- name: coveralls 40 | # uses: coverallsapp/github-action@master 41 | # with: 42 | # github-token: ${{secrets.GITHUB_TOKEN }} 43 | # path-to-lcov: ManagedCode.Storage.Tests/lcov.info 44 | -------------------------------------------------------------------------------- /.github/workflows/nuget.yml: -------------------------------------------------------------------------------- 1 | name: nuget 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | 7 | # Allows you to run this workflow manually from the Actions tab 8 | workflow_dispatch: 9 | 10 | jobs: 11 | nuget-pack: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v3 19 | with: 20 | dotnet-version: 7.0.x 21 | 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | - name: Build 25 | run: dotnet build --configuration Release 26 | # - name: Test 27 | # run: dotnet test --configuration Release 28 | - name: Pack 29 | run: dotnet pack -p:IncludeSymbols=false -p:SymbolPackageFormat=snupkg --configuration Release 30 | 31 | - name: publish nuget packages 32 | run: | 33 | shopt -s globstar 34 | for file in **/*.nupkg 35 | do 36 | dotnet nuget push "$file" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate 37 | done -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/Extensions/ProviderBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.EntityFramework.MSSQL.Models; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace ManagedCode.Database.EntityFramework.MSSQL.Extensions; 7 | 8 | public static class ProviderBuilderExtensions 9 | { 10 | public static IServiceCollection AddMSSQLBasedOnEF(this IServiceCollection serviceCollection, Action action) 11 | { 12 | var connectionOptions = new MSSQLConnectionOptions(); 13 | action.Invoke(connectionOptions); 14 | 15 | serviceCollection.AddDbContext(options => options 16 | .UseSqlServer(connectionOptions.ConnectionString) 17 | .UseQueryTrackingBehavior( 18 | connectionOptions.UseTracking ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking 19 | ) 20 | ); 21 | 22 | return serviceCollection; 23 | } 24 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/IMSSQLRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.Interfaces; 2 | 3 | namespace ManagedCode.Database.EntityFramework.MSSQL; 4 | 5 | public interface IMSSQLRepository : IEFRepository 6 | where TItem : IEFItem, new() 7 | { 8 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/MSSQLDatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace ManagedCode.Database.EntityFramework.MSSQL; 5 | 6 | public class MSSQLDatabaseContext : EFDbContext 7 | { 8 | public MSSQLDatabaseContext( 9 | DbContextOptions options, 10 | ServiceCollectionHolder serviceCollectionHolder) 11 | : base(options, serviceCollectionHolder) 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/MSSQLItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.Interfaces; 2 | 3 | namespace ManagedCode.Database.EntityFramework.MSSQL; 4 | 5 | public class MSSQLItem : IEFItem 6 | { 7 | public TId Id { get; set; } 8 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/MSSQLRepository.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.EntityFramework.MSSQL; 2 | 3 | public class MSSQLRepository : EFRepository, IMSSQLRepository 4 | where TItem : MSSQLItem, new() 5 | { 6 | public MSSQLRepository(MSSQLDatabaseContext context) : base(context) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/ManagedCode.Database.EntityFramework.MSSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | true 6 | 10 7 | embedded 8 | true 9 | 10 | 11 | 12 | 13 | ManagedCode.Repository.EntityFramework.MSSQL 14 | ManagedCode.Database.EntityFramework.MSSQL 15 | Repository for MSSQL 16 | managedcode, repository, EntityFramework, MSSQL 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.MSSQL/Models/MSSQLConnectionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.EntityFramework.MSSQL.Models; 2 | 3 | public class MSSQLConnectionOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | public bool UseTracking { get; set; } 7 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.EntityFramework.PostgreSQL.Models; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace ManagedCode.Database.EntityFramework.PostgreSQL.Extensions; 7 | 8 | public static class ProviderBuilderExtension 9 | { 10 | public static IServiceCollection AddPostgreSQLBasedOnEF(this IServiceCollection serviceCollection, Action action) 11 | { 12 | var connectionOptions = new PostgresConnectionOptions(); 13 | action.Invoke(connectionOptions); 14 | 15 | serviceCollection.AddDbContext(options => options 16 | .UseNpgsql(connectionOptions.ConnectionString) 17 | .UseQueryTrackingBehavior( 18 | connectionOptions.UseTracking ? QueryTrackingBehavior.TrackAll : QueryTrackingBehavior.NoTracking 19 | ) 20 | ); 21 | 22 | return serviceCollection; 23 | } 24 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/IPostgresRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.Interfaces; 2 | 3 | namespace ManagedCode.Database.EntityFramework.PostgreSQL; 4 | 5 | public interface IPostgresRepository : IEFRepository 6 | where TItem : IEFItem, new() 7 | { 8 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/ManagedCode.Database.EntityFramework.PostgreSQL.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 10 6 | embedded 7 | true 8 | net6.0 9 | 10 | 11 | 12 | 13 | ManagedCode.Repository.EntityFramework.PostgreSQL 14 | ManagedCode.Database.EntityFramework.PostgreSQL 15 | Repository for PostgreSQL 16 | managedcode, repository, EntityFramework, PostgreSQL 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/Models/PostgresConnectionOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.EntityFramework.PostgreSQL.Models; 2 | 3 | public class PostgresConnectionOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | public bool UseTracking { get; set; } 7 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/PostgresDatabaseContext.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace ManagedCode.Database.EntityFramework.PostgreSQL; 5 | 6 | public class PostgresDatabaseContext : EFDbContext 7 | { 8 | public PostgresDatabaseContext( 9 | DbContextOptions options, 10 | ServiceCollectionHolder serviceCollectionHolder) 11 | : base(options, serviceCollectionHolder) 12 | { 13 | } 14 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/PostgresItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.Interfaces; 2 | 3 | namespace ManagedCode.Database.EntityFramework.PostgreSQL; 4 | 5 | public class PostgresItem : IEFItem 6 | { 7 | public TId Id { get; set; } 8 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework.PostgreSQL/PostgresRepository.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.EntityFramework.PostgreSQL; 2 | 3 | public class PostgresRepository : EFRepository, IPostgresRepository 4 | where TItem : PostgresItem, new() 5 | { 6 | public PostgresRepository(PostgresDatabaseContext context) : base(context) 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework/EFBaseRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using ManagedCode.Database.EntityFramework.Interfaces; 3 | using Microsoft.EntityFrameworkCore; 4 | 5 | namespace ManagedCode.Database.EntityFramework; 6 | 7 | public abstract class EFBaseDBCollection : 8 | BaseDBCollection, 9 | IEFRepository 10 | where TItem : class, IEFItem, new() 11 | where TContext : DbContext 12 | { 13 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework/EFDbContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ManagedCode.Database.Core; 4 | using ManagedCode.Database.Core.Extensions; 5 | using Microsoft.EntityFrameworkCore; 6 | 7 | namespace ManagedCode.Database.EntityFramework; 8 | 9 | public class EFDbContext : DbContext where TContext : EFDbContext 10 | { 11 | private readonly IList _entityTypes; 12 | 13 | public EFDbContext(DbContextOptions options, ServiceCollectionHolder serviceCollectionHolder) : base(options) 14 | { 15 | _entityTypes = new List(); 16 | 17 | foreach (var item in serviceCollectionHolder.ServiceCollection) 18 | { 19 | var belongs = item.ImplementationType?.BaseType?.BaseType.EqualsToGeneric(typeof(EFRepository<,,>)); 20 | 21 | if (belongs != null && belongs.Value) 22 | { 23 | var typeParameters = item.ImplementationType.BaseType.GetGenericArguments(); 24 | 25 | if (typeParameters.Length == 2) 26 | { 27 | _entityTypes.Add(typeParameters[1]); 28 | } 29 | } 30 | } 31 | } 32 | 33 | protected override void OnModelCreating(ModelBuilder modelBuilder) 34 | { 35 | var entityMethod = typeof(ModelBuilder).GetMethod("Entity", new[] { typeof(Type) }); 36 | 37 | foreach (var type in _entityTypes) 38 | { 39 | entityMethod 40 | .Invoke(modelBuilder, new object[] { type }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework/Interfaces/IEFItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | 3 | namespace ManagedCode.Database.EntityFramework.Interfaces; 4 | 5 | public interface IEFItem : IItem 6 | { 7 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework/Interfaces/IEFRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace ManagedCode.Database.EntityFramework.Interfaces; 5 | 6 | public interface IEFRepository : IRepository 7 | where TItem : IEFItem, new() 8 | where TContext : DbContext 9 | { 10 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.EntityFramework/ManagedCode.Database.EntityFramework.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | true 6 | 10 7 | embedded 8 | true 9 | 10 | 11 | 12 | 13 | ManagedCode.Repository.EntityFramework 14 | ManagedCode.Database.EntityFramework 15 | Storage for EntityFramework 16 | managedcode, repository, EntityFramework 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Repository.MongoDB.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddMongoDb(this IServiceCollection serviceCollection, Action action) 9 | { 10 | var connectionOptions = new MongoDbRepositoryOptions(); 11 | action.Invoke(connectionOptions); 12 | 13 | serviceCollection.AddSingleton(connectionOptions); 14 | 15 | return serviceCollection; 16 | } 17 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/IMongoDbRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using MongoDB.Bson; 3 | 4 | namespace ManagedCode.Repository.MongoDB; 5 | 6 | //public interface IRealmRepository : IRepository where TItem : class, IItem 7 | //{ 8 | //} -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/ManagedCode.Database.Realm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | true 6 | 10 7 | embedded 8 | true 9 | 10 | 11 | 12 | 13 | ManagedCode.Repository.Realm 14 | ManagedCode.Database.Realm 15 | Repository for Realm 16 | managedcode, repository, Realm 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/RealmDataBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Humanizer; 6 | using ManagedCode.Database.Core; 7 | using ManagedCode.Database.Core.Common; 8 | using MongoDB.Bson; 9 | using Realms; 10 | using Realms.Sync; 11 | 12 | namespace ManagedCode.Repository.Realm; 13 | 14 | public class RealmDataBase : BaseDatabase, IDatabase 15 | { 16 | private readonly MongoDbRepositoryOptions _options; 17 | 18 | public RealmDataBase([NotNull] MongoDbRepositoryOptions options) 19 | { 20 | var client = Realms.Realm.GetInstance();// new MongoClient(options.ConnectionString); 21 | var database = client.GetDatabase(options.DataBaseName); 22 | var collectionName = string.IsNullOrEmpty(options.CollectionName) ? typeof(TItem).Name.Pluralize() : options.CollectionName; 23 | _collection = database.GetCollection(collectionName, new MongoCollectionSettings()); 24 | IsInitialized = true; 25 | } 26 | 27 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 28 | { 29 | return Task.CompletedTask; 30 | } 31 | 32 | protected override ValueTask DisposeAsyncInternal() 33 | { 34 | return new ValueTask(Task.CompletedTask); 35 | } 36 | 37 | protected override void DisposeInternal() 38 | { 39 | } 40 | 41 | public MongoDbCollection GetCollection() where TItem : class, IItem, new() 42 | { 43 | if (!IsInitialized) 44 | { 45 | throw new DatabaseNotInitializedException(GetType()); 46 | } 47 | 48 | var collectionName = string.IsNullOrEmpty(_options.CollectionName) ? typeof(TItem).Name.Pluralize() : _options.CollectionName; 49 | return new MongoDbCollection(DataBase.GetCollection(collectionName, new MongoCollectionSettings())); 50 | } 51 | 52 | public MongoDbCollection GetCollection(string name) where TItem : class, IItem, new() 53 | { 54 | if (!IsInitialized) 55 | { 56 | throw new DatabaseNotInitializedException(GetType()); 57 | } 58 | 59 | return new MongoDbCollection(DataBase.GetCollection(name, new MongoCollectionSettings())); 60 | } 61 | 62 | public override Task Delete(CancellationToken token = default) 63 | { 64 | throw new NotImplementedException(); 65 | } 66 | 67 | public IMongoDatabase DataBase { get; } 68 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/RealmDbItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using MongoDB.Bson; 3 | using MongoDB.Bson.Serialization.Attributes; 4 | using Realms; 5 | 6 | namespace ManagedCode.Repository.MongoDB; 7 | 8 | public class RealmDbItem : RealmObject, IItem 9 | { 10 | public RealmDbItem() 11 | { 12 | Id = ObjectId.GenerateNewId(); 13 | } 14 | 15 | public RealmDbItem(string id) 16 | { 17 | Id = ObjectId.Parse(id); 18 | } 19 | 20 | public RealmDbItem(ObjectId id) 21 | { 22 | Id = id; 23 | } 24 | 25 | [BsonId] 26 | [BsonRepresentation(BsonType.ObjectId)] 27 | public ObjectId Id { get; set; } 28 | } -------------------------------------------------------------------------------- /.ignore/not implemented/ManagedCode.Database.Realm/RealmDbRepositoryOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Repository.MongoDB; 2 | 3 | public class RealmDbRepositoryOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | public string DataBaseName { get; set; } 7 | 8 | public string CollectionName { get; set; } 9 | } -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ManagedCode 5 | Copyright © 2021-$([System.DateTime]::Now.ToString(`yyyy`)) ManagedCode SAS 6 | true 7 | true 8 | true 9 | snupkg 10 | Github 11 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb 12 | logo.png 13 | MIT 14 | true 15 | README.md 16 | 17 | https://github.com/managedcode/Database 18 | https://github.com/managedcode/Database 19 | Managed Code - Database 20 | 2.0.3 21 | 2.0.3 22 | 23 | 24 | 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | all 34 | runtime; build; native; contentfiles; analyzers; buildtransitive 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Managed-Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/AzureTablesCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Azure.Data.Tables; 6 | using ManagedCode.Database.AzureTables.Extensions; 7 | using ManagedCode.Database.Core; 8 | 9 | namespace ManagedCode.Database.AzureTables; 10 | 11 | public class AzureTablesCollectionQueryable : BaseCollectionQueryable 12 | where TItem : class, ITableEntity, new() 13 | { 14 | private readonly TableClient _tableClient; 15 | 16 | public AzureTablesCollectionQueryable(TableClient tableClient) 17 | { 18 | _tableClient = tableClient; 19 | } 20 | 21 | public override IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default) 22 | { 23 | var filter = ConvertPredicatesToFilter(Predicates); 24 | var query = _tableClient.QueryAsync(filter, cancellationToken: cancellationToken); 25 | 26 | return ApplyPredicates(query, Predicates); 27 | } 28 | 29 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 30 | { 31 | var filter = ConvertPredicatesToFilter(Predicates); 32 | 33 | return await _tableClient 34 | .QueryAsync(filter, 1, cancellationToken: cancellationToken) 35 | .FirstOrDefaultAsync(cancellationToken); 36 | } 37 | 38 | public override async Task CountAsync(CancellationToken cancellationToken = default) 39 | { 40 | var filter = ConvertPredicatesToFilter(Predicates); 41 | 42 | var query = _tableClient 43 | .QueryAsync(filter, cancellationToken: cancellationToken); 44 | return await ApplyPredicates(query, Predicates).CountAsync(cancellationToken); 45 | } 46 | 47 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 48 | { 49 | var filter = ConvertPredicatesToFilter(Predicates); 50 | 51 | var items = await _tableClient 52 | .QueryAsync(filter, cancellationToken: cancellationToken).ToListAsync(cancellationToken); 53 | 54 | var responses = await _tableClient.SubmitTransactionByChunksAsync(items, 55 | TableTransactionActionType.Delete, cancellationToken); 56 | 57 | return responses.Count(v => !v.IsError); 58 | } 59 | 60 | private static IAsyncEnumerable ApplyPredicates(IAsyncEnumerable enumerable, 61 | List predicates) 62 | { 63 | // TODO: add warning 64 | foreach (var predicate in predicates) 65 | enumerable = predicate.QueryType switch 66 | { 67 | QueryType.OrderBy => enumerable.OrderBy(x => predicate.ExpressionObject.Compile().Invoke(x)), 68 | QueryType.OrderByDescending => enumerable 69 | .OrderByDescending(x => predicate.ExpressionObject.Compile().Invoke(x)), 70 | QueryType.ThenBy => (enumerable as IOrderedAsyncEnumerable)! 71 | .ThenBy(x => predicate.ExpressionObject.Compile().Invoke(x)), 72 | QueryType.ThenByDescending => (enumerable as IOrderedAsyncEnumerable)! 73 | .ThenByDescending(x => predicate.ExpressionObject.Compile().Invoke(x)), 74 | QueryType.Take => predicate.Count.HasValue ? enumerable.Take(predicate.Count.Value) : enumerable, 75 | QueryType.Skip => enumerable.Skip(predicate.Count!.Value), 76 | _ => enumerable 77 | }; 78 | 79 | return enumerable; 80 | } 81 | 82 | private static string? ConvertPredicatesToFilter(IEnumerable predicates) 83 | { 84 | var wherePredicates = predicates 85 | .Where(p => p.QueryType is QueryType.Where) 86 | .ToList(); 87 | 88 | if (!wherePredicates.Any()) 89 | { 90 | return null; 91 | } 92 | 93 | var filter = wherePredicates 94 | .Where(p => p.QueryType is QueryType.Where) 95 | .Select(p => TableClient.CreateQueryFilter(p.ExpressionBool)) 96 | .Aggregate((a, b) => a + " and " + b); 97 | 98 | return filter; 99 | } 100 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/AzureTablesDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Azure.Data.Tables; 6 | using Humanizer; 7 | using ManagedCode.Database.Core; 8 | using ManagedCode.Database.Core.Common; 9 | 10 | namespace ManagedCode.Database.AzureTables; 11 | 12 | public class AzureTablesDatabase : BaseDatabase 13 | { 14 | private readonly Dictionary _collections = new(); 15 | private readonly AzureTablesOptions _options; 16 | 17 | public AzureTablesDatabase(AzureTablesOptions options) 18 | { 19 | _options = options; 20 | } 21 | 22 | public override Task DeleteAsync(CancellationToken token = default) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | 27 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 28 | { 29 | NativeClient = string.IsNullOrEmpty(_options.ConnectionString) switch 30 | { 31 | true => new TableServiceClient(_options.TableStorageUri, _options.TableSharedKeyCredential), 32 | false => new TableServiceClient(_options.ConnectionString) 33 | }; 34 | 35 | return Task.CompletedTask; 36 | } 37 | 38 | protected override ValueTask DisposeAsyncInternal() 39 | { 40 | DisposeInternal(); 41 | return new ValueTask(Task.CompletedTask); 42 | } 43 | 44 | protected override void DisposeInternal() 45 | { 46 | _collections.Clear(); 47 | } 48 | 49 | public AzureTablesDatabaseCollection GetCollection() 50 | where TItem : AzureTablesItem, IItem, new() 51 | { 52 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 53 | 54 | var collectionName = typeof(TItem).FullName; 55 | var tableName = GetTableName(); 56 | 57 | lock (_collections) 58 | { 59 | if (_collections.TryGetValue(collectionName, out var obj)) 60 | return obj as AzureTablesDatabaseCollection; 61 | 62 | var table = NativeClient.GetTableClient(tableName); 63 | 64 | if (_options.AllowTableCreation) 65 | { 66 | table.CreateIfNotExists(); 67 | } 68 | 69 | // var exists = table. 70 | // 71 | // if (!exists) 72 | // { 73 | // throw new InvalidOperationException($"Table '{tableName}' does not exist"); 74 | // } 75 | var collection = new AzureTablesDatabaseCollection(table); 76 | 77 | _collections[collectionName] = collection; 78 | 79 | return collection; 80 | } 81 | } 82 | 83 | private string GetTableName() 84 | { 85 | return string.IsNullOrEmpty(_options.TableName) ? typeof(TItem).Name.Pluralize() : _options.TableName; 86 | } 87 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/AzureTablesItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using Azure; 4 | using Azure.Data.Tables; 5 | using ManagedCode.Database.Core; 6 | 7 | namespace ManagedCode.Database.AzureTables; 8 | 9 | public class AzureTablesItem : ITableEntity, IItem 10 | { 11 | private TableId _id; 12 | 13 | public AzureTablesItem() 14 | { 15 | _id = new TableId(this); 16 | } 17 | 18 | [IgnoreDataMember] 19 | public TableId Id 20 | { 21 | get => _id; 22 | set 23 | { 24 | _id = value; 25 | _id.SetEntity(this); 26 | } 27 | } 28 | 29 | public string PartitionKey { get; set; } = default!; 30 | public string RowKey { get; set; } = default!; 31 | public DateTimeOffset? Timestamp { get; set; } = default!; 32 | public ETag ETag { get; set; } 33 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/AzureTablesOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Azure.Data.Tables; 3 | 4 | namespace ManagedCode.Database.AzureTables; 5 | 6 | public class AzureTablesOptions 7 | { 8 | public string ConnectionString { get; set; } 9 | public TableSharedKeyCredential TableSharedKeyCredential { get; set; } 10 | public Uri TableStorageUri { get; set; } 11 | public bool AllowTableCreation { get; set; } 12 | public string TableName { get; set; } 13 | 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Database.AzureTables.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddAzureTables(this IServiceCollection serviceCollection, 9 | Action action) 10 | { 11 | var connectionOptions = new AzureTablesOptions(); 12 | action.Invoke(connectionOptions); 13 | 14 | serviceCollection.AddSingleton(connectionOptions); 15 | 16 | return serviceCollection; 17 | } 18 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/Extensions/TableClientExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using Azure; 6 | using Azure.Data.Tables; 7 | using ManagedCode.Database.Core; 8 | 9 | namespace ManagedCode.Database.AzureTables.Extensions; 10 | 11 | public static class TableClientExtensions 12 | { 13 | private const int MaxItemsPerRequest = 100; 14 | 15 | public static async Task> SubmitTransactionByChunksAsync(this TableClient tableClient, 16 | IEnumerable items, 17 | TableTransactionActionType actionType, CancellationToken cancellationToken) where TItem : ITableEntity, new() 18 | { 19 | var chunks = items.Select(item => 20 | new TableTransactionAction(actionType, item, item.ETag)).Chunk(MaxItemsPerRequest); 21 | 22 | List responses = new(); 23 | 24 | foreach (var chunk in chunks) 25 | { 26 | var response = await tableClient.SubmitTransactionAsync(chunk, cancellationToken); 27 | 28 | var list = response?.Value.Select(i => i) ?? new List(); 29 | responses.AddRange(list); 30 | } 31 | 32 | return responses; 33 | } 34 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/IAzuresDatabaseCollection.cs: -------------------------------------------------------------------------------- 1 | using Azure.Data.Tables; 2 | using ManagedCode.Database.Core; 3 | 4 | namespace ManagedCode.Database.AzureTables; 5 | 6 | public interface IAzuresDatabaseCollection : IDatabaseCollection 7 | where T : class, IItem, ITableEntity, new() 8 | { 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/ManagedCode.Database.AzureTables.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | embedded 9 | true 10 | enable 11 | ManagedCode.Database.AzureTables 12 | ManagedCode.Database.AzureTables 13 | 14 | 15 | 16 | 17 | ManagedCode.Repository.AzureTables 18 | ManagedCode.Database.AzureTables 19 | Repository for AzureTables 20 | managedcode, repository, AzureTables 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ManagedCode.Database.AzureTables/TableId.cs: -------------------------------------------------------------------------------- 1 | using Azure.Data.Tables; 2 | 3 | namespace ManagedCode.Database.AzureTables; 4 | 5 | public class TableId 6 | { 7 | private ITableEntity _internalEntity; 8 | 9 | public TableId(ITableEntity entity) 10 | { 11 | _internalEntity = entity; 12 | } 13 | 14 | public TableId(string partitionKey, string rowKey) 15 | { 16 | _internalEntity = new TableEntity(partitionKey, rowKey); 17 | } 18 | 19 | public string PartitionKey 20 | { 21 | get => _internalEntity.PartitionKey; 22 | set => _internalEntity.PartitionKey = value; 23 | } 24 | 25 | public string RowKey 26 | { 27 | get => _internalEntity.RowKey; 28 | set => _internalEntity.RowKey = value; 29 | } 30 | 31 | public void SetEntity(ITableEntity entity) 32 | { 33 | entity.PartitionKey = PartitionKey; 34 | entity.RowKey = RowKey; 35 | _internalEntity = entity; 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return $"PartitionKey:{PartitionKey};RowKey:{PartitionKey};"; 41 | } 42 | 43 | public override bool Equals(object? obj) 44 | { 45 | if (obj is TableId tableId) return tableId.PartitionKey == PartitionKey && tableId.RowKey == RowKey; 46 | 47 | return false; 48 | } 49 | 50 | public override int GetHashCode() 51 | { 52 | return PartitionKey.GetHashCode() ^ RowKey.GetHashCode(); 53 | } 54 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/BaseCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace ManagedCode.Database.Core 9 | { 10 | public abstract class BaseCollectionQueryable : IOrderedCollectionQueryable 11 | { 12 | protected readonly List Predicates = new(); 13 | 14 | protected enum QueryType 15 | { 16 | Where, 17 | OrderBy, 18 | OrderByDescending, 19 | ThenBy, 20 | ThenByDescending, 21 | Take, 22 | Skip, 23 | } 24 | 25 | protected struct QueryItem 26 | { 27 | public QueryType QueryType; 28 | public Expression> ExpressionObject; 29 | public Expression> ExpressionBool; 30 | public int? Count; 31 | } 32 | 33 | public abstract IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default); 34 | public abstract Task FirstOrDefaultAsync(CancellationToken cancellationToken = default); 35 | public abstract Task CountAsync(CancellationToken cancellationToken = default); 36 | public abstract Task DeleteAsync(CancellationToken cancellationToken = default); 37 | 38 | public ICollectionQueryable Where(Expression> predicate) 39 | { 40 | Predicates.Add(new QueryItem { QueryType = QueryType.Where, ExpressionBool = predicate }); 41 | return this; 42 | } 43 | 44 | public IOrderedCollectionQueryable OrderBy(Expression> keySelector) 45 | { 46 | if (Predicates.Exists(i => i.QueryType is QueryType.OrderBy)) 47 | { 48 | throw new InvalidOperationException(); 49 | } 50 | 51 | Predicates.Add(new QueryItem { QueryType = QueryType.OrderBy, ExpressionObject = keySelector }); 52 | return this; 53 | } 54 | 55 | public IOrderedCollectionQueryable OrderByDescending(Expression> keySelector) 56 | { 57 | if (Predicates.Exists(i => i.QueryType is QueryType.OrderBy)) 58 | { 59 | throw new InvalidOperationException(); 60 | } 61 | 62 | Predicates.Add(new QueryItem { QueryType = QueryType.OrderByDescending, ExpressionObject = keySelector }); 63 | return this; 64 | } 65 | 66 | 67 | public IOrderedCollectionQueryable ThenBy(Expression> keySelector) 68 | { 69 | Predicates.Add(new QueryItem { QueryType = QueryType.ThenBy, ExpressionObject = keySelector }); 70 | return this; 71 | } 72 | 73 | public IOrderedCollectionQueryable ThenByDescending(Expression> keySelector) 74 | { 75 | Predicates.Add(new QueryItem { QueryType = QueryType.ThenByDescending, ExpressionObject = keySelector }); 76 | return this; 77 | } 78 | 79 | public ICollectionQueryable Take(int? count) 80 | { 81 | Predicates.Add(new QueryItem { QueryType = QueryType.Take, Count = count }); 82 | return this; 83 | } 84 | 85 | public ICollectionQueryable Skip(int count) 86 | { 87 | Predicates.Add(new QueryItem { QueryType = QueryType.Skip, Count = count }); 88 | return this; 89 | } 90 | 91 | public async Task> ToListAsync(CancellationToken cancellationToken = default) 92 | { 93 | return new List(await ToAsyncEnumerable(cancellationToken).ToListAsync(cancellationToken)); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/BaseDatabase.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using ManagedCode.Database.Core.Common; 4 | 5 | namespace ManagedCode.Database.Core 6 | { 7 | public abstract class BaseDatabase : IDatabase 8 | { 9 | private bool _disposed; 10 | private T _nativeClient = default!; 11 | 12 | public bool IsInitialized { get; protected set; } 13 | 14 | public T NativeClient 15 | { 16 | get 17 | { 18 | if (!IsInitialized) 19 | { 20 | throw new DatabaseNotInitializedException(GetType()); 21 | } 22 | 23 | return _nativeClient; 24 | } 25 | 26 | protected set => _nativeClient = value; 27 | } 28 | 29 | public async Task InitializeAsync(CancellationToken token = default) 30 | { 31 | if (!IsInitialized) 32 | { 33 | await InitializeAsyncInternal(token); 34 | 35 | IsInitialized = true; 36 | } 37 | } 38 | 39 | public void Dispose() 40 | { 41 | if (_disposed) 42 | { 43 | return; 44 | } 45 | 46 | _disposed = true; 47 | DisposeInternal(); 48 | } 49 | 50 | public ValueTask DisposeAsync() 51 | { 52 | if (_disposed) 53 | { 54 | return new ValueTask(Task.CompletedTask); 55 | } 56 | 57 | _disposed = true; 58 | return DisposeAsyncInternal(); 59 | } 60 | 61 | protected abstract Task InitializeAsyncInternal(CancellationToken token = default); 62 | 63 | protected abstract ValueTask DisposeAsyncInternal(); 64 | protected abstract void DisposeInternal(); 65 | public abstract Task DeleteAsync(CancellationToken token = default); 66 | } 67 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/BaseDatabaseCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using ManagedCode.Database.Core.Exceptions; 6 | 7 | namespace ManagedCode.Database.Core; 8 | 9 | public abstract class BaseDatabaseCollection : IDatabaseCollection 10 | where TItem : IItem 11 | { 12 | public abstract ICollectionQueryable Query { get; } 13 | 14 | public abstract void Dispose(); 15 | 16 | public abstract ValueTask DisposeAsync(); 17 | 18 | public Task InsertAsync(TItem item, CancellationToken cancellationToken = default) 19 | { 20 | return ExecuteAsync(InsertInternalAsync(item, cancellationToken)); 21 | } 22 | 23 | public Task InsertAsync(IEnumerable items, CancellationToken cancellationToken = default) 24 | { 25 | return ExecuteAsync(InsertInternalAsync(items, cancellationToken)); 26 | } 27 | 28 | public Task UpdateAsync(TItem item, CancellationToken cancellationToken = default) 29 | { 30 | return ExecuteAsync(UpdateInternalAsync(item, cancellationToken)); 31 | } 32 | 33 | public Task UpdateAsync(IEnumerable items, CancellationToken cancellationToken = default) 34 | { 35 | return ExecuteAsync(UpdateInternalAsync(items, cancellationToken)); 36 | } 37 | 38 | public Task InsertOrUpdateAsync(TItem item, CancellationToken cancellationToken = default) 39 | { 40 | return ExecuteAsync(InsertOrUpdateInternalAsync(item, cancellationToken)); 41 | } 42 | 43 | public Task InsertOrUpdateAsync(IEnumerable items, CancellationToken cancellationToken = default) 44 | { 45 | return ExecuteAsync(InsertOrUpdateInternalAsync(items, cancellationToken)); 46 | } 47 | 48 | public Task DeleteAsync(TId id, CancellationToken cancellationToken = default) 49 | { 50 | return ExecuteAsync(DeleteInternalAsync(id, cancellationToken)); 51 | } 52 | 53 | public Task DeleteAsync(TItem item, CancellationToken cancellationToken = default) 54 | { 55 | return ExecuteAsync(DeleteInternalAsync(item, cancellationToken)); 56 | } 57 | 58 | public Task DeleteAsync(IEnumerable ids, CancellationToken cancellationToken = default) 59 | { 60 | return ExecuteAsync(DeleteInternalAsync(ids, cancellationToken)); 61 | } 62 | 63 | public Task DeleteAsync(IEnumerable items, CancellationToken cancellationToken = default) 64 | { 65 | return ExecuteAsync(DeleteInternalAsync(items, cancellationToken)); 66 | } 67 | 68 | public Task DeleteCollectionAsync(CancellationToken cancellationToken = default) 69 | { 70 | return ExecuteAsync(DeleteCollectionInternalAsync(cancellationToken)); 71 | } 72 | 73 | public Task GetAsync(TId id, CancellationToken cancellationToken = default) 74 | { 75 | return ExecuteAsync(GetInternalAsync(id, cancellationToken)); 76 | } 77 | 78 | public Task CountAsync(CancellationToken cancellationToken = default) 79 | { 80 | return ExecuteAsync(CountInternalAsync(cancellationToken)); 81 | } 82 | 83 | protected abstract Task InsertInternalAsync(TItem item, CancellationToken cancellationToken = default); 84 | 85 | protected abstract Task InsertInternalAsync(IEnumerable items, 86 | CancellationToken cancellationToken = default); 87 | 88 | protected abstract Task UpdateInternalAsync(TItem item, CancellationToken cancellationToken = default); 89 | 90 | protected abstract Task UpdateInternalAsync(IEnumerable items, 91 | CancellationToken cancellationToken = default); 92 | 93 | protected abstract Task InsertOrUpdateInternalAsync(TItem item, 94 | CancellationToken cancellationToken = default); 95 | 96 | protected abstract Task InsertOrUpdateInternalAsync(IEnumerable items, 97 | CancellationToken cancellationToken = default); 98 | 99 | protected abstract Task DeleteInternalAsync(TId id, CancellationToken cancellationToken = default); 100 | 101 | protected abstract Task DeleteInternalAsync(TItem item, CancellationToken cancellationToken = default); 102 | 103 | protected abstract Task DeleteInternalAsync(IEnumerable ids, 104 | CancellationToken cancellationToken = default); 105 | 106 | protected abstract Task DeleteInternalAsync(IEnumerable items, 107 | CancellationToken cancellationToken = default); 108 | 109 | protected abstract Task DeleteCollectionInternalAsync(CancellationToken cancellationToken = default); 110 | 111 | protected abstract Task GetInternalAsync(TId id, CancellationToken cancellationToken = default); 112 | 113 | protected abstract Task CountInternalAsync(CancellationToken cancellationToken = default); 114 | 115 | private static async Task ExecuteAsync(Task task) 116 | { 117 | try 118 | { 119 | return await task; 120 | } 121 | catch (Exception exception) when (exception is not NotImplementedException or DatabaseException) 122 | { 123 | throw new DatabaseException(exception.Message, exception); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/Common/DatabaseNotInitializedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ManagedCode.Database.Core.Common 4 | { 5 | public class DatabaseNotInitializedException : Exception 6 | { 7 | public DatabaseNotInitializedException(Type type) : base($"Database {type} is not Initialized") 8 | { 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/Exceptions/DatabaseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ManagedCode.Database.Core.Exceptions 4 | { 5 | public class DatabaseException : Exception 6 | { 7 | public DatabaseException() 8 | { 9 | } 10 | 11 | public DatabaseException(string message) 12 | : base(message) 13 | { 14 | } 15 | 16 | public DatabaseException(string message, Exception inner) 17 | : base(message, inner) 18 | { 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/Extensions/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace ManagedCode.Database.Core.Extensions 4 | { 5 | public static class ServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddManagedCodeRepository(this IServiceCollection serviceCollection) 8 | { 9 | serviceCollection.AddSingleton(new ServiceCollectionHolder(serviceCollection)); 10 | 11 | return serviceCollection; 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/Extensions/ServiceCollectionHolder.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace ManagedCode.Database.Core.Extensions 4 | { 5 | public class ServiceCollectionHolder 6 | { 7 | public ServiceCollectionHolder(IServiceCollection serviceCollection) 8 | { 9 | ServiceCollection = serviceCollection; 10 | } 11 | 12 | public IServiceCollection ServiceCollection { get; } 13 | } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ManagedCode.Database.Core.Extensions 4 | { 5 | public static class TypeExtensions 6 | { 7 | public static bool EqualsToGeneric(this Type? type, Type baseType) 8 | { 9 | if (type != null) 10 | { 11 | return type.Name.Equals(baseType.Name); 12 | } 13 | 14 | return false; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/ICollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace ManagedCode.Database.Core 5 | { 6 | public interface ICollectionQueryable : ICollectionQueryableResultAsync 7 | { 8 | ICollectionQueryable Where(Expression> predicate); 9 | IOrderedCollectionQueryable OrderBy(Expression> keySelector); 10 | IOrderedCollectionQueryable OrderByDescending(Expression> keySelector); 11 | ICollectionQueryable Take(int? count); 12 | ICollectionQueryable Skip(int count); 13 | } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/ICollectionQueryableResultAsync.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace ManagedCode.Database.Core 6 | { 7 | public interface ICollectionQueryableResultAsync 8 | { 9 | IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default); 10 | Task FirstOrDefaultAsync(CancellationToken cancellationToken = default); 11 | Task> ToListAsync(CancellationToken cancellationToken = default); 12 | Task CountAsync(CancellationToken cancellationToken = default); 13 | Task DeleteAsync(CancellationToken cancellationToken = default); 14 | } 15 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/IDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace ManagedCode.Database.Core 6 | { 7 | public interface IDatabase : IDisposable, IAsyncDisposable 8 | { 9 | bool IsInitialized { get; } 10 | 11 | Task InitializeAsync(CancellationToken token = default); 12 | 13 | Task DeleteAsync(CancellationToken token = default); 14 | } 15 | 16 | public interface IDatabase : IDatabase 17 | { 18 | T NativeClient { get; } 19 | } 20 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/IDatabaseCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace ManagedCode.Database.Core; 7 | 8 | public interface IDatabaseCollection : IDisposable, IAsyncDisposable where TItem : IItem 9 | { 10 | ICollectionQueryable Query { get; } 11 | 12 | Task InsertAsync(TItem item, CancellationToken cancellationToken = default); 13 | Task InsertAsync(IEnumerable items, CancellationToken cancellationToken = default); 14 | 15 | Task UpdateAsync(TItem item, CancellationToken cancellationToken = default); 16 | Task UpdateAsync(IEnumerable items, CancellationToken cancellationToken = default); 17 | 18 | Task InsertOrUpdateAsync(TItem item, CancellationToken cancellationToken = default); 19 | Task InsertOrUpdateAsync(IEnumerable items, CancellationToken cancellationToken = default); 20 | 21 | Task DeleteAsync(TId id, CancellationToken cancellationToken = default); 22 | Task DeleteAsync(TItem item, CancellationToken cancellationToken = default); 23 | Task DeleteAsync(IEnumerable ids, CancellationToken cancellationToken = default); 24 | Task DeleteAsync(IEnumerable items, CancellationToken cancellationToken = default); 25 | 26 | Task DeleteCollectionAsync(CancellationToken cancellationToken = default); 27 | 28 | Task GetAsync(TId id, CancellationToken cancellationToken = default); 29 | 30 | Task CountAsync(CancellationToken cancellationToken = default); 31 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/IItem.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.Core 2 | { 3 | public interface IItem 4 | { 5 | TId Id { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/IOrderedCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace ManagedCode.Database.Core 5 | { 6 | public interface IOrderedCollectionQueryable : ICollectionQueryable 7 | { 8 | IOrderedCollectionQueryable ThenBy(Expression> keySelector); 9 | IOrderedCollectionQueryable ThenByDescending(Expression> keySelector); 10 | } 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/InMemory/InMemoryCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using System.Timers; 8 | 9 | namespace ManagedCode.Database.Core.InMemory 10 | { 11 | public class InMemoryCollectionQueryable : BaseCollectionQueryable where TId : notnull 12 | { 13 | private readonly ConcurrentDictionary _storage; 14 | 15 | public InMemoryCollectionQueryable(ConcurrentDictionary storage) 16 | { 17 | _storage = storage; 18 | } 19 | 20 | private IEnumerable> GetItemsInternal() 21 | { 22 | IEnumerable> items = _storage.AsEnumerable(); 23 | 24 | foreach (var query in Predicates) 25 | { 26 | switch(query.QueryType) 27 | { 28 | case QueryType.Where: 29 | items = items.Where(x => query.ExpressionBool.Compile().Invoke(x.Value)); 30 | break; 31 | 32 | case QueryType.OrderBy: 33 | if (items is IOrderedEnumerable>) 34 | { 35 | throw new InvalidOperationException("After OrderBy call ThenBy."); 36 | } 37 | 38 | items = items.OrderBy(x => query.ExpressionObject.Compile().Invoke(x.Value)); 39 | break; 40 | 41 | case QueryType.OrderByDescending: 42 | if (items is IOrderedEnumerable>) 43 | { 44 | throw new InvalidOperationException("After OrderBy call ThenBy."); 45 | } 46 | 47 | items = items.OrderByDescending(x => query.ExpressionObject.Compile().Invoke(x.Value)); 48 | break; 49 | 50 | case QueryType.ThenBy: 51 | if (items is IOrderedEnumerable> orderedItems) 52 | { 53 | items = orderedItems.ThenBy(x => query.ExpressionObject.Compile().Invoke(x.Value)); 54 | break; 55 | } 56 | 57 | throw new InvalidOperationException("Before ThenBy call first OrderBy."); 58 | 59 | case QueryType.ThenByDescending: 60 | if (items is IOrderedEnumerable> orderedDescendingItems) 61 | { 62 | items = orderedDescendingItems.ThenByDescending(x => query.ExpressionObject.Compile().Invoke(x.Value)); 63 | break; 64 | } 65 | 66 | throw new InvalidOperationException("Before ThenBy call first OrderBy."); 67 | 68 | case QueryType.Take: 69 | if (query.Count.HasValue) 70 | { 71 | items = items.Take(query.Count.Value); 72 | } 73 | break; 74 | 75 | case QueryType.Skip: 76 | if(query.Count.HasValue) 77 | { 78 | items = items.Skip(query.Count.Value); 79 | } 80 | break; 81 | 82 | default: 83 | break; 84 | } 85 | } 86 | 87 | return items; 88 | 89 | } 90 | 91 | public override async IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default) 92 | { 93 | await Task.Yield(); 94 | 95 | foreach (var item in GetItemsInternal()) 96 | { 97 | yield return item.Value; 98 | } 99 | } 100 | 101 | public override Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 102 | { 103 | return Task.FromResult(GetItemsInternal().FirstOrDefault().Value); 104 | } 105 | 106 | public override Task CountAsync(CancellationToken cancellationToken = default) 107 | { 108 | return Task.FromResult((long)GetItemsInternal().Count()); 109 | } 110 | 111 | public override Task DeleteAsync(CancellationToken cancellationToken = default) 112 | { 113 | int count = 0; 114 | foreach (var item in GetItemsInternal()) 115 | { 116 | _storage.Remove(item.Key, out _); 117 | count++; 118 | } 119 | 120 | return Task.FromResult(count); 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/InMemory/InMemoryDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using ManagedCode.Database.Core.Common; 7 | 8 | namespace ManagedCode.Database.Core.InMemory 9 | { 10 | public class InMemoryDatabase : BaseDatabase> 11 | { 12 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 13 | { 14 | NativeClient = new ConcurrentDictionary(); 15 | 16 | return Task.CompletedTask; 17 | } 18 | 19 | protected override ValueTask DisposeAsyncInternal() 20 | { 21 | DisposeInternal(); 22 | return new ValueTask(Task.CompletedTask); 23 | } 24 | 25 | protected override void DisposeInternal() 26 | { 27 | foreach (var item in NativeClient) 28 | { 29 | item.Value.Dispose(); 30 | } 31 | 32 | NativeClient.Clear(); 33 | } 34 | 35 | public InMemoryDatabaseCollection GetCollection() where TItem : IItem 36 | { 37 | return GetCollection(typeof(TItem).FullName); 38 | } 39 | 40 | public InMemoryDatabaseCollection GetCollection(string name) where TItem : IItem 41 | { 42 | if (!IsInitialized) 43 | { 44 | throw new DatabaseNotInitializedException(GetType()); 45 | } 46 | 47 | if (NativeClient.TryGetValue(name, out var table)) 48 | { 49 | return (InMemoryDatabaseCollection)table; 50 | } 51 | 52 | var db = new InMemoryDatabaseCollection(); 53 | NativeClient[name] = db; 54 | return db; 55 | } 56 | 57 | public override Task DeleteAsync(CancellationToken token = default) 58 | { 59 | DisposeInternal(); 60 | return Task.CompletedTask; 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Core/ManagedCode.Database.Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | embedded 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | ManagedCode.Repository.Core 16 | ManagedCode.Database.Core 17 | Repository base package 18 | managedcode, repository 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/CosmosCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using ManagedCode.Database.Core; 7 | using Microsoft.Azure.Cosmos; 8 | using Microsoft.Azure.Cosmos.Linq; 9 | 10 | namespace ManagedCode.Database.Cosmos; 11 | 12 | public class CosmosCollectionQueryable : BaseCollectionQueryable 13 | where TItem : CosmosItem, IItem, new() 14 | { 15 | private readonly Container _container; 16 | private readonly IQueryable _query; 17 | 18 | public CosmosCollectionQueryable(Container container, IQueryable query) 19 | { 20 | _container = container; 21 | _query = query; 22 | } 23 | 24 | public override async IAsyncEnumerable ToAsyncEnumerable( 25 | [EnumeratorCancellation] CancellationToken cancellationToken = default) 26 | { 27 | var feedIterator = ApplyPredicates(Predicates).ToFeedIterator(); 28 | 29 | using (var iterator = feedIterator) 30 | { 31 | while (iterator.HasMoreResults) 32 | { 33 | cancellationToken.ThrowIfCancellationRequested(); 34 | 35 | foreach (var item in await iterator.ReadNextAsync(cancellationToken)) 36 | yield return item; 37 | } 38 | } 39 | } 40 | 41 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 42 | { 43 | using var queryIterator = ApplyPredicates(Predicates).ToFeedIterator(); 44 | var response = await queryIterator.ReadNextAsync(); 45 | return response.FirstOrDefault(); 46 | } 47 | 48 | public override async Task CountAsync(CancellationToken cancellationToken = default) 49 | { 50 | using var query = ApplyPredicates(Predicates).ToFeedIterator(); 51 | var result = await query.ReadNextAsync(); 52 | return result.Count(); 53 | } 54 | 55 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 56 | { 57 | var count = 0; 58 | 59 | var asyncEnumerable = ToAsyncEnumerable(cancellationToken); 60 | 61 | await Parallel.ForEachAsync(asyncEnumerable, cancellationToken, async (item, token) => 62 | { 63 | await _container.DeleteItemAsync(item.Id, item.PartitionKey, cancellationToken: token); 64 | Interlocked.Increment(ref count); 65 | }); 66 | 67 | return count; 68 | } 69 | 70 | private IQueryable ApplyPredicates(IEnumerable predicates) 71 | { 72 | var query = _query; 73 | 74 | foreach (var predicate in predicates) 75 | query = predicate.QueryType switch 76 | { 77 | QueryType.Where => query.Where(predicate.ExpressionBool), 78 | QueryType.OrderBy => query.OrderBy(predicate.ExpressionObject), 79 | QueryType.OrderByDescending => query.OrderByDescending(predicate.ExpressionObject), 80 | QueryType.ThenBy => (query as IOrderedQueryable)!.ThenBy(predicate.ExpressionObject), 81 | QueryType.ThenByDescending => (query as IOrderedQueryable)! 82 | .ThenByDescending(predicate.ExpressionObject), 83 | QueryType.Take => predicate.Count.HasValue ? query.Take(predicate.Count.Value) : query, 84 | QueryType.Skip => query.Skip(predicate.Count!.Value), 85 | _ => query 86 | }; 87 | 88 | return query; 89 | } 90 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/CosmosDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ManagedCode.Database.Core; 5 | using ManagedCode.Database.Core.Common; 6 | using Microsoft.Azure.Cosmos; 7 | 8 | namespace ManagedCode.Database.Cosmos; 9 | 10 | public class CosmosDatabase : BaseDatabase 11 | { 12 | private readonly CosmosOptions _options; 13 | 14 | private Container? _container; 15 | private Microsoft.Azure.Cosmos.Database? _database; 16 | 17 | public CosmosDatabase(CosmosOptions options) 18 | { 19 | _options = options; 20 | } 21 | 22 | protected override async Task InitializeAsyncInternal(CancellationToken token = default) 23 | { 24 | var cosmosClient = new CosmosClient(_options.ConnectionString, _options.CosmosClientOptions); 25 | 26 | if (_options.MaxRetryAttemptsOnRateLimitedRequests.HasValue) 27 | { 28 | cosmosClient.ClientOptions.MaxRetryAttemptsOnRateLimitedRequests = _options.MaxRetryAttemptsOnRateLimitedRequests.Value; 29 | } 30 | if (_options.MaxRetryWaitTimeOnRateLimitedRequests.HasValue) 31 | { 32 | cosmosClient.ClientOptions.MaxRetryWaitTimeOnRateLimitedRequests = _options.MaxRetryWaitTimeOnRateLimitedRequests.Value; 33 | } 34 | 35 | 36 | if (_options.AllowTableCreation) 37 | { 38 | _database = await cosmosClient.CreateDatabaseIfNotExistsAsync(_options.DatabaseName, cancellationToken: token); 39 | _container = await _database.CreateContainerIfNotExistsAsync(_options.CollectionName, _options.PartitionKey, cancellationToken: token); 40 | } 41 | 42 | var database = cosmosClient.GetDatabase(_options.DatabaseName); 43 | 44 | if (database is null) 45 | throw new InvalidOperationException($"Database '{_options.DatabaseName}' does not exist."); 46 | 47 | var container = database.GetContainer(_options.CollectionName); 48 | 49 | if (container is null) 50 | throw new Exception($"Container '{_options.CollectionName}' does not exist."); 51 | 52 | _database = database; 53 | _container = container; 54 | NativeClient = cosmosClient; 55 | } 56 | 57 | protected override ValueTask DisposeAsyncInternal() 58 | { 59 | DisposeInternal(); 60 | return ValueTask.CompletedTask; 61 | } 62 | 63 | protected override void DisposeInternal() 64 | { 65 | NativeClient?.Dispose(); 66 | _database = null; 67 | _container = null; 68 | IsInitialized = false; 69 | } 70 | 71 | public CosmosCollection GetCollection() where TItem : CosmosItem, new() 72 | { 73 | if (!IsInitialized) 74 | throw new DatabaseNotInitializedException(GetType()); 75 | 76 | return new CosmosCollection(_options, _container!); 77 | } 78 | 79 | public override async Task DeleteAsync(CancellationToken token = default) 80 | { 81 | if (_database is not null) 82 | { 83 | await _database.DeleteAsync(cancellationToken: token); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/CosmosItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.Core; 3 | using Microsoft.Azure.Cosmos; 4 | using Newtonsoft.Json; 5 | 6 | namespace ManagedCode.Database.Cosmos; 7 | 8 | public class CosmosItem : IItem 9 | { 10 | public CosmosItem() 11 | { 12 | Id = $"{Guid.NewGuid():N}"; 13 | Type = GetType().Name; 14 | } 15 | 16 | public CosmosItem(string id) 17 | { 18 | Id = id; 19 | Type = GetType().Name; 20 | } 21 | 22 | [JsonProperty("type")] public string Type { get; set; } 23 | 24 | public PartitionKey PartitionKey { get; protected set; } 25 | 26 | [JsonProperty("id")] public string Id { get; set; } 27 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/CosmosOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Azure.Cosmos; 3 | 4 | namespace ManagedCode.Database.Cosmos; 5 | 6 | public class CosmosOptions 7 | { 8 | public string ConnectionString { get; set; } 9 | public string DatabaseName { get; set; } 10 | public string CollectionName { get; set; } 11 | 12 | public string PartitionKey { get; set; } = "/id"; 13 | public CosmosClientOptions CosmosClientOptions { get; set; } 14 | public bool SplitByType { get; set; } = true; 15 | public bool UseItemIdAsPartitionKey { get; set; } = true; 16 | public bool AllowTableCreation { get; set; } 17 | 18 | public TimeSpan? MaxRetryWaitTimeOnRateLimitedRequests { get; set; } 19 | public int? MaxRetryAttemptsOnRateLimitedRequests { get; set; } 20 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Database.Cosmos.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddCosmosDb(this IServiceCollection serviceCollection, 9 | Action action) 10 | { 11 | var connectionOptions = new CosmosOptions(); 12 | action.Invoke(connectionOptions); 13 | 14 | serviceCollection.AddSingleton(connectionOptions); 15 | 16 | return serviceCollection; 17 | } 18 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Cosmos/ManagedCode.Database.Cosmos.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | embedded 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | ManagedCode.Repository.Cosmos 16 | ManagedCode.Database.Cosmos 17 | Repository for Cosmos DB 18 | managedcode, repository, Cosmos 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ManagedCode.Database.DynamoDB/DynamoDBCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using Amazon.DynamoDBv2; 2 | using Amazon.DynamoDBv2.DataModel; 3 | using Amazon.DynamoDBv2.Model; 4 | using ManagedCode.Database.Core; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace ManagedCode.Database.DynamoDB; 11 | 12 | public class DynamoDBCollectionQueryable : BaseCollectionQueryable where TItem : class, IItem 13 | { 14 | private readonly DynamoDBContext _dynamoDBContext; 15 | private readonly DynamoDBOperationConfig _config; 16 | private readonly AmazonDynamoDBClient _dynamoDBClient; 17 | private readonly string _tableName; 18 | 19 | public DynamoDBCollectionQueryable(DynamoDBContext dynamoDBContext, AmazonDynamoDBClient dynamoDBClient, string tableName) 20 | { 21 | _dynamoDBContext = dynamoDBContext; 22 | _dynamoDBClient = dynamoDBClient; 23 | _tableName = tableName; 24 | _config = new DynamoDBOperationConfig 25 | { 26 | OverrideTableName = tableName, 27 | }; 28 | } 29 | 30 | public override async IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default) 31 | { 32 | var data = await _dynamoDBContext.ScanAsync(null, _config).GetRemainingAsync(); 33 | 34 | await Task.Yield(); 35 | 36 | foreach (var item in ApplyPredicates(Predicates, data.AsQueryable())) 37 | { 38 | cancellationToken.ThrowIfCancellationRequested(); 39 | 40 | yield return item; 41 | } 42 | } 43 | 44 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 45 | { 46 | var data = await _dynamoDBContext.ScanAsync(null, _config).GetRemainingAsync(); 47 | 48 | var query = ApplyPredicates(Predicates, data.AsQueryable()); 49 | 50 | return await Task.Run(() => query.FirstOrDefault(), cancellationToken); 51 | 52 | } 53 | 54 | public override async Task CountAsync(CancellationToken cancellationToken = default) 55 | { 56 | var data = await _dynamoDBContext.ScanAsync(null, _config).GetRemainingAsync(); 57 | 58 | var query = ApplyPredicates(Predicates, data.AsQueryable()); 59 | 60 | return await Task.Run(() => query.LongCount(), cancellationToken); 61 | } 62 | 63 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 64 | { 65 | var data = await _dynamoDBContext.ScanAsync(null, _config).GetRemainingAsync(); 66 | 67 | var items = ApplyPredicates(Predicates, data.AsQueryable()); 68 | 69 | var count = 0; 70 | 71 | IEnumerable itemsChunk = items.Chunk(100); 72 | 73 | foreach (var itemsList in itemsChunk) 74 | { 75 | var tasks = new List(); 76 | 77 | foreach (var item in itemsList) 78 | { 79 | tasks.Add(Task.Run(async () => 80 | { 81 | var response = await _dynamoDBClient.DeleteItemAsync(DeleteItemRequestById(item.Id), cancellationToken); 82 | 83 | if (response.Attributes.Count != 0 && response.HttpStatusCode == System.Net.HttpStatusCode.OK) 84 | Interlocked.Increment(ref count); 85 | })); 86 | } 87 | 88 | await Task.WhenAll(tasks); 89 | } 90 | 91 | return count; 92 | } 93 | 94 | private IQueryable ApplyPredicates(List predicates, IQueryable query) 95 | { 96 | foreach (var predicate in predicates) 97 | query = predicate.QueryType switch 98 | { 99 | QueryType.Where => query.Where(x => predicate.ExpressionBool.Compile().Invoke(x)), 100 | QueryType.OrderBy => query.OrderBy(x => predicate.ExpressionObject.Compile().Invoke(x)), 101 | QueryType.OrderByDescending => query.OrderByDescending(x => predicate.ExpressionObject.Compile().Invoke(x)), 102 | QueryType.ThenBy => (query as IOrderedQueryable).ThenBy(predicate.ExpressionObject), 103 | QueryType.ThenByDescending => (query as IOrderedQueryable) 104 | .ThenByDescending(predicate.ExpressionObject), 105 | QueryType.Take => predicate.Count.HasValue ? query.Take(predicate.Count.Value) : query, 106 | QueryType.Skip => query.Skip(predicate.Count!.Value), 107 | _ => query 108 | }; 109 | 110 | return query; 111 | } 112 | 113 | private DeleteItemRequest DeleteItemRequestById(string id) 114 | { 115 | return new DeleteItemRequest 116 | { 117 | TableName = _tableName, 118 | Key = new Dictionary() 119 | { 120 | {"Id", new AttributeValue() {S = id}} 121 | }, 122 | ReturnValues = "ALL_OLD", 123 | }; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /ManagedCode.Database.DynamoDB/DynamoDBDatabase.cs: -------------------------------------------------------------------------------- 1 | using Amazon.DynamoDBv2; 2 | using Amazon.DynamoDBv2.DataModel; 3 | using Amazon.DynamoDBv2.DocumentModel; 4 | using Amazon.DynamoDBv2.Model; 5 | using Amazon.Runtime; 6 | using ManagedCode.Database.Core; 7 | using ManagedCode.Database.Core.Common; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Linq; 11 | using System.Text; 12 | using System.Threading; 13 | using System.Threading.Tasks; 14 | using Humanizer; 15 | 16 | namespace ManagedCode.Database.DynamoDB; 17 | 18 | public class DynamoDBDatabase : BaseDatabase 19 | { 20 | private readonly Dictionary _collections = new(); 21 | 22 | private readonly DynamoDBOptions _dbOptions; 23 | private DynamoDBContext _dynamoDBContext; 24 | private AmazonDynamoDBClient _dynamoDBClient; 25 | 26 | public DynamoDBDatabase(DynamoDBOptions dbOptions) 27 | { 28 | _dbOptions = dbOptions; 29 | } 30 | 31 | public override Task DeleteAsync(CancellationToken token = default) 32 | { 33 | throw new NotImplementedException(); 34 | } 35 | 36 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 37 | { 38 | var creds = new BasicAWSCredentials(_dbOptions.AccessKey, _dbOptions.SecretKey); 39 | var config = new AmazonDynamoDBConfig() 40 | { 41 | ServiceURL = _dbOptions.ServiceURL, 42 | AuthenticationRegion = _dbOptions.AuthenticationRegion, 43 | }; 44 | _dynamoDBClient = new AmazonDynamoDBClient(creds, config); 45 | _dynamoDBContext = new DynamoDBContext(_dynamoDBClient); 46 | 47 | return Task.CompletedTask; 48 | } 49 | 50 | protected override ValueTask DisposeAsyncInternal() 51 | { 52 | return new ValueTask(Task.CompletedTask); 53 | } 54 | 55 | protected override void DisposeInternal() 56 | { 57 | } 58 | 59 | public async Task SetupAsync(string tableName) 60 | { 61 | var createTableRequest = new CreateTableRequest 62 | { 63 | TableName = tableName, 64 | AttributeDefinitions = new List(), 65 | KeySchema = new List(), 66 | GlobalSecondaryIndexes = new List(), 67 | LocalSecondaryIndexes = new List(), 68 | ProvisionedThroughput = new ProvisionedThroughput 69 | { 70 | ReadCapacityUnits = 1, 71 | WriteCapacityUnits = 1 72 | } 73 | }; 74 | createTableRequest.KeySchema = new[] { 75 | new KeySchemaElement { 76 | AttributeName = "Id", 77 | KeyType = KeyType.HASH, 78 | }, 79 | 80 | }.ToList(); 81 | 82 | createTableRequest.AttributeDefinitions = new[] { 83 | new AttributeDefinition { 84 | AttributeName = "Id", 85 | AttributeType = ScalarAttributeType.S, 86 | }, 87 | }.ToList(); 88 | 89 | var createTableResponse = await _dynamoDBClient.CreateTableAsync(createTableRequest); 90 | 91 | return createTableResponse; 92 | } 93 | 94 | public DynamoDBCollection GetCollection() 95 | where TItem : DynamoDBItem, new() 96 | { 97 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 98 | 99 | var tableName = string.IsNullOrEmpty(_dbOptions.CollectionName) 100 | ? typeof(TItem).Name.Pluralize() 101 | : _dbOptions.CollectionName; 102 | 103 | try 104 | { 105 | Table.LoadTable(_dynamoDBClient, tableName); //TODO Edit check created table 106 | } 107 | catch 108 | { 109 | SetupAsync(tableName).GetAwaiter().GetResult(); 110 | } 111 | 112 | return new DynamoDBCollection(_dynamoDBContext, _dynamoDBClient, tableName); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ManagedCode.Database.DynamoDB/DynamoDBItem.cs: -------------------------------------------------------------------------------- 1 | using Amazon.DynamoDBv2.DataModel; 2 | using ManagedCode.Database.Core; 3 | 4 | namespace ManagedCode.Database.DynamoDB 5 | { 6 | public class DynamoDBItem : IItem 7 | { 8 | [DynamoDBHashKey] 9 | public TId Id { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.DynamoDB/DynamoDBOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ManagedCode.Database.DynamoDB; 4 | 5 | public class DynamoDBOptions 6 | { 7 | public string ServiceURL { get; set; } 8 | public string AuthenticationRegion { get; set; } 9 | public string AccessKey { get; set; } 10 | public string SecretKey { get; set; } 11 | public string DataBaseName { get; set; } 12 | public string CollectionName { get; set; } 13 | } 14 | -------------------------------------------------------------------------------- /ManagedCode.Database.DynamoDB/ManagedCode.Database.DynamoDB.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | true 6 | True 7 | 11 8 | embedded 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | ManagedCode.Repository.DynamoDB 16 | ManagedCode.Database.DynamoDB 17 | Repository for DynamoDB 18 | managedcode, repository, DynamoDB 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Database.LiteDB.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddLiteDb(this IServiceCollection serviceCollection, Action action) 9 | { 10 | var connectionOptions = new LiteDBOptions(); 11 | action.Invoke(connectionOptions); 12 | 13 | serviceCollection.AddSingleton(connectionOptions); 14 | 15 | return serviceCollection; 16 | } 17 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/LiteDBCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using LiteDB; 6 | using ManagedCode.Database.Core; 7 | using ManagedCode.Database.Core.Exceptions; 8 | 9 | namespace ManagedCode.Database.LiteDB; 10 | 11 | public class LiteDBCollection : BaseDatabaseCollection 12 | where TItem : LiteDBItem, IItem, new() 13 | { 14 | private readonly ILiteCollection _collection; 15 | 16 | public LiteDBCollection(ILiteCollection collection) 17 | { 18 | _collection = collection; 19 | } 20 | 21 | public override ICollectionQueryable Query => new LiteDBCollectionQueryable(_collection); 22 | 23 | public override ValueTask DisposeAsync() 24 | { 25 | return new ValueTask(Task.CompletedTask); 26 | } 27 | 28 | public override void Dispose() 29 | { 30 | } 31 | 32 | #region Get 33 | 34 | protected override async Task GetInternalAsync(TId id, CancellationToken cancellationToken = default) 35 | { 36 | await Task.Yield(); 37 | return _collection.FindById(new BsonValue(id)); 38 | } 39 | 40 | #endregion 41 | 42 | #region Count 43 | 44 | protected override async Task CountInternalAsync(CancellationToken cancellationToken = default) 45 | { 46 | return await Task.Run(() => _collection.Count(), cancellationToken); 47 | } 48 | 49 | #endregion 50 | 51 | #region Insert 52 | 53 | protected override async Task InsertInternalAsync(TItem item, CancellationToken cancellationToken = default) 54 | { 55 | await Task.Yield(); 56 | 57 | var bson = _collection.Insert(item); 58 | return _collection.FindById(bson); 59 | } 60 | 61 | protected override async Task InsertInternalAsync(IEnumerable items, 62 | CancellationToken cancellationToken = default) 63 | { 64 | return await Task.Run(() => _collection.InsertBulk(items), cancellationToken); 65 | } 66 | 67 | #endregion 68 | 69 | #region InsertOrUpdate 70 | 71 | protected override async Task InsertOrUpdateInternalAsync(TItem item, 72 | CancellationToken cancellationToken = default) 73 | { 74 | return await Task.Run(() => 75 | { 76 | _collection.Upsert(item); 77 | return _collection.FindById(new BsonValue(item.Id)); 78 | }, cancellationToken); 79 | } 80 | 81 | protected override async Task InsertOrUpdateInternalAsync(IEnumerable items, 82 | CancellationToken cancellationToken = default) 83 | { 84 | return await Task.Run(() => _collection.Upsert(items), cancellationToken); 85 | } 86 | 87 | #endregion 88 | 89 | #region Update 90 | 91 | protected override async Task UpdateInternalAsync(TItem item, CancellationToken cancellationToken = default) 92 | { 93 | await Task.Yield(); 94 | var isUpdated = _collection.Update(item); 95 | 96 | if (!isUpdated) 97 | { 98 | throw new DatabaseException("Entity not found in collection."); 99 | } 100 | 101 | return _collection.FindById(new BsonValue(item.Id)); 102 | } 103 | 104 | protected override async Task UpdateInternalAsync(IEnumerable items, 105 | CancellationToken cancellationToken = default) 106 | { 107 | await Task.Yield(); 108 | return await Task.Run(() => _collection.Update(items), cancellationToken); 109 | } 110 | 111 | #endregion 112 | 113 | #region Delete 114 | 115 | protected override async Task DeleteInternalAsync(TId id, CancellationToken cancellationToken = default) 116 | { 117 | await Task.Yield(); 118 | return _collection.Delete(new BsonValue(id)); 119 | } 120 | 121 | protected override async Task DeleteInternalAsync(TItem item, CancellationToken cancellationToken = default) 122 | { 123 | await Task.Yield(); 124 | return _collection.Delete(new BsonValue(item.Id)); 125 | } 126 | 127 | protected override async Task DeleteInternalAsync(IEnumerable ids, 128 | CancellationToken cancellationToken = default) 129 | { 130 | return await Task.Run(() => ids.Count(id => _collection.Delete(new BsonValue(id))), cancellationToken); 131 | } 132 | 133 | protected override async Task DeleteInternalAsync(IEnumerable items, 134 | CancellationToken cancellationToken = default) 135 | { 136 | return await Task.Run(() => items.Count(item => _collection.Delete(new BsonValue(item.Id))), 137 | cancellationToken); 138 | } 139 | 140 | protected override async Task DeleteCollectionInternalAsync(CancellationToken cancellationToken = default) 141 | { 142 | await Task.Run(() => _collection.DeleteAll(), cancellationToken); 143 | return await CountInternalAsync(cancellationToken) == 0; 144 | } 145 | 146 | #endregion 147 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/LiteDBCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Linq.Expressions; 5 | using System.Runtime.CompilerServices; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | using LiteDB; 9 | using ManagedCode.Database.Core; 10 | 11 | namespace ManagedCode.Database.LiteDB; 12 | 13 | public class LiteDBCollectionQueryable : BaseCollectionQueryable 14 | where TItem : LiteDBItem, IItem, new() 15 | { 16 | private readonly ILiteCollection _collection; 17 | 18 | public LiteDBCollectionQueryable(ILiteCollection collection) 19 | { 20 | _collection = collection; 21 | } 22 | 23 | public override async IAsyncEnumerable ToAsyncEnumerable( 24 | [EnumeratorCancellation] CancellationToken cancellationToken = default) 25 | { 26 | await Task.Yield(); 27 | 28 | foreach (var item in ApplyPredicates(Predicates).ToEnumerable()) 29 | { 30 | cancellationToken.ThrowIfCancellationRequested(); 31 | yield return item; 32 | } 33 | } 34 | 35 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 36 | { 37 | var query = ApplyPredicates(Predicates); 38 | 39 | return await Task.Run(() => query.FirstOrDefault(), cancellationToken); 40 | } 41 | 42 | public override async Task CountAsync(CancellationToken cancellationToken = default) 43 | { 44 | var query = ApplyPredicates(Predicates); 45 | 46 | return await Task.Run(() => query.LongCount(), cancellationToken); 47 | } 48 | 49 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 50 | { 51 | var wherePredicates = Predicates 52 | .Where(p => p.QueryType is QueryType.Where) 53 | .ToList(); 54 | 55 | if (!wherePredicates.Any()) 56 | { 57 | return await Task.Run(() => _collection.DeleteAll(), cancellationToken); 58 | } 59 | 60 | var predicates = Predicates 61 | .Select(p => p.ExpressionBool) 62 | .Aggregate(CombineExpressions); 63 | 64 | return await Task.Run(() => _collection.DeleteMany(predicates), cancellationToken); 65 | } 66 | 67 | private ILiteQueryableResult ApplyPredicates(IEnumerable predicates) 68 | { 69 | // TODO: optimize 70 | var query = _collection.Query(); 71 | 72 | foreach (var predicate in predicates) 73 | query = predicate.QueryType switch 74 | { 75 | QueryType.Where => query.Where(predicate.ExpressionBool), 76 | QueryType.OrderBy => query.OrderBy(predicate.ExpressionObject), 77 | QueryType.OrderByDescending => query.OrderByDescending(predicate.ExpressionObject), 78 | QueryType.ThenBy => query.OrderBy(predicate.ExpressionObject), 79 | QueryType.ThenByDescending => query.OrderByDescending(predicate.ExpressionObject), 80 | _ => query 81 | }; 82 | 83 | ILiteQueryableResult queryableResult = query; 84 | 85 | foreach (var predicate in predicates) 86 | queryableResult = predicate.QueryType switch 87 | { 88 | QueryType.Take => predicate.Count.HasValue 89 | ? queryableResult.Limit(predicate.Count.Value) 90 | : queryableResult, 91 | 92 | QueryType.Skip => queryableResult.Skip(predicate.Count!.Value), 93 | _ => queryableResult 94 | }; 95 | 96 | return queryableResult; 97 | } 98 | 99 | private static Expression> CombineExpressions(Expression> expr1, 100 | Expression> expr2) 101 | { 102 | var body = Expression.AndAlso(expr1.Body, expr2.Body); 103 | return Expression.Lambda>(body, expr1.Parameters[0]); 104 | } 105 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/LiteDBDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using LiteDB; 5 | using ManagedCode.Database.Core; 6 | using ManagedCode.Database.Core.Common; 7 | 8 | namespace ManagedCode.Database.LiteDB; 9 | 10 | public class LiteDBDatabase : BaseDatabase 11 | { 12 | private readonly LiteDBOptions _options; 13 | 14 | public LiteDBDatabase(LiteDBOptions options) 15 | { 16 | _options = options; 17 | } 18 | 19 | public override async Task DeleteAsync(CancellationToken token = default) 20 | { 21 | await DisposeAsyncInternal(); 22 | 23 | var allCollections = NativeClient.GetCollectionNames(); 24 | foreach(var collection in allCollections) 25 | NativeClient.DropCollection(collection); 26 | } 27 | 28 | 29 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 30 | { 31 | NativeClient = _options.Database ?? new LiteDatabase(_options.ConnectionString); 32 | 33 | return Task.CompletedTask; 34 | } 35 | 36 | protected override ValueTask DisposeAsyncInternal() 37 | { 38 | NativeClient.Dispose(); 39 | return new ValueTask(Task.CompletedTask); 40 | } 41 | 42 | protected override void DisposeInternal() 43 | { 44 | NativeClient.Dispose(); 45 | } 46 | 47 | public LiteDBCollection GetCollection() where TItem : LiteDBItem, new() 48 | { 49 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 50 | 51 | return new LiteDBCollection(NativeClient.GetCollection()); 52 | } 53 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/LiteDBItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using Newtonsoft.Json; 3 | 4 | namespace ManagedCode.Database.LiteDB; 5 | 6 | public class LiteDBItem : IItem 7 | { 8 | [JsonProperty("_id")] public TId Id { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/LiteDBOptions.cs: -------------------------------------------------------------------------------- 1 | using LiteDB; 2 | 3 | namespace ManagedCode.Database.LiteDB; 4 | 5 | public class LiteDBOptions 6 | { 7 | public string ConnectionString { get; set; } 8 | public LiteDatabase? Database { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.LiteDB/ManagedCode.Database.LiteDB.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | embedded 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | ManagedCode.Repository.LiteDB 16 | ManagedCode.Database.LiteDB 17 | Repository for LiteDB 18 | managedcode, repository, LiteDB 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Database.MongoDB.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddMongoDb(this IServiceCollection serviceCollection, 9 | Action action) 10 | { 11 | var connectionOptions = new MongoDBOptions(); 12 | action.Invoke(connectionOptions); 13 | 14 | serviceCollection.AddSingleton(connectionOptions); 15 | 16 | return serviceCollection; 17 | } 18 | } -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/ManagedCode.Database.MongoDB.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | 9 | embedded 10 | true 11 | enable 12 | 13 | 14 | 15 | 16 | ManagedCode.Repository.MongoDB 17 | ManagedCode.Database.MongoDB 18 | Repository for MongoDB 19 | managedcode, repository, MongoDB 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/MongoDBCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using ManagedCode.Database.Core; 8 | using MongoDB.Bson; 9 | using MongoDB.Driver; 10 | using MongoDB.Driver.Linq; 11 | 12 | namespace ManagedCode.Database.MongoDB; 13 | 14 | public class MongoDBCollectionQueryable : BaseCollectionQueryable where TItem : class, IItem 15 | { 16 | private readonly IMongoCollection _collection; 17 | 18 | public MongoDBCollectionQueryable(IMongoCollection collection) 19 | { 20 | _collection = collection; 21 | } 22 | 23 | public override async IAsyncEnumerable ToAsyncEnumerable( 24 | [EnumeratorCancellation] CancellationToken cancellationToken = default) 25 | { 26 | await Task.Yield(); 27 | 28 | foreach (var item in ApplyPredicates(Predicates)) 29 | { 30 | cancellationToken.ThrowIfCancellationRequested(); 31 | 32 | yield return item; 33 | } 34 | } 35 | 36 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 37 | { 38 | var query = ApplyPredicates(Predicates); 39 | 40 | return await Task.Run(() => query.FirstOrDefault(), cancellationToken); 41 | } 42 | 43 | public override async Task CountAsync(CancellationToken cancellationToken = default) 44 | { 45 | var query = ApplyPredicates(Predicates); 46 | 47 | return await Task.Run(() => query.LongCount(), cancellationToken); 48 | } 49 | 50 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 51 | { 52 | var ids = 53 | ApplyPredicates(Predicates) 54 | .Select(d => d.Id); 55 | 56 | var filter = Builders.Filter.In(d => d.Id, ids); 57 | 58 | var result = await _collection.DeleteManyAsync(filter, cancellationToken); 59 | 60 | return Convert.ToInt32(result.DeletedCount); 61 | } 62 | 63 | private IMongoQueryable ApplyPredicates(IEnumerable predicates) 64 | { 65 | var query = _collection.AsQueryable(); 66 | 67 | foreach (var predicate in predicates) 68 | query = predicate.QueryType switch 69 | { 70 | QueryType.Where => query.Where(predicate.ExpressionBool), 71 | QueryType.OrderBy => query.OrderBy(predicate.ExpressionObject), 72 | QueryType.OrderByDescending => query.OrderByDescending(predicate.ExpressionObject), 73 | QueryType.ThenBy => (query as IOrderedMongoQueryable).ThenBy(predicate.ExpressionObject), 74 | QueryType.ThenByDescending => (query as IOrderedMongoQueryable) 75 | .ThenByDescending(predicate.ExpressionObject), 76 | QueryType.Take => predicate.Count.HasValue ? query.Take(predicate.Count.Value) : query, 77 | QueryType.Skip => query.Skip(predicate.Count!.Value), 78 | _ => query 79 | }; 80 | 81 | return query; 82 | } 83 | } -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/MongoDBDatabase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using Humanizer; 5 | using ManagedCode.Database.Core; 6 | using ManagedCode.Database.Core.Common; 7 | using MongoDB.Bson; 8 | using MongoDB.Driver; 9 | 10 | namespace ManagedCode.Database.MongoDB; 11 | 12 | public class MongoDBDatabase : BaseDatabase 13 | { 14 | private readonly MongoDBOptions _dbOptions; 15 | 16 | public MongoDBDatabase(MongoDBOptions dbOptions) 17 | { 18 | _dbOptions = dbOptions; 19 | } 20 | 21 | public override Task DeleteAsync(CancellationToken token = default) 22 | { 23 | throw new NotImplementedException(); 24 | } 25 | 26 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 27 | { 28 | var client = new MongoClient(_dbOptions.ConnectionString); 29 | NativeClient = client.GetDatabase(_dbOptions.DataBaseName); 30 | 31 | return Task.CompletedTask; 32 | } 33 | 34 | protected override ValueTask DisposeAsyncInternal() 35 | { 36 | return new ValueTask(Task.CompletedTask); 37 | } 38 | 39 | protected override void DisposeInternal() 40 | { 41 | } 42 | 43 | public MongoDBCollection GetCollection() where TItem : MongoDBItem, new() 44 | { 45 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 46 | 47 | var collectionName = string.IsNullOrEmpty(_dbOptions.CollectionName) 48 | ? typeof(TItem).Name.Pluralize() 49 | : _dbOptions.CollectionName; 50 | 51 | return new MongoDBCollection( 52 | NativeClient.GetCollection(collectionName, new MongoCollectionSettings())); 53 | } 54 | } -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/MongoDBItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using MongoDB.Bson; 3 | using MongoDB.Bson.Serialization.Attributes; 4 | 5 | namespace ManagedCode.Database.MongoDB; 6 | 7 | public class MongoDBItem : IItem 8 | { 9 | public MongoDBItem() 10 | { 11 | Id = ObjectId.GenerateNewId(); 12 | } 13 | 14 | public MongoDBItem(string id) 15 | { 16 | Id = ObjectId.Parse(id); 17 | } 18 | 19 | public MongoDBItem(ObjectId id) 20 | { 21 | Id = id; 22 | } 23 | 24 | [BsonId] 25 | [BsonRepresentation(BsonType.ObjectId)] 26 | public ObjectId Id { get; set; } 27 | } -------------------------------------------------------------------------------- /ManagedCode.Database.MongoDB/MongoDBOptions.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.MongoDB; 2 | 3 | public class MongoDBOptions 4 | { 5 | public string ConnectionString { get; set; } 6 | public string DataBaseName { get; set; } 7 | public string CollectionName { get; set; } 8 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/Extensions/ExpressionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace ManagedCode.Database.SQLite.Extensions; 5 | 6 | public static class ExpressionExtensions 7 | { 8 | public static Expression> CorrectExpression(this Expression> orderExpr) 9 | { 10 | LambdaExpression lambdaExpression = orderExpr.NodeType switch 11 | { 12 | ExpressionType.Lambda => orderExpr, 13 | _ => throw new NotSupportedException("Must be a predicate") 14 | }; 15 | 16 | var memberExpression = lambdaExpression.Body switch 17 | { 18 | UnaryExpression { NodeType: ExpressionType.Convert } body => body.Operand as MemberExpression, 19 | _ => lambdaExpression.Body as MemberExpression 20 | }; 21 | 22 | if (memberExpression is null) throw new NotSupportedException("Order By does not support: " + orderExpr); 23 | 24 | const string parameterName = "x"; 25 | 26 | var parameter = Expression.Parameter(typeof(T), parameterName); 27 | var property = Expression.Property(parameter, typeof(T), memberExpression.Member.Name); 28 | var convert = Expression.Convert(property, typeof(TU)); 29 | var lambda = Expression.Lambda>(convert, parameterName, new[] { parameter }); 30 | 31 | return lambda; 32 | } 33 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/Extensions/ProviderBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace ManagedCode.Database.SQLite.Extensions; 5 | 6 | public static class ProviderBuilderExtension 7 | { 8 | public static IServiceCollection AddSQLite(this IServiceCollection serviceCollection, 9 | Action action) 10 | { 11 | var connectionOptions = new SQLiteRepositoryOptions(); 12 | action.Invoke(connectionOptions); 13 | 14 | serviceCollection.AddSingleton(connectionOptions); 15 | 16 | return serviceCollection; 17 | } 18 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/ManagedCode.Database.SQLite.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | true 8 | embedded 9 | true 10 | enable 11 | 12 | 13 | 14 | 15 | ManagedCode.Repository.SQLite 16 | ManagedCode.Database.SQLite 17 | Repository for SQLite 18 | managedcode, repository, SQLite 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/SQLiteCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Runtime.CompilerServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using ManagedCode.Database.Core; 7 | using ManagedCode.Database.SQLite.Extensions; 8 | using SQLite; 9 | 10 | namespace ManagedCode.Database.SQLite; 11 | 12 | public class SQLiteCollectionQueryable : BaseCollectionQueryable 13 | where TItem : class, IItem, new() 14 | { 15 | private readonly SQLiteConnection _connection; 16 | 17 | public SQLiteCollectionQueryable(SQLiteConnection connection) 18 | { 19 | _connection = connection; 20 | } 21 | 22 | public override async IAsyncEnumerable ToAsyncEnumerable( 23 | [EnumeratorCancellation] CancellationToken cancellationToken = default) 24 | { 25 | await Task.Yield(); 26 | 27 | foreach (var item in ApplyPredicates(Predicates)) 28 | { 29 | cancellationToken.ThrowIfCancellationRequested(); 30 | yield return item; 31 | } 32 | } 33 | 34 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 35 | { 36 | var query = ApplyPredicates(Predicates); 37 | 38 | return await Task.Run(() => query.FirstOrDefault(), cancellationToken); 39 | } 40 | 41 | public override async Task CountAsync(CancellationToken cancellationToken = default) 42 | { 43 | var query = ApplyPredicates(Predicates); 44 | 45 | return await Task.Run(() => query.LongCount(), cancellationToken); 46 | } 47 | 48 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 49 | { 50 | await Task.Yield(); 51 | 52 | var count = 0; 53 | 54 | foreach (var item in ApplyPredicates(Predicates)) 55 | { 56 | cancellationToken.ThrowIfCancellationRequested(); 57 | 58 | _connection.Delete(item.Id); 59 | count++; 60 | } 61 | 62 | return count; 63 | } 64 | 65 | 66 | private TableQuery ApplyPredicates(IEnumerable predicates) 67 | { 68 | var query = _connection.Table(); 69 | 70 | foreach (var predicate in predicates) 71 | query = predicate.QueryType switch 72 | { 73 | QueryType.Where => query.Where(predicate.ExpressionBool), 74 | QueryType.OrderBy => query.OrderBy(predicate.ExpressionObject.CorrectExpression()), 75 | QueryType.OrderByDescending => query.OrderByDescending(predicate.ExpressionObject.CorrectExpression()), 76 | QueryType.ThenBy => query.ThenBy(predicate.ExpressionObject.CorrectExpression()), 77 | QueryType.ThenByDescending => query.ThenByDescending(predicate.ExpressionObject.CorrectExpression()), 78 | QueryType.Take => predicate.Count.HasValue ? query.Take(predicate.Count.Value) : query, 79 | QueryType.Skip => query.Skip(predicate.Count!.Value), 80 | _ => query 81 | }; 82 | 83 | return query; 84 | } 85 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/SQLiteDatabase.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using ManagedCode.Database.Core; 5 | using ManagedCode.Database.Core.Common; 6 | using SQLite; 7 | 8 | namespace ManagedCode.Database.SQLite; 9 | 10 | public class SQLiteDatabase : BaseDatabase 11 | { 12 | private readonly SQLiteRepositoryOptions _options; 13 | 14 | public SQLiteDatabase(SQLiteRepositoryOptions options) 15 | { 16 | _options = options; 17 | } 18 | 19 | public override Task DeleteAsync(CancellationToken token = default) 20 | { 21 | DisposeInternal(); 22 | File.Delete(NativeClient.DatabasePath); 23 | return Task.CompletedTask; 24 | } 25 | 26 | protected override ValueTask DisposeAsyncInternal() 27 | { 28 | NativeClient.Close(); 29 | NativeClient.Dispose(); 30 | return new ValueTask(Task.CompletedTask); 31 | } 32 | 33 | protected override void DisposeInternal() 34 | { 35 | NativeClient.Close(); 36 | NativeClient.Dispose(); 37 | } 38 | 39 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 40 | { 41 | NativeClient = _options.Connection ?? new SQLiteConnection(_options.ConnectionString); 42 | 43 | return Task.CompletedTask; 44 | } 45 | 46 | public SQLiteDatabaseCollection GetCollection() where TItem : class, IItem, new() 47 | { 48 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 49 | 50 | NativeClient.CreateTable(); 51 | 52 | return new SQLiteDatabaseCollection(NativeClient); 53 | } 54 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/SQLiteDatabaseCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using ManagedCode.Database.Core; 6 | using ManagedCode.Database.Core.Exceptions; 7 | using SQLite; 8 | 9 | namespace ManagedCode.Database.SQLite; 10 | 11 | public class SQLiteDatabaseCollection : BaseDatabaseCollection 12 | where TItem : class, IItem, new() 13 | { 14 | private readonly SQLiteConnection _database; 15 | 16 | public SQLiteDatabaseCollection(SQLiteConnection database) 17 | { 18 | _database = database; 19 | } 20 | 21 | public override ICollectionQueryable Query => new SQLiteCollectionQueryable(_database); 22 | 23 | public override ValueTask DisposeAsync() 24 | { 25 | return new ValueTask(Task.CompletedTask); 26 | } 27 | 28 | public override void Dispose() 29 | { 30 | } 31 | 32 | #region Get 33 | 34 | protected override async Task GetInternalAsync(TId id, CancellationToken cancellationToken = default) 35 | { 36 | await Task.Yield(); 37 | return _database.Find(id); 38 | } 39 | 40 | #endregion 41 | 42 | #region Count 43 | 44 | protected override async Task CountInternalAsync(CancellationToken cancellationToken = default) 45 | { 46 | return await Task.Run(() => _database.Table().LongCount(), cancellationToken); 47 | } 48 | 49 | #endregion 50 | 51 | #region Insert 52 | 53 | protected override async Task InsertInternalAsync(TItem item, CancellationToken cancellationToken = default) 54 | { 55 | await Task.Yield(); 56 | 57 | _database.Insert(item); 58 | return _database.Find(item.Id); 59 | } 60 | 61 | protected override async Task InsertInternalAsync(IEnumerable items, 62 | CancellationToken cancellationToken = default) 63 | { 64 | return await Task.Run(() => _database.InsertAll(items), cancellationToken); 65 | } 66 | 67 | #endregion 68 | 69 | #region InsertOrUpdate 70 | 71 | protected override async Task InsertOrUpdateInternalAsync(TItem item, 72 | CancellationToken cancellationToken = default) 73 | { 74 | await Task.Yield(); 75 | 76 | _database.InsertOrReplace(item); 77 | return _database.Find(item.Id); 78 | } 79 | 80 | protected override async Task InsertOrUpdateInternalAsync(IEnumerable items, 81 | CancellationToken cancellationToken = default) 82 | { 83 | return await Task.Run(() => items.Sum(item => _database.InsertOrReplace(item)), cancellationToken); 84 | } 85 | 86 | #endregion 87 | 88 | #region Update 89 | 90 | protected override async Task UpdateInternalAsync(TItem item, CancellationToken cancellationToken = default) 91 | { 92 | await Task.Yield(); 93 | 94 | var count = _database.Update(item); 95 | 96 | if (count == 0) 97 | { 98 | throw new DatabaseException("Entity not found in collection."); 99 | } 100 | 101 | return _database.Find(item.Id); 102 | } 103 | 104 | protected override async Task UpdateInternalAsync(IEnumerable items, 105 | CancellationToken cancellationToken = default) 106 | { 107 | return await Task.Run(() => _database.UpdateAll(items), cancellationToken); 108 | } 109 | 110 | #endregion 111 | 112 | #region Delete 113 | 114 | protected override async Task DeleteInternalAsync(TId id, CancellationToken cancellationToken = default) 115 | { 116 | await Task.Yield(); 117 | return _database.Delete(id) != 0; 118 | } 119 | 120 | protected override async Task DeleteInternalAsync(TItem item, CancellationToken cancellationToken = default) 121 | { 122 | await Task.Yield(); 123 | return _database.Delete(item) != 0; 124 | } 125 | 126 | protected override async Task DeleteInternalAsync(IEnumerable ids, 127 | CancellationToken cancellationToken = default) 128 | { 129 | return await Task.Run(() => ids.Sum(id => _database.Delete(id)), cancellationToken); 130 | } 131 | 132 | protected override async Task DeleteInternalAsync(IEnumerable items, 133 | CancellationToken cancellationToken = default) 134 | { 135 | return await Task.Run(() => items.Sum(item => _database.Delete(item.Id)), cancellationToken); 136 | } 137 | 138 | protected override async Task DeleteCollectionInternalAsync(CancellationToken cancellationToken = default) 139 | { 140 | await Task.Run(() => _database.DeleteAll(), cancellationToken); 141 | 142 | return await CountInternalAsync(cancellationToken) == 0; 143 | } 144 | 145 | #endregion 146 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/SQLiteItem.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using SQLite; 3 | 4 | namespace ManagedCode.Database.SQLite; 5 | 6 | public class SQLiteItem : IItem 7 | { 8 | [PrimaryKey] [AutoIncrement] public int Id { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.SQLite/SQLiteRepositoryOptions.cs: -------------------------------------------------------------------------------- 1 | using SQLite; 2 | 3 | namespace ManagedCode.Database.SQLite; 4 | 5 | public class SQLiteRepositoryOptions 6 | { 7 | public string ConnectionString { get; set; } 8 | public SQLiteConnection? Connection { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests.Benchmark/ManagedCode.Database.Tests.Benchmark.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | enable 7 | enable 8 | false 9 | Exe 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests.Benchmark/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Running; 2 | 3 | var summary = BenchmarkRunner.Run(typeof(Program).Assembly); -------------------------------------------------------------------------------- /ManagedCode.Database.Tests.Benchmark/Various/SerializerBenchmarks.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Diagnosers; 3 | 4 | namespace Benchmark 5 | { 6 | [HtmlExporter] 7 | [SimpleJob] 8 | [MinColumn, MaxColumn, MeanColumn, MedianColumn, /*AllStatisticsColumn*/] 9 | [RPlotExporter] 10 | [MemoryDiagnoser] 11 | [ThreadingDiagnoser] 12 | [HardwareCounters( 13 | HardwareCounter.TotalCycles, 14 | HardwareCounter.TotalIssues, 15 | HardwareCounter.CacheMisses, 16 | HardwareCounter.Timer)] 17 | public class SerializerBenchmarks 18 | { 19 | 20 | public class localItem 21 | { 22 | public int Field1 { get; set; } 23 | public long Field2 { get; set; } 24 | public string Field3 { get; set; } 25 | public float Field4 { get; set; } 26 | public double Field5 { get; set; } 27 | 28 | 29 | public static localItem Get() 30 | { 31 | return new localItem 32 | { 33 | Field1 = 15000, 34 | Field2 = 500600, 35 | Field3 = string.Join('-', Enumerable.Repeat(Guid.NewGuid(), 10)), 36 | Field4 = 2.33f, 37 | Field5 = 5.123d 38 | }; 39 | } 40 | } 41 | 42 | /* 43 | [Benchmark] 44 | public void System_Text_Json_Serialize() 45 | { 46 | var item = localItem.Get(); 47 | for (int i = 0; i < 1_000_000; i++) 48 | { 49 | var str = System.Text.Json.JsonSerializer.Serialize(item); 50 | } 51 | } 52 | 53 | [Benchmark] 54 | public void System_Text_Json_Deserialize() 55 | { 56 | var str = System.Text.Json.JsonSerializer.Serialize(localItem.Get()); 57 | for (int i = 0; i < 1_000_000; i++) 58 | { 59 | var item = System.Text.Json.JsonSerializer.Deserialize(str); 60 | } 61 | } 62 | 63 | [Benchmark] 64 | public void Newtonsoft_Json_Serialize() 65 | { 66 | var item = localItem.Get(); 67 | for (int i = 0; i < 1_000_000; i++) 68 | { 69 | var str = Newtonsoft.Json.JsonConvert.SerializeObject(item); 70 | } 71 | } 72 | 73 | [Benchmark] 74 | public void Newtonsoft_Json_Deserialize() 75 | { 76 | var str = Newtonsoft.Json.JsonConvert.SerializeObject(localItem.Get()); 77 | for (int i = 0; i < 1_000_000; i++) 78 | { 79 | var item = Newtonsoft.Json.JsonConvert.DeserializeObject(str); 80 | } 81 | } 82 | 83 | [Benchmark] 84 | public void Newtonsoft_Bson_Serialize() 85 | { 86 | var item = localItem.Get(); 87 | for (int i = 0; i < 1_000_000; i++) 88 | { 89 | var array = SerializeBson(item); 90 | } 91 | } 92 | 93 | [Benchmark] 94 | public void Newtonsoft_Bson_Deserialize() 95 | { 96 | var array = SerializeBson(localItem.Get()); 97 | for (int i = 0; i < 1_000_000; i++) 98 | { 99 | var item = DeserializeBson(array); 100 | } 101 | } 102 | 103 | private localItem DeserializeBson(byte[] bytes) 104 | { 105 | using (MemoryStream ms = new MemoryStream(bytes)) 106 | using (BsonDataReader reader = new BsonDataReader(ms)) 107 | { 108 | JsonSerializer serializer = new JsonSerializer(); 109 | return serializer.Deserialize(reader); 110 | } 111 | } 112 | 113 | private byte[] SerializeBson(localItem entry) 114 | { 115 | using (MemoryStream ms = new MemoryStream()) 116 | using (BsonDataWriter datawriter = new BsonDataWriter(ms)) 117 | { 118 | JsonSerializer serializer = new JsonSerializer(); 119 | serializer.Serialize(datawriter, entry); 120 | return ms.ToArray(); 121 | } 122 | } 123 | */ 124 | } 125 | } 126 | 127 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests.Benchmark/ZoneTreeBenchmarks.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Benchmark 3 | { 4 | } 5 | 6 | //[HtmlExporter] 7 | //[SimpleJob(RunStrategy.ColdStart, targetCount: 1)] 8 | //[MinColumn, MaxColumn, MeanColumn, MedianColumn, /*AllStatisticsColumn*/] 9 | //[RPlotExporter] 10 | //[MemoryDiagnoser] 11 | //[ThreadingDiagnoser] 12 | /*[HardwareCounters( 13 | HardwareCounter.TotalCycles, 14 | HardwareCounter.TotalIssues, 15 | HardwareCounter.CacheMisses, 16 | HardwareCounter.Timer)]*/ 17 | 18 | /* 19 | public class ZoneTreeBenchmarks 20 | { 21 | 22 | #region SQLite 23 | 24 | [Benchmark] 25 | public void SQLite_Insert_100() 26 | { 27 | SQLiteBench.Insert(100); 28 | } 29 | 30 | [Benchmark] 31 | public void SQLite_Insert_10_000() 32 | { 33 | SQLiteBench.Insert(10_000); 34 | } 35 | 36 | [Benchmark] 37 | public void SQLite_Insert_100_000() 38 | { 39 | SQLiteBench.Insert(100_000); 40 | } 41 | 42 | [Benchmark] 43 | public void SQLite_Insert_100_Batch_50() 44 | { 45 | SQLiteBench.Insert_Batch(100,50); 46 | } 47 | 48 | [Benchmark] 49 | public void SQLite_Insert_10_000_Batch_100() 50 | { 51 | SQLiteBench.Insert_Batch(10_000, 100); 52 | } 53 | 54 | [Benchmark] 55 | public void SQLite_Insert_100_000_Batch_250() 56 | { 57 | SQLiteBench.Insert_Batch(100_000, 250); 58 | } 59 | 60 | [Benchmark] 61 | public void SQLite_Count() 62 | { 63 | SQLiteBench.Count(); 64 | } 65 | 66 | [Benchmark] 67 | public void SQLite_Search() 68 | { 69 | SQLiteBench.Search("100"); 70 | } 71 | 72 | #endregion 73 | 74 | #region LiteDB 75 | 76 | [Benchmark] 77 | public void LiteDB_Insert_100() 78 | { 79 | LiteDBBench.Insert(100); 80 | } 81 | 82 | [Benchmark] 83 | public void LiteDB_Insert_10_000() 84 | { 85 | LiteDBBench.Insert(10_000); 86 | } 87 | 88 | [Benchmark] 89 | public void LiteDB_Insert_100_000() 90 | { 91 | LiteDBBench.Insert(100_000); 92 | } 93 | 94 | [Benchmark] 95 | public void LiteDB_Insert_100_Batch_50() 96 | { 97 | LiteDBBench.Insert_Batch(100,50); 98 | } 99 | 100 | [Benchmark] 101 | public void LiteDB_Insert_10_000_Batch_100() 102 | { 103 | LiteDBBench.Insert_Batch(10_000, 100); 104 | } 105 | 106 | [Benchmark] 107 | public void LiteDB_Insert_100_000_Batch_250() 108 | { 109 | LiteDBBench.Insert_Batch(100_000, 250); 110 | } 111 | 112 | [Benchmark] 113 | public void LiteDB_Count() 114 | { 115 | LiteDBBench.Count(); 116 | } 117 | 118 | [Benchmark] 119 | public void LiteDB_Search() 120 | { 121 | LiteDBBench.Search("100"); 122 | } 123 | 124 | #endregion 125 | 126 | 127 | 128 | #region ZoneTree 129 | 130 | [Benchmark] 131 | public void ZoneTree_Insert_100() 132 | { 133 | ZoneTreeBench.Insert(100); 134 | } 135 | 136 | [Benchmark] 137 | public void ZoneTree_Insert_10_000() 138 | { 139 | ZoneTreeBench.Insert(10_000); 140 | } 141 | 142 | [Benchmark] 143 | public void ZoneTree_Insert_100_000() 144 | { 145 | ZoneTreeBench.Insert(100_000); 146 | } 147 | 148 | [Benchmark] 149 | public void ZoneTree_Insert_100_Batch_50() 150 | { 151 | ZoneTreeBench.Insert_Batch(100,50); 152 | } 153 | 154 | [Benchmark] 155 | public void ZoneTree_Insert_10_000_Batch_100() 156 | { 157 | ZoneTreeBench.Insert_Batch(10_000, 100); 158 | } 159 | 160 | [Benchmark] 161 | public void ZoneTree_Insert_100_000_Batch_250() 162 | { 163 | ZoneTreeBench.Insert_Batch(100_000, 250); 164 | } 165 | 166 | [Benchmark] 167 | public void ZoneTree_Count() 168 | { 169 | ZoneTreeBench.Count(); 170 | } 171 | 172 | [Benchmark] 173 | public void ZoneTree_Search() 174 | { 175 | ZoneTreeBench.Search("100"); 176 | } 177 | 178 | #endregion 179 | 180 | } 181 | 182 | 183 | */ -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/AzureTablesTests/AzureTablesCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using Azure; 2 | using FluentAssertions; 3 | using ManagedCode.Database.AzureTables; 4 | using ManagedCode.Database.Core.Exceptions; 5 | using ManagedCode.Database.Tests.BaseTests; 6 | using ManagedCode.Database.Tests.Common; 7 | using ManagedCode.Database.Tests.TestContainers; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.Threading.Tasks; 11 | using Xunit; 12 | using Xunit.Abstractions; 13 | 14 | namespace ManagedCode.Database.Tests.AzureTablesTests; 15 | 16 | [Collection(nameof(AzureTablesTestContainer))] 17 | public class AzureTablesCollectionTests : BaseCollectionTests 18 | { 19 | public AzureTablesCollectionTests(AzureTablesTestContainer container) : base(container) 20 | { 21 | } 22 | 23 | public override async Task InsertOrUpdateListOfItems() 24 | { 25 | // Arrange 26 | int itemsCount = 5; 27 | List list = new(); 28 | 29 | for (var i = 0; i < itemsCount; i++) 30 | { 31 | list.Add(CreateNewItem()); 32 | } 33 | 34 | var itemsInsert = await Collection.InsertOrUpdateAsync(list); 35 | 36 | foreach (var item in list) 37 | { 38 | item.StringData = Guid.NewGuid().ToString(); 39 | } 40 | 41 | // Act 42 | var itemsUpdate = await Collection.InsertOrUpdateAsync(list); 43 | 44 | // Assert 45 | itemsUpdate.Should().Be(itemsCount); 46 | itemsInsert.Should().Be(itemsCount); 47 | list.Count.Should().Be(itemsCount); 48 | } 49 | 50 | public override async Task DeleteListOfItemsById() 51 | { 52 | var baseMethod = () => base.DeleteListOfItemsById(); 53 | await baseMethod.Should().ThrowExactlyAsync(); 54 | } 55 | 56 | public override async Task DeleteListOfItemsById_WhenItemsDontExist() 57 | { 58 | var baseMethod = () => base.DeleteListOfItemsById_WhenItemsDontExist(); 59 | await baseMethod.Should().ThrowExactlyAsync(); 60 | } 61 | 62 | public override async Task DeleteListOfItems_WhenItemsDontExist() 63 | { 64 | var baseMethod = () => base.DeleteListOfItems_WhenItemsDontExist(); 65 | await baseMethod.Should().ThrowExactlyAsync(); 66 | } 67 | 68 | public override async Task DeleteAll() 69 | { 70 | // Arrange 71 | int itemsCount = 5; 72 | List list = new(); 73 | 74 | for (var i = 0; i < itemsCount; i++) 75 | { 76 | list.Add(CreateNewItem()); 77 | } 78 | 79 | await Collection.InsertAsync(list); 80 | 81 | // Act 82 | var deletedItems = await Collection.DeleteCollectionAsync(); 83 | var countAction = async () => await Collection.CountAsync(); 84 | 85 | // Assert 86 | deletedItems.Should().BeTrue(); 87 | await countAction.Should().ThrowExactlyAsync(); 88 | } 89 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/AzureTablesTests/AzureTablesQueryableTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ManagedCode.Database.AzureTables; 3 | using ManagedCode.Database.Tests.BaseTests; 4 | using ManagedCode.Database.Tests.Common; 5 | using ManagedCode.Database.Tests.TestContainers; 6 | using System; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | 11 | namespace ManagedCode.Database.Tests.AzureTablesTests; 12 | 13 | [Collection(nameof(AzureTablesTestContainer))] 14 | public class AzureTablesQueryableTests : BaseQueryableTests 15 | { 16 | public AzureTablesQueryableTests(AzureTablesTestContainer container) : base(container) 17 | { 18 | } 19 | 20 | [Fact] 21 | public override async Task Where_TakeNull_ReturnException() 22 | { 23 | // Arrange 24 | int itemsCountToInsert = 1; 25 | 26 | 27 | await CreateAndInsertItemsAsync(itemsCountToInsert); 28 | 29 | // Act 30 | var itemsResult = () => Collection.Query 31 | .Where(null) 32 | .ToListAsync(); 33 | 34 | // Assert 35 | await itemsResult 36 | .Should() 37 | .ThrowAsync(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/BaseTests/BaseMultiThreadingTests.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Tests.Common; 2 | using System; 3 | using System.Threading.Tasks; 4 | using ManagedCode.Database.Tests.TestContainers; 5 | 6 | namespace ManagedCode.Database.Tests.BaseTests; 7 | 8 | public abstract class BaseMultiThreadingTests : BaseTests 9 | where TItem : IBaseItem, new() 10 | { 11 | protected BaseMultiThreadingTests(ITestContainer testContainer) : base(testContainer) 12 | { 13 | } 14 | 15 | public Task InitializeAsync() 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | 20 | public Task DisposeAsync() 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/BaseTests/BaseTests.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ManagedCode.Database.Core; 3 | using ManagedCode.Database.Tests.Common; 4 | using ManagedCode.Database.Tests.TestContainers; 5 | using Xunit; 6 | 7 | namespace ManagedCode.Database.Tests.BaseTests; 8 | 9 | // TODO: rename 10 | public abstract class BaseTests : IAsyncLifetime where TItem : IBaseItem, new() 11 | { 12 | private readonly ITestContainer _testContainer; 13 | 14 | protected BaseTests(ITestContainer testContainer) 15 | { 16 | _testContainer = testContainer; 17 | } 18 | 19 | protected IDatabaseCollection Collection => _testContainer.Collection; 20 | 21 | protected TId GenerateId() 22 | { 23 | return _testContainer.GenerateId(); 24 | } 25 | 26 | public async Task InitializeAsync() 27 | { 28 | await _testContainer.InitializeAsync(); 29 | } 30 | 31 | public async Task DisposeAsync() 32 | { 33 | await _testContainer.DisposeAsync(); 34 | } 35 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/IBaseItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.Core; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public interface IBaseItem : IItem 7 | { 8 | string StringData { get; set; } 9 | int IntData { get; set; } 10 | long LongData { get; set; } 11 | float FloatData { get; set; } 12 | double DoubleData { get; set; } 13 | DateTime DateTimeData { get; set; } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/InMemoryItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ManagedCode.Database.Tests.Common; 4 | 5 | public class InMemoryItem : IBaseItem 6 | { 7 | public int Id { get; set; } 8 | public string StringData { get; set; } 9 | public int IntData { get; set; } 10 | public long LongData { get; set; } 11 | public float FloatData { get; set; } 12 | public double DoubleData { get; set; } 13 | public DateTime DateTimeData { get; set; } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestAzureTablesItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.AzureTables; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public class TestAzureTablesItem : AzureTablesItem, IBaseItem 7 | { 8 | public string StringData { get; set; } 9 | public int IntData { get; set; } 10 | public long LongData { get; set; } 11 | public float FloatData { get; set; } 12 | public double DoubleData { get; set; } 13 | public DateTime DateTimeData { get; set; } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestCosmosItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.Cosmos; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public class TestCosmosItem : CosmosItem, IBaseItem 7 | { 8 | public string StringData { get; set; } 9 | public int IntData { get; set; } 10 | public long LongData { get; set; } 11 | public float FloatData { get; set; } 12 | public double DoubleData { get; set; } 13 | public DateTime DateTimeData { get; set; } 14 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestDynamoDbItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.DynamoDB; 3 | using Newtonsoft.Json; 4 | 5 | namespace ManagedCode.Database.Tests.Common; 6 | 7 | public class TestDynamoDbItem : DynamoDBItem, IBaseItem 8 | { 9 | public TestDynamoDbItem() 10 | { 11 | Id = Guid.NewGuid().ToString(); 12 | } 13 | 14 | public string StringData { get; set; } 15 | public int IntData { get; set; } 16 | public long LongData { get; set; } 17 | public float FloatData { get; set; } 18 | public double DoubleData { get; set; } 19 | 20 | [JsonIgnore] 21 | public DateTime DateTimeData { get; set; } 22 | 23 | [JsonProperty("date")] 24 | public string CustomDate 25 | { 26 | get { return DateTimeData.ToString(); } 27 | set { DateTimeData = DateTime.Parse(value); } 28 | } 29 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestLiteDBItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.LiteDB; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public class TestLiteDBItem : LiteDBItem, IBaseItem 7 | { 8 | public TestLiteDBItem() 9 | { 10 | Id = Guid.NewGuid().ToString(); 11 | } 12 | 13 | public string StringData { get; set; } 14 | public int IntData { get; set; } 15 | public long LongData { get; set; } 16 | public float FloatData { get; set; } 17 | public double DoubleData { get; set; } 18 | public DateTime DateTimeData { get; set; } 19 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestMongoDBItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.MongoDB; 3 | using MongoDB.Bson; 4 | 5 | namespace ManagedCode.Database.Tests.Common; 6 | 7 | public class TestMongoDBItem : MongoDBItem, IBaseItem 8 | { 9 | public string StringData { get; set; } 10 | public int IntData { get; set; } 11 | public long LongData { get; set; } 12 | public float FloatData { get; set; } 13 | public double DoubleData { get; set; } 14 | public DateTime DateTimeData { get; set; } 15 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestSQLiteItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SQLite; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public class TestSQLiteItem : IBaseItem 7 | { 8 | [PrimaryKey] 9 | public int Id { get; set; } 10 | 11 | public string StringData { get; set; } 12 | public int IntData { get; set; } 13 | public long LongData { get; set; } 14 | public float FloatData { get; set; } 15 | public double DoubleData { get; set; } 16 | public DateTime DateTimeData { get; set; } 17 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/Common/TestZoneTreeItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ManagedCode.Database.LiteDB; 3 | 4 | namespace ManagedCode.Database.Tests.Common; 5 | 6 | public class TestZoneTreeItem : LiteDBItem, IBaseItem 7 | { 8 | public TestZoneTreeItem() 9 | { 10 | Id = Guid.NewGuid().ToString(); 11 | } 12 | 13 | public string StringData { get; set; } 14 | public int IntData { get; set; } 15 | public long LongData { get; set; } 16 | public float FloatData { get; set; } 17 | public double DoubleData { get; set; } 18 | public DateTime DateTimeData { get; set; } 19 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/CosmosTests/CosmosCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ManagedCode.Database.Core.Exceptions; 3 | using ManagedCode.Database.Tests.BaseTests; 4 | using ManagedCode.Database.Tests.Common; 5 | using ManagedCode.Database.Tests.TestContainers; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | 11 | namespace ManagedCode.Database.Tests.CosmosTests; 12 | 13 | [Collection(nameof(CosmosTestContainer))] 14 | public class CosmosCollectionTests : BaseCollectionTests 15 | { 16 | public CosmosCollectionTests(CosmosTestContainer container) : base(container) 17 | { 18 | } 19 | 20 | public override async Task DeleteItemById_WhenItemDoesntExists() 21 | { 22 | var baseMethod = () => base.DeleteItemById_WhenItemDoesntExists(); 23 | 24 | await baseMethod.Should().ThrowExactlyAsync(); 25 | } 26 | 27 | public override async Task DeleteListOfItemsById_WhenItemsDontExist() 28 | { 29 | var baseMethod = () => base.DeleteListOfItemsById_WhenItemsDontExist(); 30 | 31 | await baseMethod.Should().ThrowExactlyAsync(); 32 | } 33 | 34 | public override async Task DeleteListOfItems_WhenItemsDontExist() 35 | { 36 | var baseMethod = () => base.DeleteListOfItems_WhenItemsDontExist(); 37 | 38 | await baseMethod.Should().ThrowExactlyAsync(); 39 | } 40 | 41 | [Fact] 42 | public override async Task DeleteCollectionAsync() 43 | { 44 | // Arrange 45 | int itemsCount = 5; 46 | List list = new(); 47 | 48 | for (var i = 0; i < itemsCount; i++) 49 | { 50 | list.Add(CreateNewItem()); 51 | } 52 | 53 | await Collection.InsertAsync(list); 54 | // Act 55 | var isDeleted = await Collection.DeleteCollectionAsync(); 56 | 57 | // Assert 58 | isDeleted.Should().BeTrue(); 59 | } 60 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/CosmosTests/CosmosQueryableTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ManagedCode.Database.Tests.BaseTests; 3 | using ManagedCode.Database.Tests.Common; 4 | using ManagedCode.Database.Tests.TestContainers; 5 | using System.Threading.Tasks; 6 | using System; 7 | using Xunit; 8 | using Xunit.Abstractions; 9 | 10 | namespace ManagedCode.Database.Tests.CosmosTests; 11 | 12 | [Collection(nameof(CosmosTestContainer))] 13 | public class CosmosQueryableTests : BaseQueryableTests 14 | { 15 | public CosmosQueryableTests(ITestOutputHelper testOutputHelper, CosmosTestContainer container) : base(container) 16 | { 17 | } 18 | 19 | public override async Task OrderBy_TakeNull_ReturnException() 20 | { 21 | // Arrange 22 | int itemsCountToInsert = 1; 23 | 24 | var guid = Guid.NewGuid().ToString(); 25 | 26 | await CreateAndInsertItemsAsync(itemsCountToInsert); 27 | 28 | // Act 29 | var itemsResult = () => Collection.Query 30 | .OrderBy(null) 31 | .ToListAsync(); 32 | 33 | // Assert 34 | await itemsResult 35 | .Should() 36 | .ThrowAsync(); 37 | } 38 | 39 | public override async Task OrderByDescending_TakeNull_ReturnException() 40 | { 41 | // Arrange 42 | int itemsCountToInsert = 1; 43 | 44 | var guid = Guid.NewGuid().ToString(); 45 | var unfaithfulGuid = Guid.NewGuid().ToString(); 46 | 47 | await CreateAndInsertItemsAsync(itemsCountToInsert, guid); 48 | 49 | // Act 50 | var itemsResult = () => Collection.Query 51 | .OrderByDescending(null) 52 | .ToListAsync(); 53 | 54 | // Assert 55 | await itemsResult 56 | .Should() 57 | .ThrowAsync(); 58 | } 59 | 60 | public override async Task Where_TakeNull_ReturnException() 61 | { 62 | // Arrange 63 | int itemsCountToInsert = 1; 64 | 65 | 66 | await CreateAndInsertItemsAsync(itemsCountToInsert); 67 | 68 | // Act 69 | var itemsResult = () => Collection.Query 70 | .Where(null) 71 | .ToListAsync(); 72 | 73 | // Assert 74 | await itemsResult 75 | .Should() 76 | .ThrowAsync(); 77 | } 78 | 79 | public override async Task ThenByDescending_TakeNull_ReturnException() 80 | { 81 | // Arrange 82 | int itemsCountToInsert = 1; 83 | 84 | await CreateAndInsertItemsAsync(itemsCountToInsert); 85 | 86 | // Act 87 | var itemsResult = () => Collection.Query 88 | .OrderBy(o => o.StringData) 89 | .ThenByDescending(null) 90 | .ToListAsync(); 91 | 92 | // Assert 93 | await itemsResult 94 | .Should() 95 | .ThrowAsync(); 96 | } 97 | 98 | public override async Task Skip_NegativeNumber_ReturnZero() 99 | { 100 | var baseMethod = async () => await base.Skip_NegativeNumber_ReturnZero(); 101 | 102 | await baseMethod.Should().ThrowExactlyAsync(); 103 | } 104 | 105 | public override async Task Take_NegativeNumber_ReturnZero() 106 | { 107 | var baseMethod = () => base.Take_NegativeNumber_ReturnZero(); 108 | 109 | await baseMethod.Should().ThrowExactlyAsync(); 110 | } 111 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/DynamoDbTests/DynamoDBQueryableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using ManagedCode.Database.Tests.BaseTests; 5 | using ManagedCode.Database.Tests.Common; 6 | using ManagedCode.Database.Tests.TestContainers; 7 | using Xunit; 8 | using Xunit.Abstractions; 9 | 10 | namespace ManagedCode.Database.Tests.DynamoDbTests; 11 | 12 | [Collection(nameof(DynamoDBTestContainer))] 13 | public class DynamoDBQueryableTests : BaseQueryableTests 14 | { 15 | public DynamoDBQueryableTests(DynamoDBTestContainer container) : base(container) 16 | { 17 | } 18 | 19 | [Fact] 20 | public override async Task ThenBy_TakeNull_ReturnException() 21 | { 22 | // Arrange 23 | int itemsCountToInsert = 1; 24 | 25 | await CreateAndInsertItemsAsync(itemsCountToInsert); 26 | 27 | // Act 28 | var itemsResult = () => Collection.Query 29 | .OrderBy(o => o.StringData) 30 | .ThenBy(null) 31 | .ToListAsync(); 32 | 33 | // Assert 34 | await itemsResult 35 | .Should() 36 | .ThrowAsync(); 37 | } 38 | 39 | [Fact] 40 | public override async Task ThenByDescending_TakeNull_ReturnException() 41 | { 42 | // Arrange 43 | int itemsCountToInsert = 1; 44 | 45 | await CreateAndInsertItemsAsync(itemsCountToInsert); 46 | 47 | // Act 48 | var itemsResult = () => Collection.Query 49 | .OrderBy(o => o.StringData) 50 | .ThenByDescending(null) 51 | .ToListAsync(); 52 | 53 | // Assert 54 | await itemsResult 55 | .Should() 56 | .ThrowAsync(); 57 | } 58 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/DynamoDbTests/DynamoDbCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ManagedCode.Database.Core.Exceptions; 3 | using ManagedCode.Database.Tests.BaseTests; 4 | using ManagedCode.Database.Tests.Common; 5 | using ManagedCode.Database.Tests.TestContainers; 6 | using System.Collections.Generic; 7 | using System.Threading.Tasks; 8 | using Xunit; 9 | using Xunit.Abstractions; 10 | 11 | namespace ManagedCode.Database.Tests.DynamoDbTests; 12 | 13 | [Collection(nameof(DynamoDBTestContainer))] 14 | public class DynamoDbCollectionTests : BaseCollectionTests 15 | { 16 | public DynamoDbCollectionTests(DynamoDBTestContainer container) : base(container) 17 | { 18 | } 19 | 20 | [Fact] 21 | public override async Task DeleteAll() 22 | { 23 | // Arrange 24 | int itemsCount = 5; 25 | List list = new(); 26 | 27 | for (var i = 0; i < itemsCount; i++) 28 | { 29 | list.Add(CreateNewItem()); 30 | } 31 | 32 | await Collection.InsertAsync(list); 33 | 34 | // Act 35 | var deletedItems = await Collection.DeleteCollectionAsync(); 36 | 37 | // Assert 38 | deletedItems.Should().BeTrue(); 39 | } 40 | 41 | [Fact] 42 | public override async Task DeleteItemById_WhenItemDoesntExists() 43 | { 44 | // Arrange 45 | var item = CreateNewItem(); 46 | 47 | // Act 48 | var deleted = () => Collection.DeleteAsync(item.Id); 49 | 50 | // Assert 51 | deleted.Should().ThrowExactlyAsync(); 52 | } 53 | 54 | [Fact] 55 | public override async Task GetById_ReturnOk() 56 | { 57 | // Arrange 58 | var itemId = GenerateId(); 59 | await Collection.InsertAsync(CreateNewItem(itemId)); 60 | 61 | try 62 | { 63 | // Act 64 | var getItemResult = await Collection.GetAsync(itemId); 65 | 66 | // Assert 67 | getItemResult.Should().NotBeNull(); 68 | } 69 | catch (DatabaseException e) 70 | { 71 | // Act 72 | var getItemResult = () => Collection.GetAsync(itemId); 73 | 74 | // Assert 75 | await getItemResult.Should().ThrowAsync(); 76 | } 77 | } 78 | 79 | [Fact] 80 | public override async Task InsertItem_WhenItemExist_ShouldThrowDatabaseException() 81 | { 82 | // Arrange 83 | var item = CreateNewItem(); 84 | 85 | // Act 86 | await Collection.InsertAsync(item); 87 | var insertItem = await Collection.InsertAsync(item); 88 | 89 | // Assert 90 | insertItem.Should().NotBeNull(); 91 | } 92 | 93 | [Fact] 94 | public override async Task UpdateItem_WhenItemDoesntExists() 95 | { 96 | // Arrange 97 | var id = GenerateId(); 98 | 99 | // Act 100 | var updateItem = await Collection.UpdateAsync(CreateNewItem(id)); 101 | 102 | // Assert 103 | updateItem.Should().BeNull(); 104 | } 105 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Disables anonymized usage information from being sent on build. Read more about what data is being collected and why here: https://github.com/realm/realm-dotnet/blob/main/Realm/Realm.Weaver/Analytics.cs 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 20 | 21 | 22 | 23 | 24 | A comma-separated list of error codes that can be safely ignored in assembly verification. 25 | 26 | 27 | 28 | 29 | 'false' to turn off automatic generation of the XML Schema file. 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/InMemoryTests/InMemoryCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Tests.BaseTests; 2 | using ManagedCode.Database.Tests.Common; 3 | using ManagedCode.Database.Tests.TestContainers; 4 | 5 | namespace ManagedCode.Database.Tests.InMemoryTests; 6 | 7 | public class InMemoryCollectionTests : BaseCollectionTests 8 | { 9 | public InMemoryCollectionTests() : base(new InMemoryTestContainer()) 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/InMemoryTests/InMemoryQueryableTests.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Tests.BaseTests; 2 | using ManagedCode.Database.Tests.Common; 3 | using ManagedCode.Database.Tests.TestContainers; 4 | 5 | namespace ManagedCode.Database.Tests.InMemoryTests; 6 | 7 | public class InMemoryQueryableTests : BaseQueryableTests 8 | { 9 | public InMemoryQueryableTests() : base(new InMemoryTestContainer()) 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/LiteDBTests/LiteDBCollectionTests.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Collections.Generic; 3 | // using System.Threading.Tasks; 4 | // using FluentAssertions; 5 | // using ManagedCode.Database.Tests.BaseTests; 6 | // using ManagedCode.Database.Tests.Common; 7 | // using ManagedCode.Database.Tests.TestContainers; 8 | // 9 | // namespace ManagedCode.Database.Tests.LiteDBTests; 10 | // 11 | // public class LiteDBCollectionTests : BaseCollectionTests 12 | // { 13 | // public LiteDBCollectionTests() : base(new LiteDBTestContainer()) 14 | // { 15 | // 16 | // } 17 | // 18 | // public override async Task InsertOrUpdateListOfItems() 19 | // { 20 | // 21 | // // Arrange 22 | // int itemsCount = 5; 23 | // int updatedItemsCount = 0; 24 | // List list = new(); 25 | // 26 | // for (var i = 0; i < itemsCount; i++) 27 | // { 28 | // list.Add(CreateNewItem()); 29 | // } 30 | // 31 | // var itemsInsert = await Collection.InsertOrUpdateAsync(list); 32 | // 33 | // foreach (var item in list) 34 | // { 35 | // item.DateTimeData = DateTime.Now.AddDays(-1); 36 | // } 37 | // 38 | // // Act 39 | // var itemsUpdate = await Collection.InsertOrUpdateAsync(list); 40 | // 41 | // // Assert 42 | // itemsUpdate.Should().Be(updatedItemsCount); 43 | // itemsInsert.Should().Be(itemsCount); 44 | // list.Count.Should().Be(itemsCount); 45 | // } 46 | // } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/MSSQL/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.MSSQL; 2 | 3 | namespace ManagedCode.Database.Tests.MSSQL.Models; 4 | 5 | public class Customer : MSSQLItem 6 | { 7 | public string Name { get; set; } 8 | public int Age { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/MSSQL/Repositories/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.MSSQL; 2 | using ManagedCode.Database.Tests.MSSQL.Models; 3 | 4 | namespace ManagedCode.Database.Tests.MSSQL.Repositories; 5 | 6 | public class CustomerRepository : MSSQLRepository, ICustomerRepository 7 | { 8 | public CustomerRepository(MSSQLDatabaseContext context) : base(context) 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/MSSQL/Repositories/ICustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.MSSQL; 2 | using ManagedCode.Database.Tests.MSSQL.Models; 3 | 4 | namespace ManagedCode.Database.Tests.MSSQL.Repositories; 5 | 6 | public interface ICustomerRepository : IMSSQLRepository 7 | { 8 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/ManagedCode.Database.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | 11 6 | true 7 | false 8 | 11 9 | 10 | 11 | 12 | true 13 | GeneratedCodeAttribute 14 | [*]*.Migrations.* 15 | **/MyFile.cs 16 | lcov 17 | 18 | 19 | 20 | PreserveNewest 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | runtime; build; native; contentfiles; analyzers; buildtransitive 35 | all 36 | 37 | 38 | runtime; build; native; contentfiles; analyzers; buildtransitive 39 | all 40 | 41 | 42 | runtime; build; native; contentfiles; analyzers; buildtransitive 43 | all 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/MongoDBTests/MongoDBCollectionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using ManagedCode.Database.Tests.BaseTests; 3 | using ManagedCode.Database.Tests.Common; 4 | using ManagedCode.Database.Tests.TestContainers; 5 | using MongoDB.Bson; 6 | using System.Threading.Tasks; 7 | using Xunit; 8 | using Xunit.Abstractions; 9 | 10 | namespace ManagedCode.Database.Tests.MongoDBTests; 11 | 12 | [Collection(nameof(MongoDBTestContainer))] 13 | public class MongoDBCollectionTests : BaseCollectionTests 14 | { 15 | public MongoDBCollectionTests(MongoDBTestContainer container) : base(container) 16 | { 17 | } 18 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/MongoDBTests/MongoDBQueryableTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using ManagedCode.Database.Tests.BaseTests; 5 | using ManagedCode.Database.Tests.Common; 6 | using ManagedCode.Database.Tests.TestContainers; 7 | using MongoDB.Bson; 8 | using MongoDB.Driver; 9 | using Xunit; 10 | using Xunit.Abstractions; 11 | 12 | namespace ManagedCode.Database.Tests.MongoDBTests; 13 | 14 | [Collection(nameof(MongoDBTestContainer))] 15 | public class MongoDBQueryableTests : BaseQueryableTests 16 | { 17 | public MongoDBQueryableTests(MongoDBTestContainer container) : base(container) 18 | { 19 | } 20 | 21 | public override async Task OrderBy_TakeNull_ReturnException() 22 | { 23 | // Arrange 24 | int itemsCountToInsert = 1; 25 | 26 | var guid = Guid.NewGuid().ToString(); 27 | 28 | await CreateAndInsertItemsAsync(itemsCountToInsert); 29 | 30 | // Act 31 | var itemsResult = () => Collection.Query 32 | .OrderBy(null) 33 | .ToListAsync(); 34 | 35 | // Assert 36 | await itemsResult 37 | .Should() 38 | .ThrowAsync(); 39 | } 40 | 41 | public override async Task OrderByDescending_TakeNull_ReturnException() 42 | { 43 | // Arrange 44 | int itemsCountToInsert = 1; 45 | 46 | var guid = Guid.NewGuid().ToString(); 47 | var unfaithfulGuid = Guid.NewGuid().ToString(); 48 | 49 | await CreateAndInsertItemsAsync(itemsCountToInsert, guid); 50 | 51 | // Act 52 | var itemsResult = () => Collection.Query 53 | .OrderByDescending(null) 54 | .ToListAsync(); 55 | 56 | // Assert 57 | await itemsResult 58 | .Should() 59 | .ThrowAsync(); 60 | } 61 | 62 | public override async Task ThenBy_TakeNull_ReturnException() 63 | { 64 | // Arrange 65 | int itemsCountToInsert = 1; 66 | 67 | await CreateAndInsertItemsAsync(itemsCountToInsert); 68 | 69 | // Act 70 | var itemsResult = () => Collection.Query 71 | .OrderBy(o => o.StringData) 72 | .ThenBy(null) 73 | .ToListAsync(); 74 | 75 | // Assert 76 | await itemsResult 77 | .Should() 78 | .ThrowAsync(); 79 | } 80 | 81 | public override async Task ThenByDescending_TakeNull_ReturnException() 82 | { 83 | // Arrange 84 | int itemsCountToInsert = 1; 85 | 86 | await CreateAndInsertItemsAsync(itemsCountToInsert); 87 | 88 | // Act 89 | var itemsResult = () => Collection.Query 90 | .OrderBy(o => o.StringData) 91 | .ThenByDescending(null) 92 | .ToListAsync(); 93 | 94 | // Assert 95 | await itemsResult 96 | .Should() 97 | .ThrowAsync(); 98 | } 99 | 100 | public override async Task Where_TakeNull_ReturnException() 101 | { 102 | // Arrange 103 | int itemsCountToInsert = 1; 104 | 105 | 106 | await CreateAndInsertItemsAsync(itemsCountToInsert); 107 | 108 | // Act 109 | var itemsResult = () => Collection.Query 110 | .Where(null) 111 | .ToListAsync(); 112 | 113 | // Assert 114 | await itemsResult 115 | .Should() 116 | .ThrowAsync(); 117 | } 118 | 119 | public override async Task Skip_NegativeNumber_ReturnZero() 120 | { 121 | // In mongoDb using negative numbers in skip is not allowed 122 | var baseMethod = () => base.Skip_NegativeNumber_ReturnZero(); 123 | 124 | await baseMethod.Should().ThrowExactlyAsync(); 125 | } 126 | 127 | public override async Task Take_NegativeNumber_ReturnZero() 128 | { 129 | var baseMethod = () => base.Take_NegativeNumber_ReturnZero(); 130 | 131 | await baseMethod.Should().ThrowExactlyAsync(); 132 | } 133 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/PostgreSQL/Models/Customer.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.PostgreSQL; 2 | 3 | namespace ManagedCode.Database.Tests.PostgreSQL.Models; 4 | 5 | public class Customer : PostgresItem 6 | { 7 | public string Name { get; set; } 8 | public int Age { get; set; } 9 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/PostgreSQL/Repositories/CustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.PostgreSQL; 2 | using ManagedCode.Database.Tests.PostgreSQL.Models; 3 | 4 | namespace ManagedCode.Database.Tests.PostgreSQL.Repositories; 5 | 6 | public class CustomerRepository : PostgresRepository, ICustomerRepository 7 | { 8 | public CustomerRepository(PostgresDatabaseContext context) : base(context) 9 | { 10 | } 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/PostgreSQL/Repositories/ICustomerRepository.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.EntityFramework.PostgreSQL; 2 | using ManagedCode.Database.Tests.PostgreSQL.Models; 3 | 4 | namespace ManagedCode.Database.Tests.PostgreSQL.Repositories; 5 | 6 | public interface ICustomerRepository : IPostgresRepository 7 | { 8 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/SQLiteTests/SQLiteCollectionTests.cs: -------------------------------------------------------------------------------- 1 | // using ManagedCode.Database.Tests.BaseTests; 2 | // using ManagedCode.Database.Tests.Common; 3 | // using ManagedCode.Database.Tests.TestContainers; 4 | // 5 | // namespace ManagedCode.Database.Tests.SQLiteTests; 6 | // 7 | // public class SQLiteCollectionTests : BaseCollectionTests 8 | // { 9 | // public SQLiteCollectionTests() : base(new SQLiteTestContainer()) 10 | // { 11 | // } 12 | // } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/CosmosTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Docker.DotNet; 6 | using Docker.DotNet.Models; 7 | using DotNet.Testcontainers.Builders; 8 | using DotNet.Testcontainers.Containers; 9 | using ManagedCode.Database.Core; 10 | using ManagedCode.Database.Cosmos; 11 | using ManagedCode.Database.Tests.Common; 12 | using Microsoft.Azure.Cosmos; 13 | using Xunit; 14 | 15 | namespace ManagedCode.Database.Tests.TestContainers; 16 | 17 | [CollectionDefinition(nameof(CosmosTestContainer))] 18 | public class CosmosTestContainer : ITestContainer, 19 | ICollectionFixture, IDisposable 20 | { 21 | private readonly TestcontainersContainer _cosmosTestContainer; 22 | private CosmosDatabase _database; 23 | private DockerClient _dockerClient; 24 | private const string containerName = "cosmosContainer"; 25 | private const ushort privatePort = 8081; 26 | private bool containerExsist = false; 27 | private string containerId; 28 | 29 | public CosmosTestContainer() 30 | { 31 | _cosmosTestContainer = new TestcontainersBuilder() 32 | .WithImage("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator") 33 | .WithName(containerName) 34 | .WithExposedPort(8081) 35 | .WithPortBinding(8081, 8081) 36 | .WithPortBinding(10250, 10250) 37 | .WithPortBinding(10251, 10251) 38 | .WithPortBinding(10252, 10252) 39 | .WithPortBinding(10253, 10253) 40 | .WithPortBinding(10254, 10254) 41 | .WithPortBinding(10255, 10255) 42 | .WithEnvironment("AZURE_COSMOS_EMULATOR_PARTITION_COUNT", "2") 43 | .WithEnvironment("AZURE_COSMOS_EMULATOR_IP_ADDRESS_OVERRIDE", "127.0.0.1") 44 | .WithEnvironment("AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE", "true") 45 | .WithCleanUp(false) 46 | .WithWaitStrategy(Wait.ForUnixContainer() 47 | .UntilPortIsAvailable(8081)) 48 | .Build(); 49 | 50 | _dockerClient = new DockerClientConfiguration().CreateClient(); 51 | } 52 | 53 | public IDatabaseCollection Collection => 54 | _database.GetCollection(); 55 | 56 | public string GenerateId() 57 | { 58 | return $"{Guid.NewGuid():N}"; 59 | } 60 | 61 | public async Task InitializeAsync() 62 | { 63 | ushort publicPort = 0; 64 | 65 | try 66 | { 67 | await _cosmosTestContainer.StartAsync(); 68 | 69 | containerExsist = false; 70 | } 71 | catch (Exception ex) //TODO catch name already using exception 72 | { 73 | containerExsist = true; 74 | } 75 | 76 | if (!containerExsist) 77 | { 78 | publicPort = _cosmosTestContainer.GetMappedPublicPort(privatePort); 79 | containerId = _cosmosTestContainer.Id; 80 | } 81 | else 82 | { 83 | var listContainers = await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters()); 84 | 85 | ContainerListResponse containerListResponse = listContainers.FirstOrDefault(container => container.Names.Contains($"/{containerName}")); 86 | 87 | if (containerListResponse != null) 88 | { 89 | publicPort = containerListResponse.Ports.Single(port => port.PrivatePort == privatePort).PublicPort; 90 | 91 | containerId = containerListResponse.ID; 92 | } 93 | } 94 | 95 | 96 | _database = new CosmosDatabase(new CosmosOptions 97 | { 98 | ConnectionString = 99 | $"AccountEndpoint=https://localhost:{publicPort}/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", 100 | DatabaseName = "database", 101 | CollectionName = $"testContainer", 102 | AllowTableCreation = true, 103 | CosmosClientOptions = new CosmosClientOptions() 104 | { 105 | HttpClientFactory = () => 106 | { 107 | HttpMessageHandler httpMessageHandler = new HttpClientHandler() 108 | { 109 | ServerCertificateCustomValidationCallback = (_, _, _, _) => true 110 | }; 111 | 112 | return new HttpClient(httpMessageHandler); 113 | }, 114 | ConnectionMode = ConnectionMode.Gateway 115 | }, 116 | }); 117 | 118 | 119 | await _database.InitializeAsync(); 120 | } 121 | 122 | public async Task DisposeAsync() 123 | { 124 | // await _database.DeleteAsync(); 125 | await _database.DisposeAsync(); 126 | 127 | /* _testOutputHelper.WriteLine($"Cosmos container State:{_cosmosContainer.State}"); 128 | _testOutputHelper.WriteLine("=STOP=");*/ 129 | } 130 | 131 | public async void Dispose() 132 | { 133 | 134 | await _dockerClient.Containers.RemoveContainerAsync(containerId, 135 | new ContainerRemoveParameters 136 | { 137 | Force = true 138 | }); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/DynamoDBTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Docker.DotNet; 5 | using Docker.DotNet.Models; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | using ManagedCode.Database.Core; 9 | using ManagedCode.Database.DynamoDB; 10 | using ManagedCode.Database.Tests.Common; 11 | using Xunit; 12 | 13 | namespace ManagedCode.Database.Tests.TestContainers; 14 | 15 | [CollectionDefinition(nameof(DynamoDBTestContainer))] 16 | public class DynamoDBTestContainer : ITestContainer, 17 | ICollectionFixture, IDisposable 18 | { 19 | // private readonly ITestOutputHelper _testOutputHelper; 20 | private readonly TestcontainersContainer _dynamoDBTestContainer; 21 | private DynamoDBDatabase _dbDatabase; 22 | private DockerClient _dockerClient; 23 | private const string containerName = "dynamoContainer"; 24 | private const ushort privatePort = 8000; 25 | private bool containerExsist = false; 26 | private string containerId; 27 | 28 | public DynamoDBTestContainer() 29 | { 30 | //_testOutputHelper = testOutputHelper; 31 | _dynamoDBTestContainer = new TestcontainersBuilder() 32 | .WithImage("amazon/dynamodb-local") 33 | .WithName(containerName) 34 | .WithPortBinding(privatePort, true) 35 | .WithCleanUp(false) 36 | .WithWaitStrategy(Wait.ForUnixContainer() 37 | .UntilPortIsAvailable(privatePort)) 38 | .Build(); 39 | 40 | _dockerClient = new DockerClientConfiguration().CreateClient(); 41 | } 42 | 43 | public IDatabaseCollection Collection => 44 | _dbDatabase.GetCollection(); 45 | 46 | public string GenerateId() 47 | { 48 | return Guid.NewGuid().ToString(); 49 | } 50 | 51 | public async Task InitializeAsync() 52 | { 53 | ushort publicPort = 0; 54 | 55 | try 56 | { 57 | await _dynamoDBTestContainer.StartAsync(); 58 | 59 | containerExsist = false; 60 | } 61 | catch (Exception ex) //TODO catch name already using exception 62 | { 63 | containerExsist = true; 64 | } 65 | 66 | if (!containerExsist) 67 | { 68 | publicPort = _dynamoDBTestContainer.GetMappedPublicPort(privatePort); 69 | 70 | containerId = _dynamoDBTestContainer.Id; 71 | } 72 | else 73 | { 74 | var listContainers = await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters()); 75 | 76 | ContainerListResponse containerListResponse = listContainers.Single(container => container.Names.Contains($"/{containerName}")); 77 | 78 | if (containerListResponse != null) 79 | { 80 | publicPort = containerListResponse.Ports.Single(port => port.PrivatePort == privatePort).PublicPort; 81 | 82 | containerId = containerListResponse.ID; 83 | } 84 | } 85 | 86 | _dbDatabase = new DynamoDBDatabase(new DynamoDBOptions() 87 | { 88 | ServiceURL = $"http://localhost:{publicPort}", 89 | AuthenticationRegion = "eu-central-1", 90 | AccessKey = $"AccessKey", 91 | SecretKey = $"SecretKey", 92 | DataBaseName = $"db{Guid.NewGuid().ToString("N")}", 93 | CollectionName = $"collection{Guid.NewGuid().ToString("N")}", 94 | }); 95 | 96 | await _dbDatabase.InitializeAsync(); 97 | 98 | /*_testOutputHelper.WriteLine($"DynamoDb container State:{_dynamoDBTestContainer.State}"); 99 | _testOutputHelper.WriteLine("=START=");*/ 100 | } 101 | 102 | public async Task DisposeAsync() 103 | { 104 | await _dbDatabase.DisposeAsync(); 105 | 106 | //_testOutputHelper.WriteLine($"DynamoDb container State:{_dynamoDBContainer.State}"); 107 | //_testOutputHelper.WriteLine("=STOP="); 108 | } 109 | 110 | public async void Dispose() 111 | { 112 | 113 | await _dockerClient.Containers.RemoveContainerAsync(containerId, 114 | new ContainerRemoveParameters 115 | { 116 | Force = true 117 | }); 118 | } 119 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/ITestContainer.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using Xunit; 3 | 4 | namespace ManagedCode.Database.Tests.TestContainers; 5 | 6 | public interface ITestContainer : IAsyncLifetime where TItem : IItem 7 | { 8 | public IDatabaseCollection Collection { get; } 9 | 10 | public TId GenerateId(); 11 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/InMemoryTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | using ManagedCode.Database.Core; 4 | using ManagedCode.Database.Core.InMemory; 5 | using ManagedCode.Database.Tests.Common; 6 | 7 | namespace ManagedCode.Database.Tests.TestContainers; 8 | 9 | public class InMemoryTestContainer : ITestContainer 10 | { 11 | private static volatile int _count; 12 | private InMemoryDatabase _database; 13 | 14 | public IDatabaseCollection Collection => _database.GetCollection(); 15 | 16 | public int GenerateId() 17 | { 18 | Interlocked.Increment(ref _count); 19 | return _count; 20 | } 21 | 22 | public async Task InitializeAsync() 23 | { 24 | _database = new InMemoryDatabase(); 25 | await _database.InitializeAsync(); 26 | } 27 | 28 | public async Task DisposeAsync() 29 | { 30 | await _database.DisposeAsync(); 31 | } 32 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/LiteDBTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using ManagedCode.Database.Core; 5 | using ManagedCode.Database.LiteDB; 6 | using ManagedCode.Database.Tests.Common; 7 | 8 | namespace ManagedCode.Database.Tests.TestContainers; 9 | 10 | public class LiteDBTestContainer : ITestContainer 11 | { 12 | private readonly LiteDBDatabase _database; 13 | private readonly string _databasePath; 14 | 15 | public LiteDBTestContainer() 16 | { 17 | _databasePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.db"); 18 | 19 | _database = new LiteDBDatabase(new LiteDBOptions 20 | { 21 | ConnectionString = _databasePath, 22 | }); 23 | } 24 | 25 | public IDatabaseCollection Collection => 26 | _database.GetCollection(); 27 | 28 | public string GenerateId() 29 | { 30 | return Guid.NewGuid().ToString(); 31 | } 32 | 33 | public async Task InitializeAsync() 34 | { 35 | await _database.InitializeAsync(); 36 | } 37 | 38 | public async Task DisposeAsync() 39 | { 40 | await _database.DisposeAsync(); 41 | File.Delete(_databasePath); 42 | } 43 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/MongoDBTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using Docker.DotNet; 5 | using Docker.DotNet.Models; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | using ManagedCode.Database.Core; 9 | using ManagedCode.Database.MongoDB; 10 | using ManagedCode.Database.Tests.Common; 11 | using MongoDB.Bson; 12 | using Xunit; 13 | 14 | namespace ManagedCode.Database.Tests.TestContainers; 15 | 16 | [CollectionDefinition(nameof(MongoDBTestContainer))] 17 | public class MongoDBTestContainer : ITestContainer, 18 | ICollectionFixture, IDisposable 19 | { 20 | //private readonly ITestOutputHelper _testOutputHelper; 21 | private readonly TestcontainersContainer _mongoDBTestContainer; 22 | private MongoDBDatabase _dbDatabase; 23 | private DockerClient _dockerClient; 24 | private const string containerName = "mongoContainer"; 25 | private const ushort privatePort = 27017; 26 | private bool containerExsist = false; 27 | private string containerId; 28 | 29 | public MongoDBTestContainer() 30 | { 31 | //_testOutputHelper = testOutputHelper; 32 | 33 | _mongoDBTestContainer = new TestcontainersBuilder() 34 | .WithImage("mongo") 35 | .WithName(containerName) 36 | .WithPortBinding(privatePort, true) 37 | .WithCleanUp(false) 38 | .WithWaitStrategy(Wait.ForUnixContainer() 39 | .UntilPortIsAvailable(privatePort)) 40 | .Build(); 41 | 42 | _dockerClient = new DockerClientConfiguration().CreateClient(); 43 | } 44 | 45 | public IDatabaseCollection Collection => 46 | _dbDatabase.GetCollection(); 47 | 48 | public ObjectId GenerateId() 49 | { 50 | return ObjectId.GenerateNewId(); 51 | } 52 | 53 | public async Task InitializeAsync() 54 | { 55 | ushort publicPort = 0; 56 | 57 | try 58 | { 59 | await _mongoDBTestContainer.StartAsync(); 60 | 61 | containerExsist = false; 62 | } 63 | catch (Exception ex) //TODO catch name already using exception 64 | { 65 | containerExsist = true; 66 | } 67 | 68 | if (!containerExsist) 69 | { 70 | publicPort = _mongoDBTestContainer.GetMappedPublicPort(privatePort); 71 | containerId = _mongoDBTestContainer.Id; 72 | } 73 | else 74 | { 75 | var listContainers = await _dockerClient.Containers.ListContainersAsync(new ContainersListParameters()); 76 | 77 | ContainerListResponse containerListResponse = listContainers.Single(container => container.Names.Contains($"/{containerName}")); 78 | 79 | if (containerListResponse != null) 80 | { 81 | publicPort = containerListResponse.Ports.Single(port => port.PrivatePort == privatePort).PublicPort; 82 | 83 | containerId = containerListResponse.ID; 84 | } 85 | } 86 | 87 | _dbDatabase = new MongoDBDatabase(new MongoDBOptions() 88 | { 89 | ConnectionString = $"mongodb://localhost:{publicPort}", 90 | DataBaseName = $"db{Guid.NewGuid().ToString("N")}", 91 | }); 92 | 93 | await _dbDatabase.InitializeAsync(); 94 | 95 | //_testOutputHelper.WriteLine($"Mongo container State:{_mongoContainer.State}"); 96 | //_testOutputHelper.WriteLine("=START="); 97 | } 98 | 99 | public async Task DisposeAsync() 100 | { 101 | await _dbDatabase.DisposeAsync(); 102 | 103 | // _testOutputHelper.WriteLine($"Mongo container State:{_mongoDBContainer.State}"); 104 | //_testOutputHelper.WriteLine("=STOP="); 105 | } 106 | 107 | public async void Dispose() 108 | { 109 | 110 | await _dockerClient.Containers.RemoveContainerAsync(containerId, 111 | new ContainerRemoveParameters 112 | { 113 | Force = true 114 | }); 115 | } 116 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/SQLiteTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using ManagedCode.Database.Core; 6 | using ManagedCode.Database.SQLite; 7 | using ManagedCode.Database.Tests.Common; 8 | 9 | namespace ManagedCode.Database.Tests.TestContainers; 10 | 11 | public class SQLiteTestContainer : ITestContainer 12 | { 13 | private static volatile int _count; 14 | private readonly SQLiteDatabase _database; 15 | 16 | public SQLiteTestContainer() 17 | { 18 | var path = Path.Combine(Environment.CurrentDirectory, $"{Guid.NewGuid():N}.db"); 19 | 20 | _database = new SQLiteDatabase(new SQLiteRepositoryOptions 21 | { 22 | ConnectionString = path, 23 | }); 24 | } 25 | 26 | public IDatabaseCollection Collection => 27 | _database.GetCollection(); 28 | 29 | public async Task InitializeAsync() 30 | { 31 | await _database.InitializeAsync(); 32 | } 33 | 34 | public async Task DisposeAsync() 35 | { 36 | await _database.DeleteAsync(); 37 | } 38 | 39 | public int GenerateId() 40 | { 41 | return Interlocked.Increment(ref _count); 42 | } 43 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/TestContainers/ZoneTreeTestContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading.Tasks; 4 | using ManagedCode.Database.Core; 5 | using ManagedCode.Database.Tests.Common; 6 | using ManagedCode.Database.ZoneTree; 7 | 8 | namespace ManagedCode.Database.Tests.TestContainers; 9 | 10 | public class ZoneTreeTestContainer : ITestContainer 11 | { 12 | private readonly ZoneTreeDatabase _database; 13 | 14 | public ZoneTreeTestContainer() 15 | { 16 | var databasePath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}"); 17 | 18 | _database = new ZoneTreeDatabase(new ZoneTreeOptions() 19 | { 20 | Path = databasePath, 21 | }); 22 | } 23 | 24 | public IDatabaseCollection Collection => 25 | _database.GetCollection(); 26 | 27 | public string GenerateId() 28 | { 29 | return $"{Guid.NewGuid():N}"; 30 | } 31 | 32 | public async Task InitializeAsync() 33 | { 34 | await _database.InitializeAsync(); 35 | } 36 | 37 | public async Task DisposeAsync() 38 | { 39 | await _database.DeleteAsync(); 40 | } 41 | } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/ZoneTreeTests/ZoneTreeCollectionTests.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Collections.Generic; 3 | // using System.Threading.Tasks; 4 | // using FluentAssertions; 5 | // using ManagedCode.Database.Tests.BaseTests; 6 | // using ManagedCode.Database.Tests.Common; 7 | // using ManagedCode.Database.Tests.TestContainers; 8 | // 9 | // namespace ManagedCode.Database.Tests.ZoneTreeTests; 10 | // 11 | // public class ZoneTreeCollectionTests : BaseCollectionTests 12 | // { 13 | // public ZoneTreeCollectionTests() : base(new ZoneTreeTestContainer()) 14 | // { 15 | // } 16 | // 17 | // public override async Task InsertOrUpdateListOfItems() 18 | // { 19 | // // Arrange 20 | // int itemsCount = 5; 21 | // int updatedItemsCount = 0; 22 | // List list = new(); 23 | // 24 | // for (var i = 0; i < itemsCount; i++) 25 | // { 26 | // list.Add(CreateNewItem()); 27 | // } 28 | // 29 | // var itemsInsert = await Collection.InsertOrUpdateAsync(list); 30 | // 31 | // foreach (var item in list) 32 | // { 33 | // item.DateTimeData = DateTime.Now.AddDays(-1); 34 | // } 35 | // 36 | // // Act 37 | // var itemsUpdate = await Collection.InsertOrUpdateAsync(list); 38 | // //TODO: LiteDB must be 100, but result 0 39 | // 40 | // // Assert 41 | // itemsUpdate.Should().Be(updatedItemsCount); 42 | // itemsInsert.Should().Be(itemsCount); 43 | // list.Count.Should().Be(itemsCount); 44 | // } 45 | // } -------------------------------------------------------------------------------- /ManagedCode.Database.Tests/ZoneTreeTests/ZoneTreeQueryableTests.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Threading.Tasks; 3 | // using FluentAssertions; 4 | // using ManagedCode.Database.Tests.BaseTests; 5 | // using ManagedCode.Database.Tests.Common; 6 | // using ManagedCode.Database.Tests.TestContainers; 7 | // 8 | // namespace ManagedCode.Database.Tests.ZoneTreeTests; 9 | // 10 | // public class ZoneTreeQueryableTests : BaseQueryableTests 11 | // { 12 | // public ZoneTreeQueryableTests() : base(new ZoneTreeTestContainer()) 13 | // { 14 | // } 15 | // 16 | // public override async Task ThenByDescending_TakeNull_ReturnException() 17 | // { 18 | // // Arrange 19 | // int itemsCountToInsert = 1; 20 | // 21 | // await CreateAndInsertItemsAsync(itemsCountToInsert); 22 | // 23 | // // Act 24 | // var itemsResult = () => Collection.Query 25 | // .OrderBy(o => o.StringData) 26 | // .ThenByDescending(null) 27 | // .ToListAsync(); 28 | // 29 | // // Assert 30 | // await itemsResult 31 | // .Should() 32 | // .ThrowAsync(); 33 | // } 34 | // 35 | // public override async Task ThenBy_TakeNull_ReturnException() 36 | // { 37 | // // Arrange 38 | // int itemsCountToInsert = 1; 39 | // 40 | // await CreateAndInsertItemsAsync(itemsCountToInsert); 41 | // 42 | // // Act 43 | // var itemsResult = () => Collection.Query 44 | // .OrderBy(o => o.StringData) 45 | // .ThenBy(null) 46 | // .ToListAsync(); 47 | // 48 | // // Assert 49 | // await itemsResult 50 | // .Should() 51 | // .ThrowAsync(); 52 | // } 53 | // } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/IZoneTreeItem.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.ZoneTree; 2 | 3 | public interface IZoneTreeItem 4 | { 5 | public T Id { get; set; } 6 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/JsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Tenray.ZoneTree.Serializers; 3 | 4 | namespace ManagedCode.Database.ZoneTree; 5 | 6 | internal class JsonSerializer : ISerializer 7 | { 8 | public T Deserialize(byte[] bytes) 9 | { 10 | return JsonSerializer.Deserialize(bytes); 11 | } 12 | 13 | public byte[] Serialize(in T entry) 14 | { 15 | return JsonSerializer.SerializeToUtf8Bytes(entry); 16 | } 17 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ManagedCode.Database.ZoneTree.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | 11 6 | True 7 | enable 8 | enable 9 | 10 | 11 | 12 | 13 | ManagedCode.Database.ZoneTree 14 | ManagedCode.Database.ZoneTree 15 | Repository for ZoneTree 16 | managedcode, repository, ZoneTree 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/StorageType.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.ZoneTree; 2 | 3 | public enum StorageType 4 | { 5 | File, 6 | Blob 7 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeCollection.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using ManagedCode.Database.Core.Exceptions; 3 | 4 | namespace ManagedCode.Database.ZoneTree; 5 | 6 | public class ZoneTreeCollection : BaseDatabaseCollection where TItem : IItem 7 | { 8 | private readonly ZoneTreeWrapper _zoneTree; 9 | 10 | internal ZoneTreeCollection(ZoneTreeCollectionOptions options) 11 | { 12 | _zoneTree = new ZoneTreeWrapper(options); 13 | } 14 | 15 | public override ICollectionQueryable Query => new ZoneTreeCollectionQueryable(_zoneTree); 16 | 17 | 18 | public override void Dispose() 19 | { 20 | _zoneTree.Dispose(); 21 | } 22 | 23 | public override ValueTask DisposeAsync() 24 | { 25 | _zoneTree.Dispose(); 26 | return ValueTask.CompletedTask; 27 | } 28 | 29 | protected override async Task InsertInternalAsync(TItem item, CancellationToken cancellationToken = default) 30 | { 31 | await Task.Yield(); 32 | 33 | if (!_zoneTree.Insert(item.Id, item)) 34 | { 35 | throw new DatabaseException("The specified entity already exists."); 36 | } 37 | 38 | return _zoneTree.Get(item.Id)!; 39 | } 40 | 41 | protected override async Task InsertInternalAsync(IEnumerable items, 42 | CancellationToken cancellationToken = default) 43 | { 44 | return await Task.Run(() => items.Count(item => _zoneTree.Insert(item.Id, item)), cancellationToken); 45 | } 46 | 47 | protected override async Task UpdateInternalAsync(TItem item, CancellationToken cancellationToken = default) 48 | { 49 | await Task.Yield(); 50 | 51 | if (!_zoneTree.Update(item.Id, item)) 52 | { 53 | throw new DatabaseException("Entity not found in collection."); 54 | } 55 | 56 | return _zoneTree.Get(item.Id)!; 57 | } 58 | 59 | protected override async Task UpdateInternalAsync(IEnumerable items, 60 | CancellationToken cancellationToken = default) 61 | { 62 | return await Task.Run(() => items.Count(item => _zoneTree.Update(item.Id, item)), cancellationToken); 63 | } 64 | 65 | protected override async Task DeleteInternalAsync(TId id, CancellationToken cancellationToken = default) 66 | { 67 | await Task.Yield(); 68 | return _zoneTree.Delete(id); 69 | } 70 | 71 | protected override async Task DeleteInternalAsync(TItem item, CancellationToken cancellationToken = default) 72 | { 73 | await Task.Yield(); 74 | return _zoneTree.Delete(item.Id); 75 | } 76 | 77 | protected override async Task DeleteInternalAsync(IEnumerable ids, 78 | CancellationToken cancellationToken = default) 79 | { 80 | return await Task.Run(() => ids.Count(id => _zoneTree.Delete(id)), cancellationToken); 81 | } 82 | 83 | protected override async Task DeleteInternalAsync(IEnumerable items, 84 | CancellationToken cancellationToken = default) 85 | { 86 | return await Task.Run(() => items.Count(item => _zoneTree.Delete(item.Id)), cancellationToken); 87 | } 88 | 89 | protected override Task DeleteCollectionInternalAsync(CancellationToken cancellationToken = default) 90 | { 91 | _zoneTree.DeleteAll(); 92 | return Task.FromResult(true); 93 | } 94 | 95 | protected override async Task InsertOrUpdateInternalAsync(TItem item, 96 | CancellationToken cancellationToken = default) 97 | { 98 | await Task.Yield(); 99 | 100 | _zoneTree.Upsert(item.Id, item); 101 | return _zoneTree.Get(item.Id)!; 102 | } 103 | 104 | protected override async Task InsertOrUpdateInternalAsync(IEnumerable items, 105 | CancellationToken cancellationToken = default) 106 | { 107 | return await Task.Run(() => items.Count(item => _zoneTree.InsertOrUpdate(item.Id, item)), cancellationToken); 108 | } 109 | 110 | protected override async Task GetInternalAsync(TId id, CancellationToken cancellationToken = default) 111 | { 112 | await Task.Yield(); 113 | return _zoneTree.Get(id); 114 | } 115 | 116 | protected override async Task CountInternalAsync(CancellationToken cancellationToken = default) 117 | { 118 | return await Task.Run(() => _zoneTree.Count(), cancellationToken); 119 | } 120 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeCollectionOptions.cs: -------------------------------------------------------------------------------- 1 | using Tenray.ZoneTree.Comparers; 2 | using Tenray.ZoneTree.Options; 3 | using Tenray.ZoneTree.Serializers; 4 | 5 | namespace ManagedCode.Database.ZoneTree; 6 | 7 | internal class ZoneTreeCollectionOptions 8 | { 9 | public string Path { get; set; } 10 | public string ConnectionString { get; set; } 11 | public StorageType StorageType { get; set; } 12 | 13 | public ISerializer KeySerializer { get; set; } 14 | public ISerializer ValueSerializer { get; set; } 15 | public IRefComparer Comparer { get; set; } 16 | 17 | public WriteAheadLogMode WALMode { get; set; } 18 | 19 | public DiskSegmentMode DiskSegmentMode { get; set; } 20 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeCollectionQueryable.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using ManagedCode.Database.Core; 3 | 4 | namespace ManagedCode.Database.ZoneTree; 5 | 6 | public class ZoneTreeCollectionQueryable : BaseCollectionQueryable where TItem : IItem 7 | { 8 | private readonly ZoneTreeWrapper _zoneTree; 9 | 10 | internal ZoneTreeCollectionQueryable(ZoneTreeWrapper zoneTree) 11 | { 12 | _zoneTree = zoneTree; 13 | } 14 | 15 | public override async IAsyncEnumerable ToAsyncEnumerable( 16 | [EnumeratorCancellation] CancellationToken cancellationToken = default) 17 | { 18 | await Task.Yield(); 19 | 20 | foreach (var item in ApplyPredicates()) 21 | { 22 | cancellationToken.ThrowIfCancellationRequested(); 23 | yield return item; 24 | } 25 | } 26 | 27 | public override async Task FirstOrDefaultAsync(CancellationToken cancellationToken = default) 28 | { 29 | await Task.Yield(); 30 | 31 | return ApplyPredicates().FirstOrDefault(); 32 | } 33 | 34 | public override async Task CountAsync(CancellationToken cancellationToken = default) 35 | { 36 | var query = ApplyPredicates(); 37 | 38 | return await Task.Run(() => query.LongCount(), cancellationToken); 39 | } 40 | 41 | public override async Task DeleteAsync(CancellationToken cancellationToken = default) 42 | { 43 | await Task.Yield(); 44 | 45 | var count = 0; 46 | 47 | foreach (var item in ApplyPredicates()) 48 | { 49 | cancellationToken.ThrowIfCancellationRequested(); 50 | 51 | _zoneTree.Delete(item.Id); 52 | count++; 53 | } 54 | 55 | return count; 56 | } 57 | 58 | private IEnumerable ApplyPredicates() 59 | { 60 | var enumerable = _zoneTree.Enumerate(); 61 | 62 | foreach (var predicate in Predicates) 63 | enumerable = predicate.QueryType switch 64 | { 65 | QueryType.Where => enumerable.Where(x => predicate.ExpressionBool.Compile().Invoke(x)), 66 | QueryType.OrderBy => enumerable 67 | .OrderBy(x => predicate.ExpressionObject.Compile().Invoke(x)), 68 | QueryType.OrderByDescending => enumerable 69 | .OrderByDescending(x => predicate.ExpressionObject.Compile().Invoke(x)), 70 | QueryType.ThenBy => (enumerable as IOrderedEnumerable)! 71 | .ThenBy(x => predicate.ExpressionObject.Compile().Invoke(x)), 72 | QueryType.ThenByDescending => (enumerable as IOrderedEnumerable)! 73 | .ThenByDescending(x => predicate.ExpressionObject.Compile().Invoke(x)), 74 | QueryType.Take => predicate.Count.HasValue ? enumerable.Take(predicate.Count.Value) : enumerable, 75 | QueryType.Skip => enumerable.Skip(predicate.Count!.Value), 76 | _ => enumerable 77 | }; 78 | 79 | return enumerable; 80 | } 81 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeDatabase.cs: -------------------------------------------------------------------------------- 1 | using ManagedCode.Database.Core; 2 | using ManagedCode.Database.Core.Common; 3 | 4 | namespace ManagedCode.Database.ZoneTree; 5 | 6 | public class ZoneTreeDatabase : BaseDatabase 7 | { 8 | private readonly ZoneTreeOptions _options; 9 | private readonly Dictionary _collection = new(); 10 | 11 | public ZoneTreeDatabase(ZoneTreeOptions options) 12 | { 13 | _options = options; 14 | } 15 | 16 | protected override Task InitializeAsyncInternal(CancellationToken token = default) 17 | { 18 | NativeClient = this; 19 | Directory.CreateDirectory(_options.Path); 20 | 21 | return Task.CompletedTask; 22 | } 23 | 24 | protected override async ValueTask DisposeAsyncInternal() 25 | { 26 | await Task.Run(Dispose); 27 | } 28 | 29 | protected override void DisposeInternal() 30 | { 31 | lock (NativeClient) 32 | { 33 | foreach (var table in _collection) 34 | { 35 | table.Value.Dispose(); 36 | } 37 | } 38 | } 39 | 40 | public override async Task DeleteAsync(CancellationToken token = default) 41 | { 42 | await DisposeAsyncInternal(); 43 | Directory.Delete(_options.Path, true); 44 | } 45 | 46 | public ZoneTreeCollection GetCollection() where TItem : IItem 47 | { 48 | if (!IsInitialized) throw new DatabaseNotInitializedException(GetType()); 49 | 50 | lock (NativeClient) 51 | { 52 | var collectionName = typeof(TItem).FullName!; 53 | 54 | if (_collection.TryGetValue(collectionName, out var collection)) 55 | { 56 | return (ZoneTreeCollection)collection; 57 | } 58 | 59 | ZoneTreeCollectionOptions collectionOptions = new() 60 | { 61 | Path = Path.Combine(_options.Path, collectionName), 62 | WALMode = _options.WALMode, 63 | DiskSegmentMode = _options.DiskSegmentMode, 64 | StorageType = _options.StorageType, 65 | ValueSerializer = new JsonSerializer() 66 | }; 67 | 68 | var newCollection = new ZoneTreeCollection(collectionOptions); 69 | _collection.Add(collectionName, newCollection); 70 | return newCollection; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeItem.cs: -------------------------------------------------------------------------------- 1 | namespace ManagedCode.Database.ZoneTree; 2 | 3 | public class ZoneTreeItem : IZoneTreeItem 4 | { 5 | public Guid Id { get; set; } = Guid.NewGuid(); 6 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeOptions.cs: -------------------------------------------------------------------------------- 1 | using Tenray.ZoneTree.Options; 2 | 3 | namespace ManagedCode.Database.ZoneTree; 4 | 5 | public class ZoneTreeOptions 6 | { 7 | public string Path { get; set; } 8 | public string ConnectionString { get; set; } 9 | public StorageType StorageType { get; set; } 10 | public WriteAheadLogMode WALMode { get; set; } 11 | public DiskSegmentMode DiskSegmentMode { get; set; } 12 | } -------------------------------------------------------------------------------- /ManagedCode.Database.ZoneTree/ZoneTreeWrapper.cs: -------------------------------------------------------------------------------- 1 | using Tenray.ZoneTree; 2 | using Tenray.ZoneTree.AbstractFileStream; 3 | using Tenray.ZoneTree.Options; 4 | 5 | namespace ManagedCode.Database.ZoneTree; 6 | 7 | internal class ZoneTreeWrapper : IDisposable 8 | { 9 | private readonly IMaintainer _maintainer; 10 | private readonly IZoneTree _zoneTree; 11 | 12 | public ZoneTreeWrapper(ZoneTreeCollectionOptions options) 13 | { 14 | /*IFileStreamProvider streamProvider = options.StorageType switch 15 | { 16 | StorageType.Blob => new BlobFileStreamProvider(options.ConnectionString, options.Path), 17 | StorageType.File => new LocalFileStreamProvider(), 18 | _ => throw new ArgumentOutOfRangeException(nameof(options.StorageType), options.StorageType, null) 19 | };*/ 20 | var factory = new ZoneTreeFactory(new LocalFileStreamProvider()) 21 | //.SetKeySerializer(options.KeySerializer) 22 | .SetValueSerializer(options.ValueSerializer) 23 | //.SetComparer(options.Comparer) //StringOrdinalComparerAscending() 24 | .SetDataDirectory(options.Path) 25 | .SetWriteAheadLogDirectory(options.Path) 26 | .SetIsValueDeletedDelegate((in TValue? value) => value == null) 27 | .SetMarkValueDeletedDelegate((ref TValue? value) => value = default) 28 | 29 | //.SetIsValueDeletedDelegate((in Deletable x) => x.IsDeleted) 30 | //.SetMarkValueDeletedDelegate((ref Deletable x) => x.IsDeleted = true) 31 | .ConfigureWriteAheadLogOptions(x => 32 | { 33 | // x.CompressionBlockSize = 1024 * 1024 * 20; 34 | x.WriteAheadLogMode = options.WALMode; 35 | //x.EnableIncrementalBackup = true; 36 | }) 37 | .Configure(x => 38 | { 39 | x.DiskSegmentOptions.CompressionMethod = CompressionMethod.Brotli; 40 | x.DiskSegmentOptions.CompressionBlockSize = 1024 * 1024 * 30; 41 | x.DiskSegmentOptions.DiskSegmentMode = options.DiskSegmentMode; 42 | x.DiskSegmentOptions.BlockCacheLimit = 2; 43 | x.MutableSegmentMaxItemCount = 10_000; // number of data im memory 44 | }); 45 | 46 | 47 | _zoneTree = factory.OpenOrCreate(); 48 | _maintainer = _zoneTree.CreateMaintainer(); 49 | } 50 | 51 | public void Dispose() 52 | { 53 | _maintainer.CompleteRunningTasks(); 54 | _maintainer.Dispose(); 55 | _zoneTree.Dispose(); 56 | } 57 | 58 | public void Maintenance() 59 | { 60 | _maintainer.CompleteRunningTasks(); 61 | // TODO: check 62 | _zoneTree.Maintenance.MoveMutableSegmentForward(); 63 | _zoneTree.Maintenance.StartMergeOperation()?.Join(); 64 | } 65 | 66 | public bool Insert(TKey key, TValue value) 67 | { 68 | return _zoneTree.TryAtomicAdd(key, value); 69 | } 70 | 71 | public bool Update(TKey key, TValue value) 72 | { 73 | return _zoneTree.TryAtomicUpdate(key, value); 74 | } 75 | 76 | public bool InsertOrUpdate(TKey key, TValue value) 77 | { 78 | return _zoneTree.TryAtomicAddOrUpdate(key, value, (ref TValue? _) => true); 79 | } 80 | 81 | public void Upsert(TKey key, TValue value) 82 | { 83 | _zoneTree.Upsert(key, value); 84 | } 85 | 86 | public TValue? Get(TKey key) 87 | { 88 | return _zoneTree.TryGet(in key, out var value) 89 | ? value 90 | : default; 91 | } 92 | 93 | public bool Contains(TKey key) 94 | { 95 | return _zoneTree.ContainsKey(key); 96 | } 97 | 98 | public bool Delete(TKey key) 99 | { 100 | return _zoneTree.TryDelete(in key); 101 | } 102 | 103 | public void DeleteAll() 104 | { 105 | _zoneTree.Maintenance.DestroyTree(); 106 | } 107 | 108 | public long Count() 109 | { 110 | //add more cache logic 111 | return _zoneTree.Count(); 112 | } 113 | 114 | public IEnumerable Enumerate() 115 | { 116 | using var iterator = _zoneTree.CreateIterator(); 117 | while (iterator.Next()) yield return iterator.CurrentValue; 118 | } 119 | 120 | public IEnumerable EnumerateReverse() 121 | { 122 | using var iterator = _zoneTree.CreateReverseIterator(); 123 | while (iterator.Next()) yield return iterator.CurrentValue; 124 | } 125 | } -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/managedcode/Database/3b7b7e8fec13c5918cfa12d51e939b2d31ff2835/logo.png -------------------------------------------------------------------------------- /testEnvironments.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1", 3 | "environments": [ 4 | // See https://aka.ms/remotetesting for more details 5 | // about how to configure remote environments. 6 | //{ 7 | // "name": "WSL Ubuntu", 8 | // "type": "wsl", 9 | // "wslDistribution": "Ubuntu" 10 | //}, 11 | //{ 12 | // "name": "Docker dotnet/sdk", 13 | // "type": "docker", 14 | // "dockerImage": "mcr.microsoft.com/dotnet/sdk" 15 | //} 16 | ] 17 | } --------------------------------------------------------------------------------