From 5a2d0a2c7a11359e9674bdb3983fd3e7045db1fe Mon Sep 17 00:00:00 2001 From: Hendrik Bulens Date: Fri, 6 Oct 2023 08:57:23 +0200 Subject: [PATCH 1/5] Upgrade to .NET 8 and remove legacy full framework projects --- README.md | 8 +- assets/db.svg | 1 - assets/logo.png | Bin 0 -> 183777 bytes azure-pipelines.yaml | 28 -- src/Dime.Repositories.sln | 29 +- src/Dime.Repositories.sln.DotSettings.user | 20 -- src/NuGet.Config | 6 - .../Dime.Repositories.Sql.csproj | 20 +- .../IStoredProcedureRepository.cs | 9 - .../Dime.Repositories.csproj | 11 +- .../Configuration/RepositoryConfiguration.cs | 33 --- .../CachedNamedDbContextFactory.cs | 121 -------- .../INamedDbContextFactory.cs | 19 -- ...es.Sql.EntityFramework.NetFramework.csproj | 42 --- .../Exceptions/ConcurrencyException.cs | 28 -- .../ConstraintViolationException.cs | 48 ---- .../Exceptions/DatabaseAccessException.cs | 53 ---- .../Model Builder/DbContextModelBuilder.cs | 34 --- .../Model Builder/IModelBuilder.cs | 24 -- .../Async/AggregateRepositoryAsync.cs | 23 -- .../Repository/Async/CreateRepositoryAsync.cs | 77 ----- .../Repository/Async/DeleteRepositoryAsync.cs | 97 ------- .../Repository/Async/GetRepositoryAsync.cs | 160 ----------- .../Repository/Async/PagedRepositoryAsync.cs | 270 ------------------ .../Repository/Async/RepositoryAsync.cs | 175 ------------ .../Repository/Async/SqlRepositoryAsync.cs | 97 ------- .../Repository/Async/UpdateRepositoryAsync.cs | 75 ----- .../Repository/EfRepositoryFactory.cs | 34 --- .../Repository/Sync/AggregateRepository.cs | 21 -- .../Repository/Sync/CreateRepository.cs | 77 ----- .../Repository/Sync/DeleteRepository.cs | 81 ------ .../Repository/Sync/GetRepository.cs | 177 ------------ .../Repository/Sync/PagedRepository.cs | 214 -------------- .../Repository/Sync/Repository.cs | 126 -------- .../Sync/StoredProcedureRepository.cs | 73 ----- .../Repository/Sync/UpdateRepository.cs | 76 ----- .../Utilities/DataReaderExtensions.cs | 53 ---- .../Utilities/DbContextExtensions.cs | 26 -- .../Utilities/DbSetExtensions.cs | 23 -- .../Utilities/Extensions/EFExtensions.cs | 81 ------ .../Utilities/LinqOperationExtensions.cs | 31 -- .../Utilities/LinqOperationHelper.cs | 111 ------- .../Query Factory/SkipQueryFactory.cs | 83 ------ .../Query Factory/SortingQueryFactory.cs | 107 ------- .../Query Factory/TakeQueryFactory.cs | 23 -- ...ories.Sql.EntityFramework.Shared.projitems | 36 --- ...sitories.Sql.EntityFramework.Shared.shproj | 13 - .../Query Factory/GroupByQueryFactory.cs | 44 --- .../Query Factory/SelectQueryFactory.cs | 94 ------ .../Query Factory/WhereQueryFactory.cs | 29 -- .../Configuration/RepositoryConfiguration.cs | 0 ...e.Repositories.Sql.EntityFramework.csproj} | 18 +- .../Exceptions/ConcurrencyException.cs | 0 .../ConstraintViolationException.cs | 0 .../Exceptions/DatabaseAccessException.cs | 0 .../Model Builder/DbContextModelBuilder.cs | 0 .../Model Builder/IModelBuilder.cs | 0 .../Async/AggregateRepositoryAsync.cs | 0 .../Repository/Async/CreateRepositoryAsync.cs | 0 .../Repository/Async/DeleteRepositoryAsync.cs | 0 .../Repository/Async/GetRepositoryAsync.cs | 0 .../Repository/Async/PagedRepositoryAsync.cs | 0 .../Repository/Async/RepositoryAsync.cs | 0 .../Repository/Async/SqlRepositoryAsync.cs | 0 .../Repository/Async/UpdateRepositoryAsync.cs | 0 .../Repository/EfRepositoryFactory.cs | 0 .../Repository/IEfRepositoryFactory.cs | 0 .../Repository/Sync/AggregateRepository.cs | 0 .../Repository/Sync/CreateRepository.cs | 0 .../Repository/Sync/DeleteRepository.cs | 0 .../Repository/Sync/GetRepository.cs | 0 .../Repository/Sync/PagedRepository.cs | 0 .../Repository/Sync/Repository.cs | 0 .../Sync/StoredProcedureRepository.cs | 0 .../Repository/Sync/UpdateRepository.cs | 0 .../Utilities/DataReaderExtensions.cs | 0 .../Utilities/DbContextExtensions.cs | 0 .../Utilities/EFExtensions.cs | 0 .../Utilities/LinqOperationExtensions.cs | 0 .../Utilities/LinqOperationHelper.cs | 0 .../Query Factory/GroupByQueryFactory.cs | 0 .../Query Factory/SelectQueryFactory.cs | 0 .../Query Factory/SkipQueryFactory.cs | 0 .../Query Factory/SortingQueryFactory.cs | 0 .../Query Factory/TakeQueryFactory.cs | 0 .../Query Factory/WhereQueryFactory.cs | 0 src/test.runsettings | 30 -- .../AggregateRepositoryTests.cs | 81 ------ .../BloggingContext.cs | 54 ---- .../CreateRepositoryTests.cs | 37 --- .../DeleteRepositoryTests.cs | 56 ---- ....EntityFramework.NetFramework.Tests.csproj | 23 -- .../GetRepositoryTests.cs | 86 ------ .../PagedQueryRepositoryTests.cs | 45 --- .../UpdateRepositoryTests.cs | 57 ---- .../AggregateRepositoryTests.cs | 0 .../BloggingContext.cs | 0 .../CreateRepositoryTests.cs | 0 .../DeleteRepositoryTests.cs | 0 ...sitories.Sql.EntityFramework.Tests.csproj} | 14 +- .../GetRepositoryTests.cs | 0 .../UpdateRepositoryTests.cs | 0 102 files changed, 29 insertions(+), 3613 deletions(-) delete mode 100644 assets/db.svg create mode 100644 assets/logo.png delete mode 100644 azure-pipelines.yaml delete mode 100644 src/Dime.Repositories.sln.DotSettings.user delete mode 100644 src/NuGet.Config delete mode 100644 src/providers/EntityFramework.NetFramework/Configuration/RepositoryConfiguration.cs delete mode 100644 src/providers/EntityFramework.NetFramework/DbContext Factory/CachedNamedDbContextFactory.cs delete mode 100644 src/providers/EntityFramework.NetFramework/DbContext Factory/INamedDbContextFactory.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Dime.Repositories.Sql.EntityFramework.NetFramework.csproj delete mode 100644 src/providers/EntityFramework.NetFramework/Exceptions/ConcurrencyException.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Exceptions/ConstraintViolationException.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Exceptions/DatabaseAccessException.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Model Builder/DbContextModelBuilder.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Model Builder/IModelBuilder.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/AggregateRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/CreateRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/DeleteRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/GetRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/PagedRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/RepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/SqlRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Async/UpdateRepositoryAsync.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/EfRepositoryFactory.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/AggregateRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/CreateRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/DeleteRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/GetRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/PagedRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/Repository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/StoredProcedureRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Repository/Sync/UpdateRepository.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/DataReaderExtensions.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/DbContextExtensions.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/DbSetExtensions.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/Extensions/EFExtensions.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/LinqOperationExtensions.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/LinqOperationHelper.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SkipQueryFactory.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SortingQueryFactory.cs delete mode 100644 src/providers/EntityFramework.NetFramework/Utilities/Query Factory/TakeQueryFactory.cs delete mode 100644 src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.projitems delete mode 100644 src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.shproj delete mode 100644 src/providers/EntityFramework.Shared/Utilities/Query Factory/GroupByQueryFactory.cs delete mode 100644 src/providers/EntityFramework.Shared/Utilities/Query Factory/SelectQueryFactory.cs delete mode 100644 src/providers/EntityFramework.Shared/Utilities/Query Factory/WhereQueryFactory.cs rename src/providers/{EntityFramework.Shared => EntityFramework}/Configuration/RepositoryConfiguration.cs (100%) rename src/providers/{EntityFramework.NetCore/Dime.Repositories.Sql.EntityFramework.NetCore.csproj => EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj} (71%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Exceptions/ConcurrencyException.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Exceptions/ConstraintViolationException.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Exceptions/DatabaseAccessException.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Model Builder/DbContextModelBuilder.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Model Builder/IModelBuilder.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Async/AggregateRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Async/CreateRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Async/DeleteRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Async/GetRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Async/PagedRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Async/RepositoryAsync.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Async/SqlRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Async/UpdateRepositoryAsync.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/EfRepositoryFactory.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/IEfRepositoryFactory.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Sync/AggregateRepository.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Sync/CreateRepository.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Sync/DeleteRepository.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Sync/GetRepository.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Sync/PagedRepository.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Sync/Repository.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Repository/Sync/StoredProcedureRepository.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Repository/Sync/UpdateRepository.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/DataReaderExtensions.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/DbContextExtensions.cs (100%) rename src/providers/{EntityFramework.NetCore => EntityFramework}/Utilities/EFExtensions.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/LinqOperationExtensions.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/LinqOperationHelper.cs (100%) rename src/providers/{EntityFramework.NetFramework => EntityFramework}/Utilities/Query Factory/GroupByQueryFactory.cs (100%) rename src/providers/{EntityFramework.NetFramework => EntityFramework}/Utilities/Query Factory/SelectQueryFactory.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/Query Factory/SkipQueryFactory.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/Query Factory/SortingQueryFactory.cs (100%) rename src/providers/{EntityFramework.Shared => EntityFramework}/Utilities/Query Factory/TakeQueryFactory.cs (100%) rename src/providers/{EntityFramework.NetFramework => EntityFramework}/Utilities/Query Factory/WhereQueryFactory.cs (100%) delete mode 100644 src/test.runsettings delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/AggregateRepositoryTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/BloggingContext.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/CreateRepositoryTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/DeleteRepositoryTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests.csproj delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/GetRepositoryTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/PagedQueryRepositoryTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/UpdateRepositoryTests.cs rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/AggregateRepositoryTests.cs (100%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/BloggingContext.cs (100%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/CreateRepositoryTests.cs (100%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/DeleteRepositoryTests.cs (100%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests/Dime.Repositories.Sql.EntityFramework.NetCore.Tests.csproj => Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj} (58%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/GetRepositoryTests.cs (100%) rename src/test/{Dime.Repositories.Sql.EntityFramework.NetCore.Tests => Dime.Repositories.Sql.EntityFramework.Tests}/UpdateRepositoryTests.cs (100%) diff --git a/README.md b/README.md index fcb33d2..44a5f07 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -

Logo

+

Logo

-# Repositories - -[![Build Status](https://dev.azure.com/dimesoftware/Utilities/_apis/build/status/dimesoftware.repository?branchName=master)](https://dev.azure.com/dimesoftware/Utilities/_build/latest?definitionId=182&branchName=master) +
+

Repositories

+
## Introduction diff --git a/assets/db.svg b/assets/db.svg deleted file mode 100644 index ad486de..0000000 --- a/assets/db.svg +++ /dev/null @@ -1 +0,0 @@ -server status \ No newline at end of file diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6b236fa8217f7869980945577ae622f6c89fd54e GIT binary patch literal 183777 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 g~knA=^YrPD%6 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%0A`IvZ& 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 - C:\Users\hendr\AppData\Local\Temp\JetBrains\ReSharperPlatformVs16\vAny_16eaa788\CoverageData\_Dime.Repositories.288376933\Snapshot\snapshot.utdcvr - <SessionState ContinuousTestingMode="0" IsActive="True" Name="Repository_Count_NoPredicate_ShouldCountAll #4" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <Solution /> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="Repository_Count_NoPredicate_ShouldCountAll #3" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::4C40805D-D4F3-4D49-B2C4-F464FE4CD833::.NETCoreApp,Version=v3.1::Dime.Repositories.Sql.EntityFramework.NetCore.Tests.RepositoryTests.Repository_Count_NoPredicate_ShouldCountAll</TestId> - </TestAncestor> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="Repository_Count_NoPredicate_ShouldCountAll #2" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::4C40805D-D4F3-4D49-B2C4-F464FE4CD833::.NETCoreApp,Version=v3.1::Dime.Repositories.Sql.EntityFramework.NetCore.Tests.RepositoryTests.Repository_Count_NoPredicate_ShouldCountAll</TestId> - </TestAncestor> -</SessionState> - <SessionState ContinuousTestingMode="0" Name="Repository_Count_NoPredicate_ShouldCountAll" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> - <TestAncestor> - <TestId>MSTest::4C40805D-D4F3-4D49-B2C4-F464FE4CD833::.NETCoreApp,Version=v3.1::Dime.Repositories.Sql.EntityFramework.NetCore.Tests.RepositoryTests.Repository_Count_NoPredicate_ShouldCountAll</TestId> - </TestAncestor> -</SessionState> \ No newline at end of file diff --git a/src/NuGet.Config b/src/NuGet.Config deleted file mode 100644 index 3f0e003..0000000 --- a/src/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj index 3ac1eaf..a30bf67 100644 --- a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj +++ b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj @@ -5,9 +5,9 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.38 + 2.0.0.0-alpha.39 Dime Software - net461;net7.0 + net8.0 Dime.Repositories.Sql Dime.Repositories.Sql https://cdn.dime-software.com/dime-software/logo-shape.png @@ -19,7 +19,7 @@ Repository contracts with support for SQL databases True True - Copyright © 2022 + Copyright © 2023 https://github.com/dimesoftware/repository https://github.com/dimesoftware/repository @@ -31,17 +31,7 @@ - - - - - - - - - - - + + - diff --git a/src/core/Dime.Repositories.Sql/IStoredProcedureRepository.cs b/src/core/Dime.Repositories.Sql/IStoredProcedureRepository.cs index c13f3ba..50d45a9 100644 --- a/src/core/Dime.Repositories.Sql/IStoredProcedureRepository.cs +++ b/src/core/Dime.Repositories.Sql/IStoredProcedureRepository.cs @@ -1,16 +1,7 @@ using System.Collections.Generic; using System.Data.Common; - -#if NET461 - -using System.Data.SqlClient; - -#else - using Microsoft.Data.SqlClient; -#endif - namespace Dime.Repositories { public interface IStoredProcedureRepository diff --git a/src/core/Dime.Repositories/Dime.Repositories.csproj b/src/core/Dime.Repositories/Dime.Repositories.csproj index fe07a13..b503bc5 100644 --- a/src/core/Dime.Repositories/Dime.Repositories.csproj +++ b/src/core/Dime.Repositories/Dime.Repositories.csproj @@ -4,8 +4,8 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.48 - net461;net7.0 + 2.0.0.0-alpha.49 + net8.0 Dime.Repositories Dime.Repositories https://cdn.dime-software.com/dime-software/logo-shape.png @@ -17,7 +17,7 @@ true True True - Copyright © 2022 + Copyright © 2023 https://github.com/dimesoftware/repository https://github.com/dimesoftware/repository git @@ -25,9 +25,4 @@ MIT - - - - - diff --git a/src/providers/EntityFramework.NetFramework/Configuration/RepositoryConfiguration.cs b/src/providers/EntityFramework.NetFramework/Configuration/RepositoryConfiguration.cs deleted file mode 100644 index d58c617..0000000 --- a/src/providers/EntityFramework.NetFramework/Configuration/RepositoryConfiguration.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Dime.Repositories -{ - /// - /// Represents a repository configuration object - /// - public class RepositoryConfiguration - { - /// - /// Gets or sets the identifier of the tenant - /// - public string Tenant { get; set; } - - /// - /// Gets or sets the database connection - /// - public string Connection { get; set; } - - /// - /// Gets or sets the flag to indicate whether to leverage the UOW pattern and save in batch - /// - public bool SaveInBatch { get; set; } - - /// - /// Gets or sets the database save strategy - /// - public ConcurrencyStrategy SaveStrategy { get; set; } - - /// - /// Gets or sets the flag to indicate whether to leverage the caching mechanism - /// - public bool Cached { get; set; } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/DbContext Factory/CachedNamedDbContextFactory.cs b/src/providers/EntityFramework.NetFramework/DbContext Factory/CachedNamedDbContextFactory.cs deleted file mode 100644 index 1c9ce86..0000000 --- a/src/providers/EntityFramework.NetFramework/DbContext Factory/CachedNamedDbContextFactory.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Data.Common; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Diagnostics.CodeAnalysis; - -namespace Dime.Repositories -{ - /// - /// - /// - /// - [ExcludeFromCodeCoverage] - public abstract class CachedNamedDbContextFactory : INamedDbContextFactory where TContext : DbContext - { - /// - /// Initializes a new instance of the class - /// - protected CachedNamedDbContextFactory() - { - } - - /// - /// Initializes a new instance of the class - /// - /// The connection string - protected CachedNamedDbContextFactory(string connectionString) : this() - { - Connection = connectionString; - } - - /// - /// - /// - /// - /// - protected CachedNamedDbContextFactory(string connectionString, string tenant) : this(connectionString) - { - Tenant = tenant; - } - - protected static ConcurrentDictionary, DbCompiledModel> ModelCache = new(); - protected static ConcurrentDictionary, DbCompiledModel> NamedModelCache = new(); - - protected string Connection { get; } - protected string Tenant { get; } - - /// - /// Creates the instance of with the default settings - /// - /// - public virtual TContext Create() - => !string.IsNullOrEmpty(Tenant) && !string.IsNullOrEmpty(Connection) ? - Create(Tenant, Connection) : - Create("dbo", Connection); - - /// - /// Creates the specified connection. - /// - /// The connection. - /// - public virtual TContext Create(string nameOrConnectionString) => Create("dbo", nameOrConnectionString); - - /// - /// Creates the specified tenant. - /// - /// The tenant. - /// The connection. - /// - /// - public virtual TContext Create(string tenant, string connection, string context) - { - if (string.IsNullOrEmpty(context)) - return Create(tenant, connection); - - SqlConnectionFactory connectionFactory = new(); - DbConnection dbConnection = connectionFactory.CreateConnection(connection); - Database.SetInitializer(null); - - DbCompiledModel compiledModel = NamedModelCache.GetOrAdd( - Tuple.Create(tenant, dbConnection.ConnectionString, context), - _ => GetContextModel(dbConnection, tenant)); - - return ConstructContext(dbConnection, compiledModel, false); - } - - /// - /// Creates the specified tenant. - /// - /// The tenant. - /// The connection. - /// - public virtual TContext Create(string tenant, string nameOrConnectionString) - { - SqlConnectionFactory connectionFactory = new(); - DbConnection dbConnection = connectionFactory.CreateConnection(nameOrConnectionString); - Database.SetInitializer(null); - - DbCompiledModel compiledModel = ModelCache.GetOrAdd( - Tuple.Create(tenant, dbConnection.ConnectionString), - _ => GetContextModel(dbConnection, tenant)); - - return ConstructContext(dbConnection, compiledModel, false); - } - - /// - /// Constructs the context. - /// - /// - protected abstract TContext ConstructContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection); - - /// - /// Gets the scheduler context model. - /// - /// The database connection. - /// - /// - protected abstract DbCompiledModel GetContextModel(DbConnection dbConnection, string schema); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/DbContext Factory/INamedDbContextFactory.cs b/src/providers/EntityFramework.NetFramework/DbContext Factory/INamedDbContextFactory.cs deleted file mode 100644 index f20fde3..0000000 --- a/src/providers/EntityFramework.NetFramework/DbContext Factory/INamedDbContextFactory.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Data.Entity; -using System.Data.Entity.Infrastructure; - -namespace Dime.Repositories -{ - /// - /// Entity Framework context factory - /// - /// - public interface INamedDbContextFactory : IDbContextFactory where TContext : DbContext - { - /// - /// Creates the context by name - /// - /// The name - /// The instantiated context - TContext Create(string nameOrConnectionString); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Dime.Repositories.Sql.EntityFramework.NetFramework.csproj b/src/providers/EntityFramework.NetFramework/Dime.Repositories.Sql.EntityFramework.NetFramework.csproj deleted file mode 100644 index 08a0c24..0000000 --- a/src/providers/EntityFramework.NetFramework/Dime.Repositories.Sql.EntityFramework.NetFramework.csproj +++ /dev/null @@ -1,42 +0,0 @@ - - - Dime.Repositories.Sql.EntityFramework.NetFramework - Dime.Repositories.Sql.EntityFramework.NetFramework - net7.0;net461 - Dime.Repositories.Sql.EntityFramework.NetFramework - Dime Software - Dime.Repositories.Sql.EntityFramework.NetFramework - en - Implementation of the repository pattern with Entity Framework - Copyright © 2021 - 2.0.0.0-alpha.45 - 2.0.0.0 - 2.0.0.0 - latest - Dime Software - https://cdn.dime-software.com/dime-software/logo-shape.png - https://github.com/dimesoftware/repository - https://github.com/dimesoftware/repository - Dime.Scheduler - true - MIT - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Exceptions/ConcurrencyException.cs b/src/providers/EntityFramework.NetFramework/Exceptions/ConcurrencyException.cs deleted file mode 100644 index 902a14b..0000000 --- a/src/providers/EntityFramework.NetFramework/Exceptions/ConcurrencyException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; - -namespace Dime.Repositories -{ - [Serializable] - [Obsolete("Use shared project")] - [ExcludeFromCodeCoverage] - public class ConcurrencyException : Exception - { - public ConcurrencyException() - { - } - - public ConcurrencyException(string message) : base(message) - { - } - - public ConcurrencyException(string message, Exception innerException) : base(message, innerException) - { - } - - protected ConcurrencyException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Exceptions/ConstraintViolationException.cs b/src/providers/EntityFramework.NetFramework/Exceptions/ConstraintViolationException.cs deleted file mode 100644 index 0a7ed95..0000000 --- a/src/providers/EntityFramework.NetFramework/Exceptions/ConstraintViolationException.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; - -namespace Dime.Repositories -{ - /// - /// Exception to indicate an error with constraints such as PK and FK - /// - [Serializable] - [Obsolete("Use shared project")] - [ExcludeFromCodeCoverage] - public class ConstraintViolationException : Exception - { - /// - /// Default constructor - /// - public ConstraintViolationException() - { - } - - /// - /// Constructor accepting the message - /// - /// The exception message - public ConstraintViolationException(string message) : base(message) - { - } - - /// - /// - /// - /// The exception message - /// The exception that was caught - public ConstraintViolationException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - /// - /// - /// SerializationInfo for the exception - /// The Streaming Context - protected ConstraintViolationException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Exceptions/DatabaseAccessException.cs b/src/providers/EntityFramework.NetFramework/Exceptions/DatabaseAccessException.cs deleted file mode 100644 index fca3e69..0000000 --- a/src/providers/EntityFramework.NetFramework/Exceptions/DatabaseAccessException.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.Serialization; - -namespace Dime.Repositories -{ - /// - /// Exception to indicate a general error with the database - /// - [Serializable] - [Obsolete("Use shared project")] - [ExcludeFromCodeCoverage] - public class DatabaseAccessException : Exception - { - /// - /// Initializes a new instance of the System.Exception class. - /// - public DatabaseAccessException() - { - } - - /// - /// Initializes a new instance of the System.Exception class with a specified error message - /// - /// The error message that explains the reason for the exception. - public DatabaseAccessException(string message) - : base(message) - { - } - - /// - /// Initializes a new instance of the System.Exception class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. - public DatabaseAccessException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Initializes a new instance of the System.Exception class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// SerializationInfo for the exception - /// The Streaming Context - protected DatabaseAccessException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Model Builder/DbContextModelBuilder.cs b/src/providers/EntityFramework.NetFramework/Model Builder/DbContextModelBuilder.cs deleted file mode 100644 index d5dbc82..0000000 --- a/src/providers/EntityFramework.NetFramework/Model Builder/DbContextModelBuilder.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Data.Entity; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Reflection; - -namespace Dime.Repositories -{ - /// - /// - /// - [ExcludeFromCodeCoverage] - public static class DbContextModelBuilder - { - /// - /// - /// - /// - /// - public static void BuildModel(this DbModelBuilder modelBuilder, string schema) where T : DbContext - { - Type foundType = Assembly.GetCallingAssembly() - .GetTypes() - .FirstOrDefault(x => typeof(IModelBuilder).IsAssignableFrom(x) && !x.IsAbstract && !x.IsInterface); - - if (foundType == null) - throw new ArgumentException("No model builder found for context in assembly {0}", Assembly.GetCallingAssembly().FullName); - - object o = Activator.CreateInstance(foundType); - IModelBuilder concreteModelBuilder = (IModelBuilder)o; - concreteModelBuilder.BuildContext(modelBuilder, schema); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Model Builder/IModelBuilder.cs b/src/providers/EntityFramework.NetFramework/Model Builder/IModelBuilder.cs deleted file mode 100644 index 51a7685..0000000 --- a/src/providers/EntityFramework.NetFramework/Model Builder/IModelBuilder.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Data.Entity; - -namespace Dime.Repositories -{ - /// - /// Represents a model builder for - /// - /// The db context type - public interface IModelBuilder where T : DbContext - { - /// - /// Builds the default context - /// - /// The code first model builder - void BuildContext(DbModelBuilder builder); - - /// - /// Builds the context for a schema other than the default dbo schema - /// - /// The code first model builder - /// The schema name - void BuildContext(DbModelBuilder builder, string schema); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/AggregateRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/AggregateRepositoryAsync.cs deleted file mode 100644 index 5e522a7..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/AggregateRepositoryAsync.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public Task CountAsync() - { - using TContext ctx = Context; - long count = ctx.Count(); - return Task.FromResult(count); - } - - public Task CountAsync(Expression> where) - { - using TContext ctx = Context; - long count = ctx.Count(where); - return Task.FromResult(count); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/CreateRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/CreateRepositoryAsync.cs deleted file mode 100644 index 2a46f51..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/CreateRepositoryAsync.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual async Task CreateAsync(TEntity entity) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - await SaveChangesAsync(ctx); - - return createdItem; - } - - public virtual async Task CreateAsync(TEntity entity, Expression> condition) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().AddIfNotExists(entity, condition); - await SaveChangesAsync(ctx); - - return createdItem; - } - - public virtual async Task CreateAsync(TEntity entity, Func beforeSaveAction) - { - using TContext ctx = Context; - - await beforeSaveAction(entity, ctx); - - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - await SaveChangesAsync(ctx); - - return createdItem; - } - - public virtual async Task CreateAsync(TEntity entity, bool commit) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - - if (commit) - await SaveChangesAsync(ctx); - - return createdItem; - } - - public virtual async Task> CreateAsync(IQueryable entities) - { - if (!entities.Any()) - return entities; - - List newEntities = new(); - - using TContext ctx = Context; - foreach (TEntity entity in entities.ToList()) - { - ctx.Entry(entity).State = EntityState.Added; - TEntity newEntity = ctx.Set().Add(entity); - newEntities.Add(newEntity); - } - - await SaveChangesAsync(ctx); - - return newEntities.AsQueryable(); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/DeleteRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/DeleteRepositoryAsync.cs deleted file mode 100644 index 5d45e6d..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/DeleteRepositoryAsync.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual async Task DeleteAsync(object? id) - { - using TContext ctx = Context; - TEntity item = await ctx.Set().FindAsync(id); - if (item != default(TEntity)) - { - ctx.Set().Remove(item); - await SaveChangesAsync(ctx); - } - } - - public virtual async Task DeleteAsync(IEnumerable ids) - { - using TContext ctx = Context; - foreach (object id in ids.Distinct().ToList()) - { - TEntity item = await ctx.Set().FindAsync(id); - if (item == default(TEntity)) - continue; - - ctx.Set().Remove(item); - } - - await SaveChangesAsync(ctx); - } - - public virtual async Task DeleteAsync(object? id, bool commit) - { - using TContext ctx = Context; - TEntity item = await ctx.Set().FindAsync(id); - if (item != default(TEntity)) - { - ctx.Set().Remove(item); - if (commit) - await SaveChangesAsync(ctx); - } - } - - public virtual async Task DeleteAsync(TEntity entity) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - await SaveChangesAsync(ctx); - } - - public async Task DeleteAsync(IEnumerable entities) - { - if (!entities.Any()) - return; - - using TContext ctx = Context; - foreach (TEntity entity in entities) - { - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - } - - await SaveChangesAsync(ctx); - } - - public virtual async Task DeleteAsync(TEntity entity, bool commit) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - - if (commit) - await SaveChangesAsync(ctx); - } - - public virtual async Task DeleteAsync(Expression> where) - { - using TContext ctx = Context; - IEnumerable entities = ctx.Set().With(where).AsNoTracking().ToList(); - if (entities.Any()) - { - foreach (TEntity item in entities) - ctx.Set().Attach(item); - - ctx.Set().RemoveRange(entities); - await SaveChangesAsync(ctx); - } - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/GetRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/GetRepositoryAsync.cs deleted file mode 100644 index 6dcce8b..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/GetRepositoryAsync.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Core.Metadata.Edm; -using System.Data.Entity.Infrastructure; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using LinqKit; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual async Task ExistsAsync(Expression> where) - { - using TContext ctx = Context; - return await ctx.Set().AsNoTracking().AnyAsync(where); - } - - public virtual async Task FindByIdAsync(object? id) - { - using TContext ctx = Context; - return await ctx.Set().FindAsync(id); - } - - public virtual async Task FindByIdAsync(object? id, params string[] includes) - { - using TContext ctx = Context; - foreach (string include in includes) - ctx.Set().Include(include).AsNoTracking(); - - return await ctx.Set().FindAsync(id); - } - - public virtual async Task FindOneAsync(Expression> where) - { - using TContext ctx = Context; - TEntity query = ctx.Set() - .AsNoTracking() - .AsExpandable() - .With(where) - .FirstOrDefault(); - - return await Task.Run(() => query); - } - - public virtual async Task FindOneAsync(Expression> where, params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where); - - IQueryable fullGraphQuery = await Task.Run(() => query); - return fullGraphQuery.FirstOrDefault(); - } - - public Task FindOneAsync( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - IQueryable fullGraphQuery = Include(query, includes); - return Task.FromResult(fullGraphQuery.FirstOrDefault()); - } - - public virtual async Task> FindAllAsync(Expression> where, params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where); - - return await Task.FromResult(query.ToList()); - } - - public virtual Task> FindAllAsync( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .AsQueryable() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - return Task.FromResult(query.ToList() as IEnumerable); - } - - public virtual async Task> FindAllAsync( - Expression> where = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize); - - return await Task.FromResult(query.ToList()); - } - - private IQueryable Include(IQueryable query, params string[] includes) - { - if (includes == null) - return query; - - if (includes.Any()) - return includes - .Where(include => include != null) - .Aggregate(query, (current, include) => current.Include(include)); - - MetadataWorkspace workspace = ((IObjectContextAdapter)Context).ObjectContext.MetadataWorkspace; - ObjectItemCollection itemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace); - EntityType entityType = itemCollection.OfType().Single(e => itemCollection.GetClrType(e) == typeof(TEntity)); - - return entityType.NavigationProperties.Aggregate(query, (current, navigationProperty) => current.Include(navigationProperty.Name)); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/PagedRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/PagedRepositoryAsync.cs deleted file mode 100644 index 257634c..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/PagedRepositoryAsync.cs +++ /dev/null @@ -1,270 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using LinqKit; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual Task> FindAllPagedAsync( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - where TResult : class - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - IQueryable fullGraphQuery = Include(query, includes); - Page p = new( - fullGraphQuery.ToList(), - ctx.Set().AsNoTracking().AsExpandable().Count(where)); - - return Task.FromResult((IPage)p); - } - - public Task> FindAllPagedAsync( - Expression> where = null, - Func groupBy = null, - Expression, IEnumerable>> select = null, - Expression> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class, new() - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithGroup(groupBy) - .WithSelect(select); - - IQueryable fullGraphQuery = Include(query, includes); - - Page p = new( - fullGraphQuery.ToList(), - ctx.Set().AsNoTracking().AsExpandable().Count(where)); - - return Task.FromResult((IPage)p); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> select = null, - IEnumerable> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - return await Task.FromResult( - new Page(query.ToList(), - ctx.Count(where))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> count = null, - Expression> select = null, - IEnumerable> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - return await Task.FromResult(new Page(query.ToList(), ctx.Count(count))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .AsQueryable(); - - return await Task.FromResult( - new Page(query.ToList(), - ctx.Count(where))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize); - - return await Task.FromResult( - new Page( - query.ToList(), - ctx.Count(where))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> count = null, - IEnumerable> orderBy = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize); - - return await Task.FromResult(new Page(query.ToList(), ctx.Count(count))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - Expression> count = null, - IEnumerable> orderBy = null, - int? page = default, - int? pageSize = default, - bool trackChanges = false, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize); - - return await Task.FromResult( - new Page(trackChanges - ? query.ToList() - : query.AsNoTracking().ToList(), - ctx.Count(count)) - ); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - IEnumerable> orderBy = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize); - - return await Task.FromResult(new Page(query.ToList(), ctx.Count(where))); - } - - public async Task> FindAllPagedAsync( - Expression> where = null, - IEnumerable>> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .AsQueryable(); - - return await Task.FromResult(new Page(query.ToList(), ctx.Count(where))); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/RepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/RepositoryAsync.cs deleted file mode 100644 index 3df34ee..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/RepositoryAsync.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Data.Entity.Validation; -using System.Data.SqlClient; -using System.Linq; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual async Task SaveChangesAsync() - { - int retryMax = 0; - bool saveFailed = false; - do - { - try - { - return !Configuration.SaveInBatch && 0 < await Context.SaveChangesAsync(); - } - catch (DbEntityValidationException ex) - { - Rethrow(ex); - return false; - } - catch (DbUpdateConcurrencyException dbUpdateConcurrencyEx) - { - if (Configuration.SaveStrategy == ConcurrencyStrategy.ClientFirst) - { - foreach (DbEntityEntry failedEntry in dbUpdateConcurrencyEx.Entries) - { - if (failedEntry.State == EntityState.Deleted) - { - failedEntry.State = EntityState.Detached; - continue; - } - - DbPropertyValues dbValues = failedEntry.GetDatabaseValues(); - if (dbValues == null) - continue; - - failedEntry.OriginalValues.SetValues(dbValues); - return await SaveChangesAsync(); - } - return true; - } - else - { - foreach (DbEntityEntry failedEnttry in dbUpdateConcurrencyEx.Entries) - await failedEnttry.ReloadAsync(); - - return true; - } - } - catch (DbUpdateException dbUpdateEx) - { - if (dbUpdateEx.InnerException?.InnerException == null) - throw; - - if (dbUpdateEx.InnerException.InnerException is not SqlException sqlException) - throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException); - - throw sqlException.Number switch - { - 2627 => new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - _ => new DatabaseAccessException(sqlException.Message, sqlException) - }; - } - } - while (saveFailed && retryMax <= 3); - } - - public virtual async Task SaveChangesAsync(TContext context) - { - int retryMax; - bool saveFailed; - do - { - try - { - if (!Configuration.SaveInBatch) - { - int result = context.SaveChanges(); - return 0 < result; - } - else - return false; - } - catch (DbEntityValidationException validationEx) - { - Rethrow(validationEx); - return false; - } - catch (DbUpdateConcurrencyException dbUpdateConcurrencyEx) - { - if (Configuration.SaveStrategy == ConcurrencyStrategy.ClientFirst) - { - bool retried = false; - foreach (DbEntityEntry failedEntry in dbUpdateConcurrencyEx.Entries) - { - if (failedEntry.State == EntityState.Deleted) - { - failedEntry.State = EntityState.Detached; - retried = true; - continue; - } - - DbPropertyValues dbValues = failedEntry.GetDatabaseValues(); - if (dbValues == null) - continue; - - retried = true; - failedEntry.OriginalValues.SetValues(dbValues); - return await SaveChangesAsync(context); - } - - if (!retried) - throw; - - return retried; - } - else - { - foreach (DbEntityEntry failedEnttry in dbUpdateConcurrencyEx.Entries) - await failedEnttry.ReloadAsync(); - - return true; - } - } - catch (DbUpdateException dbUpdateEx) - { - if (dbUpdateEx.InnerException?.InnerException == null) - throw; - - if (dbUpdateEx.InnerException.InnerException is not SqlException sqlException) - throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException); - - throw sqlException.Number switch - { - 2627 => new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - _ => new DatabaseAccessException(sqlException.Message, sqlException) - }; - } - } - while (saveFailed && retryMax <= 3); - } - - private static void Rethrow(DbEntityValidationException ex) - { - IEnumerable errorMessages = ex.EntityValidationErrors - .SelectMany(x => x.ValidationErrors) - .Select(x => x.ErrorMessage); - - string fullErrorMessage = string.Join("; ", errorMessages); - - string exceptionMessage = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage); - - throw new DbEntityValidationException(exceptionMessage, ex.EntityValidationErrors); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/SqlRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/SqlRepositoryAsync.cs deleted file mode 100644 index 96fb5f6..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/SqlRepositoryAsync.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Collections.Generic; -using System.Data; -using System.Data.Common; -using System.Linq; -using System.Threading.Tasks; - -#if NET461 -using System.Data.SqlClient; -#else - -using Microsoft.Data.SqlClient; - -#endif - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public async Task ExecuteSqlAsync(string sql) - { - using TContext ctx = Context; - await ctx.Database.ExecuteSqlCommandAsync(sql); - } - - public async Task ExecuteStoredProcedureAsync(string name, params DbParameter[] parameters) - { - using TContext ctx = Context; - - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", y.Select(z => $"{z.ParameterName}={z.Value}")); - return $"EXEC {x} {parameterString}"; - } - - string execQueryString = ExecQuery(name, parameters); - - return await ctx.Database.ExecuteSqlCommandAsync(execQueryString, parameters); - } - - public async Task ExecuteStoredProcedureAsync(string name, string schema = "dbo", params DbParameter[] parameters) - { - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", y.Select(z => $"{z.ParameterName}={z.Value}")); - return $"EXEC {schema}.{x} {parameterString}"; - } - - string execQueryString = ExecQuery(name, parameters); - - using TContext ctx = Context; - return await ctx.Database.ExecuteSqlCommandAsync(execQueryString, parameters); - } - - public async Task> ExecuteStoredProcedureAsync(string name, string schema = "dbo", params DbParameter[] parameters) - { - using TContext ctx = Context; - using DbConnection connection = ctx.Database.Connection; - connection.Open(); - using DbCommand cmd = connection.CreateCommand(); - cmd.CommandText = name; - cmd.CommandType = CommandType.StoredProcedure; - cmd.Parameters.AddRange(parameters); - - using IDataReader reader = await cmd.ExecuteReaderAsync(); - return reader.GetRecords(); - } - - public async Task ExecuteStoredProcedureAsync(T name, string schema = "dbo", params DbParameter[] parameters) - { - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", y.Select(z => $"{z.ParameterName}={z.Value}")); - return $"EXEC {schema}.{nameof(x)} {parameterString}"; - } - - string execQueryString = ExecQuery(nameof(name), parameters); - - using TContext ctx = Context; - return await ctx.Database.ExecuteSqlCommandAsync(execQueryString, parameters); - } - - public async Task> ExecuteStoredProcedureAsync(string command, params DbParameter[] parameters) - { - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", y.Select(z => $"@{z.ParameterName}={z.Value}")); - return $"EXEC {x} {parameterString}"; - } - - return await Task.Run(() => - { - using TContext ctx = Context; - return ctx.Database.SqlQuery(ExecQuery(command, parameters)); - }); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Async/UpdateRepositoryAsync.cs b/src/providers/EntityFramework.NetFramework/Repository/Async/UpdateRepositoryAsync.cs deleted file mode 100644 index bb18ec4..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Async/UpdateRepositoryAsync.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual async Task UpdateAsync(TEntity entity, bool commitChanges = true) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Entry(entity).State = EntityState.Modified; - - if (commitChanges) - await SaveChangesAsync(ctx); - - return entity; - } - - public async Task UpdateAsync(IEnumerable entities, bool commitChanges = true) - { - if (!entities.Any()) - return; - - using TContext ctx = Context; - foreach (TEntity entity in entities) - { - ctx.Set().Attach(entity); - ctx.Entry(entity).State = EntityState.Modified; - } - - await SaveChangesAsync(ctx); - } - - public virtual async Task UpdateAsync(TEntity entity, params string[] properties) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - DbEntityEntry entry = ctx.Entry(entity); - - foreach (string property in properties) - { - if (entry.Member(property) is DbComplexPropertyEntry) - entry.ComplexProperty(property).IsModified = true; - else - entry.Property(property).IsModified = true; - } - - ctx.Entry(entity).State = EntityState.Modified; - await SaveChangesAsync(ctx); - return entity; - } - - public virtual async Task UpdateAsync(TEntity entity, params Expression>[] properties) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - DbEntityEntry entry = ctx.Entry(entity); - - foreach (Expression> property in properties) - entry.Property(property).IsModified = true; - - ctx.Entry(entity).State = EntityState.Modified; - - await SaveChangesAsync(ctx); - - return entity; - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/EfRepositoryFactory.cs b/src/providers/EntityFramework.NetFramework/Repository/EfRepositoryFactory.cs deleted file mode 100644 index 0285978..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/EfRepositoryFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Data.Entity; -using System.Diagnostics.CodeAnalysis; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - public class EfRepositoryFactory : IRepositoryFactory, IRepositoryFactory - where TContext : DbContext - { - public EfRepositoryFactory(INamedDbContextFactory contextFactory) - : this(contextFactory, new RepositoryConfiguration()) - { - } - - public EfRepositoryFactory( - INamedDbContextFactory contextFactory, - RepositoryConfiguration repositoryConfiguration) - { - ContextFactory = contextFactory; - RepositoryConfiguration = repositoryConfiguration; - } - - protected INamedDbContextFactory ContextFactory { get; } - public RepositoryConfiguration RepositoryConfiguration { get; } - - public virtual IRepository Create() where TEntity : class, new() - => Create(RepositoryConfiguration); - - public virtual IRepository Create(RepositoryConfiguration opts) where TEntity : class, new() - => new EfRepository( - !string.IsNullOrEmpty(opts.Connection) ? opts : RepositoryConfiguration, - ContextFactory); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/AggregateRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/AggregateRepository.cs deleted file mode 100644 index 723e404..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/AggregateRepository.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public long Count() - { - using TContext ctx = Context; - return ctx.Set().AsNoTracking().Count(); - } - - public long Count(Expression> where) - { - using TContext ctx = Context; - return ctx.Set().AsNoTracking().Count(where); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/CreateRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/CreateRepository.cs deleted file mode 100644 index eeb5d6d..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/CreateRepository.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual TEntity Create(TEntity entity) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - SaveChanges(Context); - - return createdItem; - } - - public virtual TEntity Create(TEntity entity, Expression> condition) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().AddIfNotExists(entity, condition); - SaveChanges(Context); - - return createdItem; - } - - public virtual TEntity Create(TEntity entity, Func beforeSaveAction) - { - beforeSaveAction(entity, Context); - - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - SaveChanges(Context); - - return createdItem; - } - - public virtual TEntity Create(TEntity entity, bool commit) - { - using TContext ctx = Context; - ctx.Entry(entity).State = EntityState.Added; - TEntity createdItem = ctx.Set().Add(entity); - - if (commit) - SaveChanges(Context); - - return createdItem; - } - - public virtual IQueryable Create(IQueryable entities) - { - if (!entities.Any()) - return entities; - - List newEntities = new(); - List entitiesToCreate = entities.ToList(); - - using TContext ctx = Context; - foreach (TEntity entity in entitiesToCreate) - { - ctx.Entry(entity).State = EntityState.Added; - TEntity newEntity = ctx.Set().Add(entity); - newEntities.Add(newEntity); - } - - SaveChanges(Context); - - return newEntities.AsQueryable(); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/DeleteRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/DeleteRepository.cs deleted file mode 100644 index f13780b..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/DeleteRepository.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual void Delete(object? id) - { - using TContext ctx = Context; - TEntity item = ctx.Set().Find(id); - if (item == default(TEntity)) - return; - - ctx.Set().Remove(item); - SaveChanges(Context); - } - - public virtual void Delete(object? id, bool commit) - { - using TContext ctx = Context; - TEntity item = ctx.Set().Find(id); - if (item == default(TEntity)) - return; - - ctx.Set().Remove(item); - if (commit) - SaveChanges(Context); - } - - public virtual void Delete(TEntity entity) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - SaveChanges(Context); - } - - public void Delete(IEnumerable entities) - { - if (!entities.Any()) - return; - - using TContext ctx = Context; - foreach (TEntity entity in entities) - { - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - } - - SaveChanges(Context); - } - - public virtual void Delete(TEntity entity, bool commit) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Set().Remove(entity); - - if (commit) - SaveChanges(Context); - } - - public virtual void Delete(Expression> where) - { - using TContext ctx = Context; - IEnumerable entities = ctx.Set().With(where).AsNoTracking().ToList(); - if (entities == null) - return; - - foreach (TEntity item in entities) - ctx.Set().Attach(item); - - ctx.Set().RemoveRange(entities); - SaveChanges(Context); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/GetRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/GetRepository.cs deleted file mode 100644 index d22dde7..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/GetRepository.cs +++ /dev/null @@ -1,177 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using LinqKit; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public bool Exists(Expression> where) - { - using TContext ctx = Context; - return ctx.Set().AsNoTracking().Any(where); - } - - public virtual TEntity FindById(object? id) - { - using TContext ctx = Context; - return ctx.Set().Find(id); - } - - public virtual TEntity FindById(object? id, params string[] includes) - { - using TContext ctx = Context; - foreach (string include in includes) - ctx.Set().Include(include).AsNoTracking(); - - return ctx.Set().Find(id); - } - - public virtual TEntity FindOne(Expression> where) - { - using TContext ctx = Context; - TEntity query = ctx.Set() - .AsNoTracking() - .AsExpandable() - .With(where) - .FirstOrDefault(); - - return query; - } - - public virtual TEntity FindOne(Expression> where, params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where); - - return query.FirstOrDefault(); - } - - public TResult FindOne( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - return Include(query, includes).FirstOrDefault(); - } - - public IEnumerable FindAll(Expression> where) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, null) - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where); - - return query.ToList(); - } - - public virtual IEnumerable FindAll(Expression> where, params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where); - - return query.ToList(); - } - - public IEnumerable FindAll(Expression> where, bool includeAll, params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where); - - return query.ToList(); - } - - public IEnumerable FindAll(Expression> where, int? page, int? pageSize, string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .AsExpandable() - .AsQueryable() - .AsNoTracking() - .With(where) - .With(page, pageSize, default(IEnumerable>>)) - .With(pageSize); - - return Include(query, includes); - } - - public virtual IEnumerable FindAll( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - return query.ToList(); - } - - public virtual IEnumerable FindAll( - Expression> where = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - { - using TContext ctx = Context; - IQueryable query = ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize); - - return query.ToList(); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/PagedRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/PagedRepository.cs deleted file mode 100644 index 6b5aeb7..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/PagedRepository.cs +++ /dev/null @@ -1,214 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; -using LinqKit; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public virtual Page FindAllPaged( - Expression> where = null, - Expression> select = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = null, - int? pageSize = null, - params string[] includes) - where TResult : class - { - using TContext ctx = Context; - IQueryable query = - ctx.Set() - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select); - - IQueryable fullGraphQuery = Include(query, includes); - return new Page(fullGraphQuery.ToList(), ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - Func groupBy = null, - Expression, IEnumerable>> select = null, - Expression> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class, new() - { - using TContext ctx = Context; - - IQueryable query = - ctx.Set() - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithGroup(groupBy) - .WithSelect(select); - - IQueryable fullGraphQuery = Include(query, includes); - return new Page(fullGraphQuery.ToList(), ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - Expression> select = null, - IEnumerable> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select) - .ToPage(ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - Expression> count = null, - Expression> select = null, - IEnumerable> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) where TResult : class - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .AsQueryable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .WithSelect(select) - .ToPage(ctx.Count(count)); - } - - public Page FindAllPaged( - Expression> where = null, - Expression> orderBy = null, - bool? ascending = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .AsQueryable() - .ToPage(ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - Expression> orderBy = null, - Expression> groupBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .ToPage(ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - Expression> count = null, - IEnumerable> orderBy = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .ToPage(ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - IEnumerable> orderBy = null, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsNoTracking() - .AsExpandable() - .With(where) - .WithOrder(orderBy) - .With(page, pageSize, orderBy) - .With(pageSize) - .ToPage(ctx.Count(where)); - } - - public Page FindAllPaged( - Expression> where = null, - IEnumerable>> orderBy = null, - bool? ascending = default, - int? page = default, - int? pageSize = default, - params string[] includes) - { - using TContext ctx = Context; - return ctx.Set() - .Include(ctx, includes) - .AsExpandable() - .AsNoTracking() - .With(where) - .WithOrder(orderBy, ascending ?? true) - .With(page, pageSize, orderBy) - .With(pageSize) - .AsQueryable() - .ToPage(ctx.Count(where)); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/Repository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/Repository.cs deleted file mode 100644 index 49860ea..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/Repository.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Data.Entity.Validation; -using System.Diagnostics.CodeAnalysis; - -#if NET461 -using System.Data.SqlClient; -#else - -using Microsoft.Data.SqlClient; - -#endif - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - public partial class EfRepository : ISqlRepository - where TEntity : class, new() - where TContext : DbContext - { - public EfRepository(TContext dbContext) - : this(dbContext, new RepositoryConfiguration()) - { - } - - public EfRepository(TContext dbContext, RepositoryConfiguration configuration) - { - Context = dbContext; - Configuration = configuration; - } - - public EfRepository(RepositoryConfiguration configuration, INamedDbContextFactory factory) - { - Configuration = configuration; - Factory = factory; - } - - private TContext _context; - - protected TContext Context - { - get => Factory?.Create(Configuration.Connection); - set => _context = value; - } - - private RepositoryConfiguration Configuration { get; } - - private INamedDbContextFactory Factory { get; } - - public virtual bool SaveChanges(TContext context) - { - int retryMax; - bool saveFailed; - do - { - try - { - if (Configuration.SaveInBatch) - return false; - - int result = context.SaveChanges(); - return 0 < result; - } - catch (DbEntityValidationException validationEx) - { - Rethrow(validationEx); - return false; - } - catch (DbUpdateConcurrencyException dbUpdateConcurrencyEx) - { - if (Configuration.SaveStrategy == ConcurrencyStrategy.ClientFirst) - { - foreach (DbEntityEntry failedEntry in dbUpdateConcurrencyEx.Entries) - { - DbPropertyValues dbValues = failedEntry.GetDatabaseValues(); - if (dbValues == null) - continue; - - failedEntry.OriginalValues.SetValues(dbValues); - return SaveChanges(context); - } - return true; - } - - foreach (DbEntityEntry failedEntry in dbUpdateConcurrencyEx.Entries) - failedEntry.Reload(); - - return true; - } - catch (DbUpdateException dbUpdateEx) - { - if (dbUpdateEx.InnerException?.InnerException == null) - throw; - - if (dbUpdateEx.InnerException.InnerException is not SqlException sqlException) - throw new DatabaseAccessException(dbUpdateEx.Message, dbUpdateEx.InnerException); - - throw sqlException.Number switch - { - 2627 => new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - _ => new DatabaseAccessException(sqlException.Message, sqlException) - }; - } - } - while (saveFailed && retryMax <= 3); - } - - public void Dispose() - { - if (Context == null) - return; - - Context.Dispose(); - Context = null; - } - - public static explicit operator TContext(EfRepository repository) - => repository.Context; - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/StoredProcedureRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/StoredProcedureRepository.cs deleted file mode 100644 index 41c8327..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/StoredProcedureRepository.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using System.Data.Common; -using System.Linq; - -#if NET461 -using System.Data.SqlClient; -#else - -using Microsoft.Data.SqlClient; - -#endif - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public IEnumerable GetStoredProcedureSchema(string name, string schema = "dbo") - { - using TContext ctx = Context; - using SqlConnection connection = new(ctx.Database.Connection.ConnectionString); - connection.Open(); - using SqlCommand cmd = connection.CreateCommand(); - cmd.CommandText = $"{schema}.{name}"; - cmd.CommandType = System.Data.CommandType.StoredProcedure; - - SqlCommandBuilder.DeriveParameters(cmd); - foreach (SqlParameter param in cmd.Parameters) - { - yield return param; - } - } - - public int ExecuteStoredProcedure(string name, params DbParameter[] parameters) - { - using TContext ctx = Context; - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", parameters.Select(z => $"{z.ParameterName}={z.Value}")); - return $"EXEC {name} {parameterString}"; - } - - string execQueryString = ExecQuery(name, parameters); - return ctx.Database.ExecuteSqlCommand(execQueryString, parameters); - } - - public int ExecuteStoredProcedure(string name, string schema = "dbo", params DbParameter[] parameters) - { - using TContext ctx = Context; - string ExecQuery(string x, DbParameter[] y) - { - string parameterString = string.Join(",", parameters.Select(z => $"{z.ParameterName}={z.Value}")); - return $"EXEC {schema}.{name} {parameterString}"; - } - - string execQueryString = ExecQuery(name, parameters); - return ctx.Database.ExecuteSqlCommand(execQueryString, parameters); - } - - public IEnumerable ExecuteStoredProcedure(string name, string schema = "dbo", params DbParameter[] parameters) - { - using TContext ctx = Context; - using DbConnection connection = ctx.Database.Connection; - connection.Open(); - using DbCommand cmd = connection.CreateCommand(); - cmd.CommandText = $"{schema}.{name}"; - cmd.CommandType = System.Data.CommandType.StoredProcedure; - cmd.Parameters.AddRange(parameters); - - using DbDataReader reader = cmd.ExecuteReader(); - return reader.GetRecords(); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Repository/Sync/UpdateRepository.cs b/src/providers/EntityFramework.NetFramework/Repository/Sync/UpdateRepository.cs deleted file mode 100644 index c6378d9..0000000 --- a/src/providers/EntityFramework.NetFramework/Repository/Sync/UpdateRepository.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Infrastructure; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - public partial class EfRepository - { - public TEntity Update(TEntity entity) => Update(entity, true); - - public virtual TEntity Update(TEntity entity, bool commitChanges = true) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - ctx.Entry(entity).State = EntityState.Modified; - - if (commitChanges) - SaveChanges(Context); - - return entity; - } - - public void Update(IEnumerable entities, bool commitChanges = true) - { - if (!entities.Any()) - return; - - using TContext ctx = Context; - foreach (TEntity entity in entities) - { - ctx.Set().Attach(entity); - ctx.Entry(entity).State = EntityState.Modified; - } - - SaveChanges(Context); - } - - public virtual TEntity Update(TEntity entity, params string[] properties) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - DbEntityEntry entry = ctx.Entry(entity); - - foreach (string property in properties) - { - if (entry.Member(property) is DbComplexPropertyEntry) - entry.ComplexProperty(property).IsModified = true; - else - entry.Property(property).IsModified = true; - } - - ctx.Entry(entity).State = EntityState.Modified; - SaveChanges(Context); - return entity; - } - - public virtual TEntity Update(TEntity entity, params Expression>[] properties) - { - using TContext ctx = Context; - ctx.Set().Attach(entity); - DbEntityEntry entry = ctx.Entry(entity); - - foreach (Expression> property in properties) - entry.Property(property).IsModified = true; - - ctx.Entry(entity).State = EntityState.Modified; - - SaveChanges(Context); - - return entity; - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/DataReaderExtensions.cs b/src/providers/EntityFramework.NetFramework/Utilities/DataReaderExtensions.cs deleted file mode 100644 index c752154..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/DataReaderExtensions.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Reflection; - -namespace Dime.Repositories -{ - public static class DataReaderExtensions - { - /// - /// - /// - /// - /// - /// - public static List GetRecords(this IDataReader reader) - { - List result = new(); - while (reader.Read()) - { - T t = (T)typeof(T).GetConstructor(Type.EmptyTypes).Invoke(Array.Empty()); - PropertyInfo[] props = t.GetType().GetProperties(); - object[] indexer = null; - foreach (PropertyInfo p in props) - { - if (reader.HasColumn(p.Name) && reader[p.Name].GetType() != typeof(DBNull)) - p.SetValue(t, reader[p.Name], indexer); - } - - result.Add(t); - } - - return result; - } - - /// - /// - /// - /// - /// - /// - public static bool HasColumn(this IDataRecord dr, string columnName) - { - for (int i = 0; i < dr.FieldCount; i++) - { - if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase)) - return true; - } - - return false; - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/DbContextExtensions.cs b/src/providers/EntityFramework.NetFramework/Utilities/DbContextExtensions.cs deleted file mode 100644 index 9e9e7d3..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/DbContextExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Data.Entity; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; -using LinqKit; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static class DbContextExtensions - { - /// - /// - /// - /// - /// - /// - /// - internal static int Count(this DbContext ctx, Expression> query = null) - where TEntity : class - => query == null - ? ctx.Set().AsExpandable().AsNoTracking().Count() - : ctx.Set().AsExpandable().AsNoTracking().Count(query); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/DbSetExtensions.cs b/src/providers/EntityFramework.NetFramework/Utilities/DbSetExtensions.cs deleted file mode 100644 index b3e2ae6..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/DbSetExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Data.Entity; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static class DbSetExtensions - { - /// - /// Conditionally adds the item to the set - /// - /// The entity type - /// The entity's set - /// The record to add - /// The condition - /// The created item if the condition was met; otherwise null - public static T AddIfNotExists(this DbSet dbSet, T entity, Expression> condition = null) where T : class, new() - => !(condition == null || dbSet.Any(condition)) ? dbSet.Add(entity) : null; - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Extensions/EFExtensions.cs b/src/providers/EntityFramework.NetFramework/Utilities/Extensions/EFExtensions.cs deleted file mode 100644 index 57c33f7..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/Extensions/EFExtensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Data.Entity; -using System.Data.Entity.Core.Metadata.Edm; -using System.Data.Entity.Infrastructure; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static class EFExtensions - { - private static MetadataWorkspace _workspace; - private static ObjectItemCollection _itemCollection; - - private static EntityType GetEntityType(IObjectContextAdapter context) - { - _workspace ??= context.ObjectContext.MetadataWorkspace; - _itemCollection ??= (ObjectItemCollection)_workspace.GetItemCollection(DataSpace.OSpace); - - EntityType entityType = _itemCollection.OfType().FirstOrDefault(e => _itemCollection.GetClrType(e) == typeof(T)); - return entityType; - } - - /// - /// - /// - /// - /// - /// - /// - /// - internal static IQueryable Include(this IQueryable query, DbContext context, params string[] includes) - where TEntity : class - { - // Don't eagerly load navigation properties when the includes parameter has been explicitly set to null - if (includes == null) - return query; - - List includeList = new(); - if (includes.Any()) - return includes - .Where(x => !string.IsNullOrEmpty(x) && !includeList.Contains(x)) - .Aggregate(query, (current, include) => current.Include(include)); - - ReadOnlyMetadataCollection navigationProperties = GetEntityType(context)?.NavigationProperties; - if (navigationProperties == null) - return query; - - foreach (NavigationProperty navigationProperty in - navigationProperties.Where(navigationProperty => !includeList.Contains(navigationProperty.Name))) - { - includeList.Add(navigationProperty.Name); - query = query.Include(navigationProperty.Name); - } - - return query; - } - - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal static IQueryable IncludeView(this IQueryable query, DbContext context, params string[] includes) where TEntity : class - { - if (includes != null && includes.Any()) - return includes - .Where(x => !string.IsNullOrEmpty(x)) - .Aggregate(query, (current, include) => current.Include(include)); - - return GetEntityType(context) - .NavigationProperties - .Aggregate(query, (current, navigationProperty) => current.Include(navigationProperty.Name)); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationExtensions.cs b/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationExtensions.cs deleted file mode 100644 index 0febc47..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static class OrderLinq - { - internal static IOrderedQueryable SortBy(this IEnumerable query, IOrder order) - { - string verb = order.IsAscending ? "OrderBy" : "OrderByDescending"; - LinqOrderHelper helper = new(verb, order.Property); - return helper.GetAsQueryable(query); - } - - /// - /// - /// - /// - /// - /// - /// - internal static IOrderedQueryable ThenBy(this IEnumerable source, IOrder order) - { - string verb = order.IsAscending ? "ThenBy" : "ThenByDescending"; - LinqOrderHelper helper = new(verb, order.Property); - return helper.GetAsQueryable(source); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationHelper.cs b/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationHelper.cs deleted file mode 100644 index cc48f91..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/LinqOperationHelper.cs +++ /dev/null @@ -1,111 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; - -namespace Dime.Repositories -{ - /// - /// - /// - /// - [ExcludeFromCodeCoverage] - internal class LinqOrderHelper - { - #region Constructor - - internal LinqOrderHelper(string methodName, string propertyName) - { - Method = methodName; - ParentParameterExpression = Expression.Parameter(typeof(TSource), "x"); - MemberExpression = SetMember(Parse(propertyName)); - } - - #endregion Constructor - - #region Properties - - private string Method { get; } - private ParameterExpression ParentParameterExpression { get; } - private MemberExpression MemberExpression { get; } - - /// - /// - /// - /// - /// - internal IOrderedQueryable GetAsQueryable(IEnumerable query) - { - LambdaExpression selector = Expression.Lambda(MemberExpression, ParentParameterExpression); - MethodInfo methodInfo = GetMethodInfo(Method, MemberExpression.Type); - - IOrderedQueryable newQuery = (IOrderedQueryable)methodInfo.Invoke(methodInfo, new object[] { query, selector }); - return newQuery; - } - - /// - /// - /// - /// - private MemberExpression SetMember(IOrderedEnumerable> orderedList) - { - MemberExpression memberExpression = default; - for (int i = 0; i < orderedList.Count(); i++) - { - // Get the current record in the loop - string dataIndex = orderedList.ElementAt(i).Value; - - // If this is the first iteration, just set the variable - else append the expa - memberExpression = i == 0 ? - Expression.PropertyOrField(ParentParameterExpression, dataIndex) : - Expression.PropertyOrField(memberExpression, dataIndex); - } - - return memberExpression; - } - - /// - /// - /// - /// - /// - /// - private MethodInfo GetMethodInfo(string methodName, Type type) - { - //Get System.Linq.Queryable.OrderBy() method. - Type enumarableType = typeof(Queryable); - MethodInfo method = enumarableType.GetMethods() - .Where(m => m.Name == methodName && m.IsGenericMethodDefinition) - .Where(m => - { - List parameters = m.GetParameters().ToList(); - //Put more restriction here to ensure selecting the right overload - return parameters.Count == 2;//overload that has 2 parameters - }).Single(); - - //The linq's OrderBy has two generic types, which provided here - MethodInfo genericMethod = method.MakeGenericMethod(typeof(TSource), type); - - return genericMethod; - } - - /// - /// - /// - /// - /// - private IOrderedEnumerable> Parse(string propertyName) - { - int c = 1; - IDictionary properties = new Dictionary(); - propertyName.Split('.').ToList().ForEach(x => { properties.Add(c, x); c++; }); - - IOrderedEnumerable> orderedList = properties.OrderBy(x => x.Key); - return orderedList; - } - - #endregion Properties - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SkipQueryFactory.cs b/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SkipQueryFactory.cs deleted file mode 100644 index 54eba84..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SkipQueryFactory.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data.Entity; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - internal static partial class QueryFactory - { - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. - internal static IQueryable With(this IQueryable source, int? page, int? pageSize, IEnumerable> orderBy) - { - int pageToApply = page.GetValueOrDefault(); - int pageSizeToApply = pageSize.GetValueOrDefault(); - - if (pageToApply == 0 || pageSizeToApply == 0) - return source; - - int itemsToSkip = (pageToApply - 1) * pageSizeToApply; - - return orderBy == null ? - source.OrderBy(x => true).Skip(() => itemsToSkip) : - source.Skip(() => itemsToSkip); - } - - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. - internal static IQueryable With(this IQueryable source, int? page, int? pageSize, Expression> orderBy) - { - int pageToApply = page.GetValueOrDefault(); - int pageSizeToApply = pageSize.GetValueOrDefault(); - - if (pageToApply == 0 || pageSizeToApply == 0) - return source; - - int itemsToSkip = (pageToApply - 1) * pageSizeToApply; - - return orderBy == null ? - source.OrderBy(x => true).Skip(() => itemsToSkip) : - source.Skip(() => itemsToSkip); - } - - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. - internal static IQueryable With(this IQueryable source, int? page, int? pageSize, IEnumerable>> orderBy) - { - int pageToApply = page.GetValueOrDefault(); - int pageSizeToApply = pageSize.GetValueOrDefault(); - - if (pageToApply == 0 || pageSizeToApply == 0) - return source; - - int itemsToSkip = (pageToApply - 1) * pageSizeToApply; - - return orderBy == null ? - source.OrderBy(x => true).Skip(() => itemsToSkip) : - source.Skip(() => itemsToSkip); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SortingQueryFactory.cs b/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SortingQueryFactory.cs deleted file mode 100644 index 01ae960..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SortingQueryFactory.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - internal static partial class QueryFactory - { - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - internal static IQueryable WithOrder(this IQueryable source, IEnumerable> orderByExpression) - { - if (orderByExpression != null && orderByExpression.Count() > 1) - { - IEnumerable orderBy = null; - for (int i = 0; i < orderByExpression.Count(); i++) - { - IOrder element = orderByExpression.ElementAt(i); - orderBy = i == 0 ? source.SortBy(element) : orderBy.ThenBy(element); - } - - return orderBy.AsQueryable(); - } - - if (orderByExpression != null && orderByExpression.Count() == 1) - return source.SortBy(orderByExpression.ElementAt(0)).AsQueryable(); - - return source.OrderBy(x => true); - } - - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// - internal static IQueryable WithOrder(this IQueryable source, IEnumerable>> orderByExpression, bool ascending) - { - if (orderByExpression == null) - return source; - if (orderByExpression.Count() > 1) - { - Func orderBy = orderByExpression.ElementAt(0).Compile(); - Func orderByThen = orderByExpression.ElementAt(1).Compile(); - - return ascending - ? source.OrderBy(orderBy).ThenBy(orderByThen).AsQueryable() - : source.OrderBy(orderBy).ThenByDescending(orderByThen).AsQueryable(); - } - else - { - Func orderBy = orderByExpression.ElementAt(0).Compile(); - - return ascending - ? source.OrderBy(orderBy).AsQueryable() - : source.OrderByDescending(orderBy).AsQueryable(); - } - } - - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// - internal static IQueryable WithOrder(this IQueryable source, Expression> orderByExpression, bool ascending) - { - if (orderByExpression == null) - return source; - Func compiledExpression = orderByExpression.Compile(); - return ascending - ? source.OrderBy(compiledExpression).AsQueryable() - : source.OrderByDescending(compiledExpression).AsQueryable(); - } - - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// - internal static IQueryable WithOrder(this IQueryable source, Func orderByExpression, bool ascending) - { - if (orderByExpression != null) - return ascending - ? source.OrderBy(orderByExpression).AsQueryable() - : source.OrderByDescending(orderByExpression).AsQueryable(); - - Func defaultSorting = x => true; - return ascending - ? source.OrderBy(defaultSorting).AsQueryable() - : source.OrderByDescending(defaultSorting).AsQueryable(); - } - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/TakeQueryFactory.cs b/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/TakeQueryFactory.cs deleted file mode 100644 index 591e7d4..0000000 --- a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/TakeQueryFactory.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Data.Entity; -using System.Diagnostics.CodeAnalysis; -using System.Linq; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static class TakeQueryFactory - { - internal static IQueryable With(this IQueryable source, int? takeCount) - { - int itemsToTake = takeCount.GetValueOrDefault(); - return itemsToTake == 0 ? source : source.Take(() => itemsToTake); - } - } - - [ExcludeFromCodeCoverage] - internal static class PageFactory - { - internal static Page ToPage(this IQueryable source, int count) - => new Page(source.ToList(), count); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.projitems b/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.projitems deleted file mode 100644 index b23aa39..0000000 --- a/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.projitems +++ /dev/null @@ -1,36 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - e2c7ade6-23d8-4897-a156-05dcd901e2d0 - - - Dime.Repositories.Sql.EntityFramework.Shared - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.shproj b/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.shproj deleted file mode 100644 index c8937cd..0000000 --- a/src/providers/EntityFramework.Shared/Dime.Repositories.Sql.EntityFramework.Shared.shproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - e2c7ade6-23d8-4897-a156-05dcd901e2d0 - 14.0 - - - - - - - - diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/GroupByQueryFactory.cs b/src/providers/EntityFramework.Shared/Utilities/Query Factory/GroupByQueryFactory.cs deleted file mode 100644 index 0b5b073..0000000 --- a/src/providers/EntityFramework.Shared/Utilities/Query Factory/GroupByQueryFactory.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - [ExcludeFromCodeCoverage] - internal static partial class QueryFactory - { - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// - public static IQueryable> WithGroup(this IQueryable source, Expression> predicate) - => source.GroupBy(predicate); - - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// - public static IQueryable> WithGroup(this IQueryable source, Func predicate) - => source.GroupBy(predicate).AsQueryable(); - - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// - public static IQueryable> WithGroup(this IOrderedEnumerable source, Func predicate) - => source.GroupBy(predicate).AsQueryable(); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/SelectQueryFactory.cs b/src/providers/EntityFramework.Shared/Utilities/Query Factory/SelectQueryFactory.cs deleted file mode 100644 index 0810e97..0000000 --- a/src/providers/EntityFramework.Shared/Utilities/Query Factory/SelectQueryFactory.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - internal static partial class QueryFactory - { - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// - /// The source. - /// The selector. - /// - internal static IQueryable WithSelect( - this IQueryable> source, - Expression, IEnumerable>> selector) - => selector == null ? default : source.SelectMany(selector); - - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// - internal static IQueryable WithSelect( - this IQueryable> source, - Expression, int, TResult>> selector) - => selector == null ? default : source.Select(selector); - - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// - internal static IQueryable WithSelect(this IOrderedEnumerable source, - Func selector) - where TSource : class - where TResult : class - => selector == null ? default : source.Select(selector).AsQueryable(); - - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// - internal static IQueryable WithSelect(this IQueryable source, - Expression> selector) - where TSource : class - => selector == null ? default : source.Select(selector).AsQueryable(); - - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// - internal static IQueryable WithSelect(this IQueryable source, - Func selector) - where TSource : class - where TResult : class - => selector == null ? default : source.Select(selector).AsQueryable(); - - /// - /// - /// - /// - /// - /// - /// - /// - internal static TResult WithFirstSelect(this TSource source, - Expression> selector) - where TSource : class - where TResult : class - => selector == null - ? default - : new List { source }.AsQueryable().Select(selector).FirstOrDefault(); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/WhereQueryFactory.cs b/src/providers/EntityFramework.Shared/Utilities/Query Factory/WhereQueryFactory.cs deleted file mode 100644 index ee48f92..0000000 --- a/src/providers/EntityFramework.Shared/Utilities/Query Factory/WhereQueryFactory.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; - -namespace Dime.Repositories -{ - internal static partial class QueryFactory - { - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// The source. - /// The predicate. - /// - internal static IQueryable With(this IQueryable source, Expression> predicate) - => predicate == null ? source : source.Where(predicate); - - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// The source. - /// The predicate. - /// - internal static TSource WithFirst(this IQueryable source, Expression> predicate) - => predicate == null ? source.FirstOrDefault() : source.FirstOrDefault(predicate); - } -} \ No newline at end of file diff --git a/src/providers/EntityFramework.Shared/Configuration/RepositoryConfiguration.cs b/src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Configuration/RepositoryConfiguration.cs rename to src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs diff --git a/src/providers/EntityFramework.NetCore/Dime.Repositories.Sql.EntityFramework.NetCore.csproj b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj similarity index 71% rename from src/providers/EntityFramework.NetCore/Dime.Repositories.Sql.EntityFramework.NetCore.csproj rename to src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj index ab6d49f..32a0dae 100644 --- a/src/providers/EntityFramework.NetCore/Dime.Repositories.Sql.EntityFramework.NetCore.csproj +++ b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj @@ -19,34 +19,28 @@ Dime Software - net7.0 + net8.0 Dime.Repositories.Sql.EntityFramework Dime.Repositories.Sql.EntityFramework Entity Framework;Repository;SQL https://cdn.dime-software.com/dime-software/logo-shape.png - 2.0.0.0-alpha.47 + 2.0.0.0-alpha.48 Implementation of the repository pattern with Microsoft SQL using Entity Framework Core - Copyright © 2022 + Copyright © 2023 https://github.com/dimesoftware/repository https://github.com/dimesoftware/repository git - - - - - - - - + + + - diff --git a/src/providers/EntityFramework.Shared/Exceptions/ConcurrencyException.cs b/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Exceptions/ConcurrencyException.cs rename to src/providers/EntityFramework/Exceptions/ConcurrencyException.cs diff --git a/src/providers/EntityFramework.Shared/Exceptions/ConstraintViolationException.cs b/src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Exceptions/ConstraintViolationException.cs rename to src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs diff --git a/src/providers/EntityFramework.Shared/Exceptions/DatabaseAccessException.cs b/src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Exceptions/DatabaseAccessException.cs rename to src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs diff --git a/src/providers/EntityFramework.NetCore/Model Builder/DbContextModelBuilder.cs b/src/providers/EntityFramework/Model Builder/DbContextModelBuilder.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Model Builder/DbContextModelBuilder.cs rename to src/providers/EntityFramework/Model Builder/DbContextModelBuilder.cs diff --git a/src/providers/EntityFramework.NetCore/Model Builder/IModelBuilder.cs b/src/providers/EntityFramework/Model Builder/IModelBuilder.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Model Builder/IModelBuilder.cs rename to src/providers/EntityFramework/Model Builder/IModelBuilder.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Async/AggregateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Async/AggregateRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Async/CreateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Async/CreateRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Async/DeleteRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Async/DeleteRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Async/GetRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Async/GetRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Async/PagedRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Async/PagedRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Async/RepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Async/RepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Async/SqlRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Async/SqlRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Async/UpdateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Async/UpdateRepositoryAsync.cs rename to src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/EfRepositoryFactory.cs b/src/providers/EntityFramework/Repository/EfRepositoryFactory.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/EfRepositoryFactory.cs rename to src/providers/EntityFramework/Repository/EfRepositoryFactory.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/IEfRepositoryFactory.cs b/src/providers/EntityFramework/Repository/IEfRepositoryFactory.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/IEfRepositoryFactory.cs rename to src/providers/EntityFramework/Repository/IEfRepositoryFactory.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Sync/AggregateRepository.cs b/src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Sync/AggregateRepository.cs rename to src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Sync/CreateRepository.cs b/src/providers/EntityFramework/Repository/Sync/CreateRepository.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Sync/CreateRepository.cs rename to src/providers/EntityFramework/Repository/Sync/CreateRepository.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Sync/DeleteRepository.cs b/src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Sync/DeleteRepository.cs rename to src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Sync/GetRepository.cs b/src/providers/EntityFramework/Repository/Sync/GetRepository.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Sync/GetRepository.cs rename to src/providers/EntityFramework/Repository/Sync/GetRepository.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Sync/PagedRepository.cs b/src/providers/EntityFramework/Repository/Sync/PagedRepository.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Sync/PagedRepository.cs rename to src/providers/EntityFramework/Repository/Sync/PagedRepository.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Sync/Repository.cs b/src/providers/EntityFramework/Repository/Sync/Repository.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Sync/Repository.cs rename to src/providers/EntityFramework/Repository/Sync/Repository.cs diff --git a/src/providers/EntityFramework.NetCore/Repository/Sync/StoredProcedureRepository.cs b/src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Repository/Sync/StoredProcedureRepository.cs rename to src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs diff --git a/src/providers/EntityFramework.Shared/Repository/Sync/UpdateRepository.cs b/src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Repository/Sync/UpdateRepository.cs rename to src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/DataReaderExtensions.cs b/src/providers/EntityFramework/Utilities/DataReaderExtensions.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/DataReaderExtensions.cs rename to src/providers/EntityFramework/Utilities/DataReaderExtensions.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/DbContextExtensions.cs b/src/providers/EntityFramework/Utilities/DbContextExtensions.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/DbContextExtensions.cs rename to src/providers/EntityFramework/Utilities/DbContextExtensions.cs diff --git a/src/providers/EntityFramework.NetCore/Utilities/EFExtensions.cs b/src/providers/EntityFramework/Utilities/EFExtensions.cs similarity index 100% rename from src/providers/EntityFramework.NetCore/Utilities/EFExtensions.cs rename to src/providers/EntityFramework/Utilities/EFExtensions.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/LinqOperationExtensions.cs b/src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/LinqOperationExtensions.cs rename to src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/LinqOperationHelper.cs b/src/providers/EntityFramework/Utilities/LinqOperationHelper.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/LinqOperationHelper.cs rename to src/providers/EntityFramework/Utilities/LinqOperationHelper.cs diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/GroupByQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.NetFramework/Utilities/Query Factory/GroupByQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SelectQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.NetFramework/Utilities/Query Factory/SelectQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/SkipQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/Query Factory/SkipQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/SortingQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/Query Factory/SortingQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs diff --git a/src/providers/EntityFramework.Shared/Utilities/Query Factory/TakeQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.Shared/Utilities/Query Factory/TakeQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs diff --git a/src/providers/EntityFramework.NetFramework/Utilities/Query Factory/WhereQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs similarity index 100% rename from src/providers/EntityFramework.NetFramework/Utilities/Query Factory/WhereQueryFactory.cs rename to src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs diff --git a/src/test.runsettings b/src/test.runsettings deleted file mode 100644 index 2c3dbef..0000000 --- a/src/test.runsettings +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - .*\.dll$ - .*\.exe$ - - - .*CPPUnitTestFramework.* - .*moq.dll - .*Tests.dll - - - - - False - True - True - False - - - - - - \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/AggregateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/AggregateRepositoryTests.cs deleted file mode 100644 index 761bf64..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/AggregateRepositoryTests.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Count_NoPredicate_ShouldCountAll() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection)); - long result = repo.Count(); - Assert.AreEqual(3, result); - } - - [TestMethod] - public async Task Repository_CountAsync_NoPredicate_ShouldCountAll() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection)); - long result = await repo.CountAsync(); - Assert.AreEqual(3, result); - } - - [TestMethod] - public async Task Repository_CountAsync_Predicate_ShouldCountCorrectly() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection)); - long result = await repo.CountAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result); - } - - [TestMethod] - public void Repository_Count_Predicate_ShouldCountCorrectly() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(connection)); - long result = repo.Count(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result); - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/BloggingContext.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/BloggingContext.cs deleted file mode 100644 index 7daff68..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/BloggingContext.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using System.Data.Common; -using System.Data.Entity; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - public class BloggingContext : DbContext - { - public BloggingContext() - { - } - - public BloggingContext(DbConnection connection) : base(connection, true) - { - } - - public DbSet Blogs { get; set; } - public DbSet Posts { get; set; } - - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - modelBuilder.Entity().HasKey(c => c.Id); - - modelBuilder.Entity().HasKey(c => c.BlogId); - modelBuilder.Entity().HasOptional(c => c.Category); - - base.OnModelCreating(modelBuilder); - } - } - - public class Blog - { - public int BlogId { get; set; } - public string Url { get; set; } - public Category Category { get; set; } - public List Posts { get; set; } - } - - public class Category - { - public int Id { get; set; } - public string Name { get; set; } - } - - public class Post - { - public int PostId { get; set; } - public string Title { get; set; } - public string Content { get; set; } - - public int BlogId { get; set; } - public Blog Blog { get; set; } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/CreateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/CreateRepositoryTests.cs deleted file mode 100644 index 9f9cf61..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/CreateRepositoryTests.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Create_ShouldAddOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - repo.Create(new Blog { Url = "http://sample.com" }); - - using BloggingContext context = new(connection); - Assert.AreEqual(1, context.Blogs.Count()); - Assert.AreEqual("http://sample.com", context.Blogs.Single().Url); - } - - [TestMethod] - public async Task Repository_CreateAsync_ShouldAddOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - await repo.CreateAsync(new Blog { Url = "http://sample.com" }); - - // Use a separate instance of the context to verify correct data was saved to database - using BloggingContext context = new(connection); - Assert.AreEqual(1, context.Blogs.Count()); - Assert.AreEqual("http://sample.com", context.Blogs.Single().Url); - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/DeleteRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/DeleteRepositoryTests.cs deleted file mode 100644 index a0878d4..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/DeleteRepositoryTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Delete_ByEntity_ShouldRemoveOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - repo.Delete(new Blog { BlogId = 1 }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(connection)) - { - Assert.AreEqual(2, context.Blogs.Count()); - } - } - - [TestMethod] - public async Task Repository_DeleteAsync_ByEntity_ShouldRemoveOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - await repo.DeleteAsync(new Blog { BlogId = 1 }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(connection)) - { - Assert.AreEqual(2, context.Blogs.Count()); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests.csproj b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests.csproj deleted file mode 100644 index 38358ef..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - {719B5ACC-8EA5-47B7-A8DA-B54A47E5ACA4} - net461 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - False - UnitTest - false - latest - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/GetRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/GetRepositoryTests.cs deleted file mode 100644 index 1853f96..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/GetRepositoryTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - [TestClass] - public partial class RepositoryTests - { - [TestMethod] - public void Repository_FindAll_Contains_ShouldFindMatches() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection), new RepositoryConfiguration()); - IEnumerable result = repo.FindAll(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result.Count()); - } - - [TestMethod] - public async Task Repository_FindAllAsync_Contains_ShouldFindMatches() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - await context.SaveChangesAsync(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection), new RepositoryConfiguration()); - IEnumerable result = await repo.FindAllAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result.Count()); - } - - [TestMethod] - public async Task Repository_FindAllAsync_ShouldFindMatches() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats", Category = new() { Name = "Pets" } }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish", Category = new() { Name = "Pets" } }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs", Category = new() { Name = "Pets" } }); - await context.SaveChangesAsync(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection), new RepositoryConfiguration()); - - Expression>[] includes = { x => x.Category, x => x.Category.Name }; - IEnumerable result = await repo.FindAllAsync( - where: x => x.Url.Contains("cat"), - orderBy: null, - ascending: false, - includes: includes.ToPropertyExpression()); - Assert.AreEqual(2, result.Count()); - } - } - - internal static class EfIncludesExtensions - { - public static string[] ToPropertyExpression(this Expression>[] items) - => items != null && items.Any() ? items.Where(x => x != null).Select(x => x.GetPropertyName()).ToArray() : null; - - public static string[] ToPropertyExpression(this Expression>[] includes, Expression> select = null) - => includes != null && select == null && includes.Any() ? includes.Select(x => x.GetPropertyName()).ToArray() : null; - - public static string[] ToPropertyExpression(this string[] includes) - => includes != null && includes.Any() ? includes.ToArray() : null; - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/PagedQueryRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/PagedQueryRepositoryTests.cs deleted file mode 100644 index 565b633..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/PagedQueryRepositoryTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - [TestClass] - public class PagedQueryRepositoryTests - { - [TestMethod] - public async Task Repository_FindPagedAsync_Contains_ShouldFindMatches() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/2" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/3" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/4" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/5" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/6" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/7" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/8" }); - - await context.SaveChangesAsync(); - } - - IRepository repo = new EfRepository(new BloggingContext(connection), new RepositoryConfiguration()); - IPage result = await repo.FindAllPagedAsync( - where: x => x.Url.Contains("cat"), - orderBy: null, - count: null, - page: 1, - pageSize: 5, - includes: null); - - Assert.AreEqual(2, result.Data.Count()); - Assert.AreEqual(10, result.Total); - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/UpdateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/UpdateRepositoryTests.cs deleted file mode 100644 index eb4f828..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetFramework.Tests/UpdateRepositoryTests.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading.Tasks; -using Effort.Provider; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetFramework.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Update_ByEntity_ShouldRemoveOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - repo.Update(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(connection)) - { - Blog blog = context.Blogs.Find(1); - Assert.IsTrue(blog.Url == "http://sample.com/zebras"); - } - } - - [TestMethod] - public async Task Repository_UpdateAsync_ByEntity_ShouldRemoveOne() - { - EffortConnection connection = Effort.DbConnectionFactory.CreateTransient(); - - using (BloggingContext context = new(connection)) - { - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(connection))) - await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(connection)) - { - Blog blog = await context.Blogs.FindAsync(1); - Assert.IsTrue(blog.Url == "http://sample.com/zebras"); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/AggregateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/AggregateRepositoryTests.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/BloggingContext.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/BloggingContext.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/BloggingContext.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/BloggingContext.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/CreateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/CreateRepositoryTests.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/DeleteRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/DeleteRepositoryTests.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/Dime.Repositories.Sql.EntityFramework.NetCore.Tests.csproj b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj similarity index 58% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/Dime.Repositories.Sql.EntityFramework.NetCore.Tests.csproj rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj index c972154..87daa7f 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/Dime.Repositories.Sql.EntityFramework.NetCore.Tests.csproj +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj @@ -1,21 +1,21 @@  - net7.0 + net8.0 false - - - - - + + + + + - + diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/GetRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/GetRepositoryTests.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/UpdateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs similarity index 100% rename from src/test/Dime.Repositories.Sql.EntityFramework.NetCore.Tests/UpdateRepositoryTests.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs From c83d827d56724195b53b2bb369136e0db26ee1cf Mon Sep 17 00:00:00 2001 From: Hendrik Bulens Date: Fri, 6 Oct 2023 09:18:35 +0200 Subject: [PATCH 2/5] Test refactoring --- .../AggregateRepositoryTests.cs | 158 ------------------ .../CountTests.cs | 48 ++++++ .../CreateRepositoryTests.cs | 78 --------- .../CreateTests.cs | 38 +++++ .../DeleteRepositoryTests.cs | 132 --------------- .../DeleteTests.cs | 50 ++++++ .../GetRepositoryTests.cs | 85 ---------- .../GetTests.cs | 29 ++++ .../{ => Helpers}/BloggingContext.cs | 2 +- .../Helpers/TestDatabase.cs | 45 +++++ .../UpdateRepositoryTests.cs | 88 ---------- .../UpdateTests.cs | 36 ++++ 12 files changed, 247 insertions(+), 542 deletions(-) delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs rename src/test/Dime.Repositories.Sql.EntityFramework.Tests/{ => Helpers}/BloggingContext.cs (94%) create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs delete mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs deleted file mode 100644 index ea065e5..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/AggregateRepositoryTests.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Count_NoPredicate_ShouldCountAll() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - // Insert seed data into the database using one instance of the context - using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - long result = repo.Count(); - Assert.AreEqual(3, result); - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_CountAsync_NoPredicate_ShouldCountAll() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - await using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - // Insert seed data into the database using one instance of the context - await using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - long result = await repo.CountAsync(); - Assert.AreEqual(3, result); - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_CountAsync_Predicate_ShouldCountCorrectly() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - await using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - } - - // Insert seed data into the database using one instance of the context - await using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - long result = await repo.CountAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result); - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public void Repository_Count_Predicate_ShouldCountCorrectly() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - } - - // Insert seed data into the database using one instance of the context - using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - long result = repo.Count(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result); - } - finally - { - connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs new file mode 100644 index 0000000..ee67f4c --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs @@ -0,0 +1,48 @@ +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + public partial class CountTests + { + [TestMethod] + public void Count_NoPredicate_ShouldCountAll() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = repo.Count(); + Assert.AreEqual(3, result); + } + + [TestMethod] + public async Task CountAsync_NoPredicate_ShouldCountAll() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = await repo.CountAsync(); + Assert.AreEqual(3, result); + } + + [TestMethod] + public async Task CountAsync_Predicate_ShouldCountCorrectly() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = await repo.CountAsync(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result); + } + + [TestMethod] + public void Count_Predicate_ShouldCountCorrectly() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = repo.Count(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs deleted file mode 100644 index 3d3afad..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateRepositoryTests.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Create_ShouldAddOne() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - // Run the test against one instance of the context - using (IRepository repo = new EfRepository(new BloggingContext(options))) - repo.Create(new Blog { Url = "http://sample.com" }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(options)) - { - Assert.AreEqual(1, context.Blogs.Count()); - Assert.AreEqual("http://sample.com", context.Blogs.Single().Url); - } - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_CreateAsync_ShouldAddOne() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - await using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - using (IRepository repo = new EfRepository(new BloggingContext(options))) - await repo.CreateAsync(new Blog { Url = "http://sample.com" }); - - // Use a separate instance of the context to verify correct data was saved to database - await using (BloggingContext context = new(options)) - { - Assert.AreEqual(1, context.Blogs.Count()); - Assert.AreEqual("http://sample.com", context.Blogs.Single().Url); - } - } - finally - { - connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs new file mode 100644 index 0000000..24967ef --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + public partial class RepositoryTests + { + [TestMethod] + public void Create_ShouldAddOne() + { + using TestDatabase testDb = new(); + + // Run the test against one instance of the context + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + repo.Create(new Blog { Url = "http://sample.com" }); + + // Use a separate instance of the context to verify correct data was saved to database + using BloggingContext context = new(testDb.Options); + Assert.AreEqual(4, context.Blogs.Count()); + Assert.AreEqual("http://sample.com", context.Blogs.OrderByDescending(x => x.BlogId).First().Url); + } + + [TestMethod] + public async Task CreateAsync_ShouldAddOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.CreateAsync(new Blog { Url = "http://sample.com" }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(4, context.Blogs.Count()); + Assert.AreEqual("http://sample.com", context.Blogs.OrderByDescending(x => x.BlogId).First().Url); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs deleted file mode 100644 index 607baca..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteRepositoryTests.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Delete_ByEntity_ShouldRemoveOne() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(options))) - repo.Delete(new Blog { BlogId = 1 }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(options)) - { - Assert.AreEqual(2, context.Blogs.Count()); - } - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_DeleteAsync_ByEntity_ShouldRemoveOne() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - await using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(options))) - await repo.DeleteAsync(new Blog { BlogId = 1 }); - - // Use a separate instance of the context to verify correct data was saved to database - await using (BloggingContext context = new(options)) - { - Assert.AreEqual(2, context.Blogs.Count()); - } - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_DeleteAsync_ByIds_ShouldRemoveList() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - await using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - List ids = new() { 1, 2 }; - using (IRepository repo = new EfRepository(new BloggingContext(options))) - await repo.DeleteAsync(ids); - - // Use a separate instance of the context to verify correct data was saved to database - await using (BloggingContext context = new(options)) - { - Assert.AreEqual(1, context.Blogs.Count()); - } - } - catch (Exception ex) - { - Assert.Fail(ex.Message); - } - finally - { - connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs new file mode 100644 index 0000000..364235d --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + public partial class RepositoryTests + { + [TestMethod] + public void Delete_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + repo.Delete(new Blog { BlogId = 1 }); + + // Use a separate instance of the context to verify correct data was saved to database + using BloggingContext context = new(testDb.Options); + Assert.AreEqual(2, context.Blogs.Count()); + } + + [TestMethod] + public async Task DeleteAsync_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.DeleteAsync(new Blog { BlogId = 1 }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(2, context.Blogs.Count()); + } + + [TestMethod] + public async Task DeleteAsync_ByIds_ShouldRemoveList() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + + List ids = new() { 1, 2 }; + await repo.DeleteAsync(ids); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(1, context.Blogs.Count()); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs deleted file mode 100644 index 8e891a4..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetRepositoryTests.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests -{ - [TestClass] - public partial class RepositoryTests - { - [TestMethod] - public void Repository_FindAll_Contains_ShouldFindMatches() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - // Insert seed data into the database using one instance of the context - using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - IEnumerable result = repo.FindAll(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result.Count()); - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_FindAllAsync_Contains_ShouldFindMatches() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - // Create the schema in the database - await using (BloggingContext context = new(options)) - context.Database.EnsureCreated(); - - // Insert seed data into the database using one instance of the context - await using (BloggingContext context = new(options)) - { - context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using IRepository repo = new EfRepository(new BloggingContext(options)); - IEnumerable result = await repo.FindAllAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result.Count()); - } - finally - { - connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs new file mode 100644 index 0000000..3296def --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class RepositoryTests + { + [TestMethod] + public void FindAll_Contains_ShouldFindMatches() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + IEnumerable result = repo.FindAll(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result.Count()); + } + + [TestMethod] + public async Task FindAllAsync_Contains_ShouldFindMatches() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + IEnumerable result = await repo.FindAllAsync(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result.Count()); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/BloggingContext.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs similarity index 94% rename from src/test/Dime.Repositories.Sql.EntityFramework.Tests/BloggingContext.cs rename to src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs index 1a3a6c5..2cf0ebf 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/BloggingContext.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Microsoft.EntityFrameworkCore; -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests +namespace Dime.Repositories.Sql.EntityFramework.Tests { public class BloggingContext : DbContext { diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs new file mode 100644 index 0000000..5ee19ad --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs @@ -0,0 +1,45 @@ +using System; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + internal class TestDatabase : IDisposable + { + internal TestDatabase() + { + // In-memory database only exists while the connection is open + Connection = new("DataSource=:memory:"); + Connection.Open(); + + Options = new DbContextOptionsBuilder().UseSqlite(Connection).Options; + + CreateDatabase(); + } + + internal SqliteConnection Connection { get; private set; } + + internal DbContextOptions Options { get; private set; } + + internal void CreateDatabase() + { + // Create the schema in the database + using (BloggingContext context = new(Options)) + context.Database.EnsureCreated(); + + // Insert seed data into the database using one instance of the context + using (BloggingContext context = new(Options)) + { + context.Blogs.Add(new Blog { Url = "http://sample.com/cats" }); + context.Blogs.Add(new Blog { Url = "http://sample.com/catfish" }); + context.Blogs.Add(new Blog { Url = "http://sample.com/dogs" }); + context.SaveChanges(); + } + } + + public void Dispose() + { + Connection.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs deleted file mode 100644 index 7f06095..0000000 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateRepositoryTests.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Threading.Tasks; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace Dime.Repositories.Sql.EntityFramework.NetCore.Tests -{ - public partial class RepositoryTests - { - [TestMethod] - public void Repository_Update_ByEntity_ShouldRemoveOne() - { - // In-memory database only exists while the connection is open - using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(options))) - repo.Update(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); - - // Use a separate instance of the context to verify correct data was saved to database - using (BloggingContext context = new(options)) - { - Blog blog = context.Blogs.Find(1); - Assert.IsTrue(blog.Url == "http://sample.com/zebras"); - } - } - finally - { - connection.Close(); - } - } - - [TestMethod] - public async Task Repository_UpdateAsync_ByEntity_ShouldRemoveOne() - { - // In-memory database only exists while the connection is open - await using SqliteConnection connection = new("DataSource=:memory:"); - connection.Open(); - - try - { - DbContextOptions options = new DbContextOptionsBuilder() - .UseSqlite(connection) - .Options; - - await using (BloggingContext context = new(options)) - { - context.Database.EnsureCreated(); - - context.Blogs.Add(new Blog { BlogId = 1, Url = "http://sample.com/cats" }); - context.Blogs.Add(new Blog { BlogId = 2, Url = "http://sample.com/catfish" }); - context.Blogs.Add(new Blog { BlogId = 3, Url = "http://sample.com/dogs" }); - context.SaveChanges(); - } - - using (IRepository repo = new EfRepository(new BloggingContext(options))) - await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); - - // Use a separate instance of the context to verify correct data was saved to database - await using (BloggingContext context = new(options)) - { - Blog blog = await context.Blogs.FindAsync(1); - Assert.IsTrue(blog.Url == "http://sample.com/zebras"); - } - } - finally - { - connection.Close(); - } - } - } -} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs new file mode 100644 index 0000000..155eb54 --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs @@ -0,0 +1,36 @@ +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + public partial class RepositoryTests + { + [TestMethod] + public void Update_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + repo.Update(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); + + // Use a separate instance of the context to verify correct data was saved to database + using BloggingContext context = new(testDb.Options); + Blog blog = context.Blogs.Find(1); + Assert.IsTrue(blog.Url == "http://sample.com/zebras"); + } + + [TestMethod] + public async Task UpdateAsync_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Blog blog = await context.Blogs.FindAsync(1); + Assert.IsTrue(blog.Url == "http://sample.com/zebras"); + } + } +} \ No newline at end of file From 54bb470db746185f531b9c9e6836ee268f531d55 Mon Sep 17 00:00:00 2001 From: Hendrik Bulens Date: Fri, 6 Oct 2023 09:28:09 +0200 Subject: [PATCH 3/5] Cleanup --- README.md | 68 +++++-------------- .../Exceptions/ConcurrencyException.cs | 5 -- .../EntityFramework/Utilities/EFExtensions.cs | 4 +- .../Utilities/LinqOperationExtensions.cs | 35 +--------- .../Utilities/LinqOperationHelper.cs | 1 - .../Query Factory/SortingQueryFactory.cs | 1 - .../Helpers/TestDatabase.cs | 6 +- 7 files changed, 23 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 44a5f07..d53b0b7 100644 --- a/README.md +++ b/README.md @@ -4,30 +4,18 @@

Repositories

-## Introduction +Implementation of the repository pattern with Entity Framework. -Implementation of the repository pattern with Entity Framework (Core). - -## Getting Started - -- You must have Visual Studio 2019 Community or higher. -- The dotnet cli is also highly recommended. ## About this project Generic repository pattern with an implementation using Entity Framework. This project revolves around the `IRepository` interface. This interfaces defines the capabilities of a repository which - rather unsurprisingly - are simple CRUD operations. -In addition, this project is also concerned with instantiating the repositories. Rather than accessing the repository's implementation directly, a repository factory (defined by `IRepositoryFactory`) can be used and injected into the application. Support for multi-tenancy is built-in with the `IMultiTenantRepositoryFactory` interface. +In addition, this project is also concerned with instantiating the repositories. Rather than accessing the repository's implementation directly, a repository factory (defined by `IRepositoryFactory`) can be used and injected into the application. The projects in the `Providers` folder provide the implementation of the contracts defined in the Dime.Repositories assembly. -## Build and Test - -- Run dotnet restore -- Run dotnet build -- Run dotnet test - -## Installation +## Getting started Use the package manager NuGet to install Dime.Repositories: @@ -39,55 +27,33 @@ Here's a simple example which demonstrates the usage of the repository. ``` csharp using Dime.Repositories; -... public class CustomerService { - private readonly IRepositoryFactory _repositoryFactory; - - public CustomerService(IRepositoryFactory repositoryFactory) + private readonly IRepository _repository; + public CustomerService(IRepository repository) { - _repositoryFactory = repositoryFactory; + _repository = repository; } public async IEnumerable GetCustomers() - { - using IRepository customerRepository = _repositoryFactory.Create(); - return await customerRepository.FindAllAsync(x => x.IsActive == true); - } + => await _repository_.FindAllAsync(x => x.IsActive == true); } ``` -This is an example of the dependency injection registration in Unity: +## Build and Test -```csharp -public sealed class UnityConfig -{ - public static void RegisterTypes(IUnityContainer container) - { - container.RegisterType>( - new PerRequestOrTransientLifeTimeManager(), - new InjectionConstructor(new MyDbContextEfContextFactory())); - } -} +To run the solution, you will need: -public class MyDbContextEfContextFactory : MultiTenantContextFactory -{ - ... - - protected override SchedulerContext ConstructContext() - { - MyDbContext ctx = new MyDbContext(); - ctx.Configuration.ProxyCreationEnabled = false; - ctx.Configuration.LazyLoadingEnabled = false; - ctx.Configuration.AutoDetectChangesEnabled = false; - ctx.Configuration.UseDatabaseNullSemantics = true; - ctx.Database.CommandTimeout = 60; - return ctx; - } -} -``` +- You must have Visual Studio 2022 Community or higher. +- The dotnet cli is also highly recommended. + +To run the tests, you can use the trustee dotnet cli commands: +- Run dotnet restore +- Run dotnet build +- Run dotnet test + ## Contributing ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) diff --git a/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs b/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs index 70e4da5..a70fd98 100644 --- a/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs +++ b/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs @@ -1,5 +1,4 @@ using System; -using System.Runtime.Serialization; namespace Dime.Repositories { @@ -17,9 +16,5 @@ public ConcurrencyException(string message) : base(message) public ConcurrencyException(string message, Exception innerException) : base(message, innerException) { } - - protected ConcurrencyException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Utilities/EFExtensions.cs b/src/providers/EntityFramework/Utilities/EFExtensions.cs index 11399b4..06570f6 100644 --- a/src/providers/EntityFramework/Utilities/EFExtensions.cs +++ b/src/providers/EntityFramework/Utilities/EFExtensions.cs @@ -18,7 +18,7 @@ internal static IQueryable Include(this IQueryable qu return query; List includeList = new(); - if (includes.Any()) + if (includes.Length != 0) return includes .Where(x => !string.IsNullOrEmpty(x) && !includeList.Contains(x)) .Aggregate(query, (current, include) => current.Include(include)); @@ -43,7 +43,7 @@ internal static IQueryable IncludeView(this IQueryabl where TEntity : class where TResult : class { - if (includes != null && includes.Any()) + if (includes != null && includes.Length != 0) return includes.Where(include => include != null) .Aggregate(query, (current, include) => current.Include(context, include)); diff --git a/src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs b/src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs index 913591a..dda720a 100644 --- a/src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs +++ b/src/providers/EntityFramework/Utilities/LinqOperationExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -8,56 +7,24 @@ namespace Dime.Repositories [ExcludeFromCodeCoverage] internal static class OrderLinq { - /// - /// - /// - /// - /// - /// - /// - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] internal static IOrderedQueryable OrderDescending(this IEnumerable query, string propertyName) { LinqOrderHelper helper = new("OrderByDescending", propertyName); return helper.GetAsQueryable(query); } - /// - /// - /// - /// - /// - /// - /// - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] internal static IOrderedQueryable Order(this IEnumerable query, string propertyName) { LinqOrderHelper helper = new("OrderBy", propertyName); return helper.GetAsQueryable(query); } - /// - /// - /// - /// - /// - /// - /// - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] internal static IOrderedQueryable ThenBy(this IEnumerable source, string property) { LinqOrderHelper helper = new("ThenBy", property); return helper.GetAsQueryable(source); } - /// - /// - /// - /// - /// - /// - /// - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] internal static IOrderedQueryable ThenByDescending(this IEnumerable source, string property) { LinqOrderHelper helper = new("ThenByDescending", property); diff --git a/src/providers/EntityFramework/Utilities/LinqOperationHelper.cs b/src/providers/EntityFramework/Utilities/LinqOperationHelper.cs index d00fbc9..15a8b2a 100644 --- a/src/providers/EntityFramework/Utilities/LinqOperationHelper.cs +++ b/src/providers/EntityFramework/Utilities/LinqOperationHelper.cs @@ -7,7 +7,6 @@ namespace Dime.Repositories { - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] [ExcludeFromCodeCoverage] internal class LinqOrderHelper { diff --git a/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs index 02824fc..6e1f5f7 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs @@ -5,7 +5,6 @@ namespace Dime.Repositories { - [Obsolete("In a next release this will be internal. Fetch a utility library instead.", false)] internal static partial class QueryFactory { /// diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs index 5ee19ad..166ec1a 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/TestDatabase.cs @@ -5,7 +5,7 @@ namespace Dime.Repositories.Sql.EntityFramework.Tests { internal class TestDatabase : IDisposable - { + { internal TestDatabase() { // In-memory database only exists while the connection is open @@ -19,8 +19,8 @@ internal TestDatabase() internal SqliteConnection Connection { get; private set; } - internal DbContextOptions Options { get; private set; } - + internal DbContextOptions Options { get; private set; } + internal void CreateDatabase() { // Create the schema in the database From 2fded9be0976d1a9b6b35fe8ffbb3b86964308e1 Mon Sep 17 00:00:00 2001 From: Hendrik Bulens Date: Sun, 8 Oct 2023 20:09:03 +0200 Subject: [PATCH 4/5] Dispose DB context on repository disposal --- .../Dime.Repositories.Sql.csproj | 2 +- .../Dime.Repositories.csproj | 2 +- src/core/Dime.Repositories/Models/Order.cs | 10 -- src/core/Dime.Repositories/Models/Page.cs | 41 ------ .../Configuration/RepositoryConfiguration.cs | 5 +- ...me.Repositories.Sql.EntityFramework.csproj | 2 +- .../Exceptions/ConcurrencyException.cs | 2 + .../ConstraintViolationException.cs | 22 +--- .../Exceptions/DatabaseAccessException.cs | 24 +--- .../Async/AggregateRepositoryAsync.cs | 17 +-- .../Repository/Async/CreateRepositoryAsync.cs | 17 +-- .../Repository/Async/DeleteRepositoryAsync.cs | 23 ++-- .../Repository/Async/GetRepositoryAsync.cs | 102 ++------------- .../Repository/Async/PagedRepositoryAsync.cs | 36 ++---- .../Repository/Async/RepositoryAsync.cs | 16 +-- .../Repository/Async/SqlRepositoryAsync.cs | 8 +- .../Repository/Async/UpdateRepositoryAsync.cs | 8 +- .../Repository/Sync/AggregateRepository.cs | 16 --- .../Repository/Sync/DeleteRepository.cs | 44 +------ .../Repository/Sync/GetRepository.cs | 99 +-------------- .../Repository/Sync/PagedRepository.cs | 119 ------------------ .../Repository/Sync/Repository.cs | 9 +- .../Sync/StoredProcedureRepository.cs | 9 +- .../Repository/Sync/UpdateRepository.cs | 30 +---- .../Utilities/DataReaderExtensions.cs | 12 -- .../Utilities/DbContextExtensions.cs | 26 ++-- .../EntityFramework/Utilities/EFExtensions.cs | 10 +- .../Query Factory/GroupByQueryFactory.cs | 24 ---- .../Query Factory/SelectQueryFactory.cs | 49 -------- .../Query Factory/SkipQueryFactory.cs | 36 ------ .../Query Factory/SortingQueryFactory.cs | 31 ----- .../Query Factory/TakeQueryFactory.cs | 15 +-- .../Query Factory/WhereQueryFactory.cs | 14 --- .../CountAsyncTests.cs | 29 +++++ .../CountTests.cs | 24 +--- .../CreateAsyncTests.cs | 39 ++++++ .../CreateTests.cs | 3 +- .../DeleteAsyncTests.cs | 38 ++++++ .../DeleteTests.cs | 32 +---- ...ositories.Sql.EntityFramework.Tests.csproj | 1 - .../GetAsyncTests.cs | 20 +++ .../GetTests.cs | 12 +- .../Helpers/BloggingContext.cs | 1 + .../PagedAsyncTests.cs | 30 +++++ .../UpdateAsyncTests.cs | 41 ++++++ .../UpdateTests.cs | 25 +++- 46 files changed, 306 insertions(+), 869 deletions(-) create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountAsyncTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateAsyncTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteAsyncTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetAsyncTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/PagedAsyncTests.cs create mode 100644 src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateAsyncTests.cs diff --git a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj index a30bf67..b15e37c 100644 --- a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj +++ b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj @@ -5,7 +5,7 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.39 + 2.0.0.0-alpha.40 Dime Software net8.0 Dime.Repositories.Sql diff --git a/src/core/Dime.Repositories/Dime.Repositories.csproj b/src/core/Dime.Repositories/Dime.Repositories.csproj index b503bc5..9bbd428 100644 --- a/src/core/Dime.Repositories/Dime.Repositories.csproj +++ b/src/core/Dime.Repositories/Dime.Repositories.csproj @@ -4,7 +4,7 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.49 + 2.0.0.0-alpha.50 net8.0 Dime.Repositories Dime.Repositories diff --git a/src/core/Dime.Repositories/Models/Order.cs b/src/core/Dime.Repositories/Models/Order.cs index 4ac5650..686fc56 100644 --- a/src/core/Dime.Repositories/Models/Order.cs +++ b/src/core/Dime.Repositories/Models/Order.cs @@ -1,9 +1,5 @@ namespace Dime.Repositories { - /// - /// - /// - /// public class Order : IOrder { public Order(string property, bool isAscending) @@ -12,14 +8,8 @@ public Order(string property, bool isAscending) IsAscending = isAscending; } - /// - /// Gets or sets the sorting property - /// public string Property { get; set; } - /// - /// Gets or sets the sorting direction - /// public bool IsAscending { get; set; } public void Deconstruct(out string property, out bool isAscending) diff --git a/src/core/Dime.Repositories/Models/Page.cs b/src/core/Dime.Repositories/Models/Page.cs index b47c761..d8c75d4 100644 --- a/src/core/Dime.Repositories/Models/Page.cs +++ b/src/core/Dime.Repositories/Models/Page.cs @@ -4,84 +4,43 @@ namespace Dime.Repositories { - /// - /// - /// - /// [KnownType(typeof(Page<>))] public class Page : IPage { - /// - /// Default constructor - /// public Page() { } - /// - /// - /// - /// public Page(IEnumerable data) { Data = data; Summary = new List(); } - /// - /// - /// - /// - /// public Page(IEnumerable data, int total) : this(data) { Total = total; } - /// - /// - /// - /// - /// - /// public Page(IEnumerable data, int total, string message) : this(data, total) { Message = message; } - /// - /// - /// - /// - /// - /// - /// public Page(IEnumerable data, int total, string message, IEnumerable summary) : this(data, total, message) { Summary = summary != null ? summary.ToList() : new List(); } - /// - /// - /// public IEnumerable Data { get; set; } - /// - /// - /// public int Total { get; set; } - /// - /// - /// public string Message { get; set; } - /// - /// - /// public List Summary { get; } } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs b/src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs index 5f8c103..8a6d395 100644 --- a/src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs +++ b/src/providers/EntityFramework/Configuration/RepositoryConfiguration.cs @@ -1,8 +1,11 @@ -namespace Dime.Repositories +using System.Diagnostics.CodeAnalysis; + +namespace Dime.Repositories { /// /// Represents a repository configuration object /// + [ExcludeFromCodeCoverage] public class RepositoryConfiguration { /// diff --git a/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj index 32a0dae..397cf8e 100644 --- a/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj +++ b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj @@ -24,7 +24,7 @@ Dime.Repositories.Sql.EntityFramework Entity Framework;Repository;SQL https://cdn.dime-software.com/dime-software/logo-shape.png - 2.0.0.0-alpha.48 + 2.0.0.0-alpha.49 Implementation of the repository pattern with Microsoft SQL using Entity Framework Core Copyright © 2023 https://github.com/dimesoftware/repository diff --git a/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs b/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs index a70fd98..a9b27a9 100644 --- a/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs +++ b/src/providers/EntityFramework/Exceptions/ConcurrencyException.cs @@ -1,8 +1,10 @@ using System; +using System.Diagnostics.CodeAnalysis; namespace Dime.Repositories { [Serializable] + [ExcludeFromCodeCoverage] public class ConcurrencyException : Exception { public ConcurrencyException() diff --git a/src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs b/src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs index 0afbe20..a194fd0 100644 --- a/src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs +++ b/src/providers/EntityFramework/Exceptions/ConstraintViolationException.cs @@ -1,43 +1,25 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; namespace Dime.Repositories { - /// - /// Exception to indicate an error with constraints such as PK and FK - /// [Serializable] + [ExcludeFromCodeCoverage] public class ConstraintViolationException : Exception { - /// - /// Default constructor - /// public ConstraintViolationException() { } - /// - /// Constructor accepting the message - /// - /// The exception message public ConstraintViolationException(string message) : base(message) { } - /// - /// - /// - /// The exception message - /// The exception that was caught public ConstraintViolationException(string message, Exception innerException) : base(message, innerException) { } - /// - /// - /// - /// SerializationInfo for the exception - /// The Streaming Context protected ConstraintViolationException(SerializationInfo info, StreamingContext context) : base(info, context) { } diff --git a/src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs b/src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs index e5691b1..706f8ef 100644 --- a/src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs +++ b/src/providers/EntityFramework/Exceptions/DatabaseAccessException.cs @@ -1,47 +1,27 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; namespace Dime.Repositories { - /// - /// Exception to indicate a general error with the database - /// [Serializable] + [ExcludeFromCodeCoverage] public class DatabaseAccessException : Exception { - /// - /// Initializes a new instance of the System.Exception class. - /// public DatabaseAccessException() { } - /// - /// Initializes a new instance of the System.Exception class with a specified error message - /// - /// The error message that explains the reason for the exception. public DatabaseAccessException(string message) : base(message) { } - /// - /// Initializes a new instance of the System.Exception class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// The error message that explains the reason for the exception. - /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. public DatabaseAccessException(string message, Exception innerException) : base(message, innerException) { } - /// - /// Initializes a new instance of the System.Exception class with a specified error - /// message and a reference to the inner exception that is the cause of this exception. - /// - /// SerializationInfo for the exception - /// The Streaming Context protected DatabaseAccessException(SerializationInfo info, StreamingContext context) : base(info, context) { diff --git a/src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs index 46f9428..a1a750d 100644 --- a/src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/AggregateRepositoryAsync.cs @@ -6,24 +6,9 @@ namespace Dime.Repositories { public partial class EfRepository { - /// - /// Counts the amount of records in the data store for the table that corresponds to the entity type . - /// - /// A number of the amount of records public Task CountAsync() - { - long count; - using (TContext ctx = Context) - count = ctx.Count(); - - return Task.FromResult(count); - } + => CountAsync(null); - /// - /// Counts the amount of records in the data store for the table that corresponds to the entity type . - /// - /// A number of the amount of records - /// The expression to execute against the data store public Task CountAsync(Expression> where) { long count; diff --git a/src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs index 86bcb0a..9095933 100644 --- a/src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/CreateRepositoryAsync.cs @@ -3,24 +3,15 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; - -#if NET461 - -using System.Data.Entity; - -#else - using Microsoft.EntityFrameworkCore; -#endif - namespace Dime.Repositories { public partial class EfRepository { public virtual async Task CreateAsync(TEntity entity) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Entry(entity).State = EntityState.Added; TEntity createdItem = ctx.Set().Add(entity)?.Entity; await SaveChangesAsync(ctx); @@ -46,7 +37,7 @@ public virtual async Task CreateAsync(TEntity entity, Expression CreateAsync(TEntity entity, Func beforeSaveAction) { - await using TContext ctx = Context; + TContext ctx = Context; await beforeSaveAction(entity, ctx); ctx.Entry(entity).State = EntityState.Added; @@ -58,7 +49,7 @@ public virtual async Task CreateAsync(TEntity entity, Func CreateAsync(TEntity entity, bool commit) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Entry(entity).State = EntityState.Added; TEntity createdItem = ctx.Set().Add(entity)?.Entity; @@ -74,7 +65,7 @@ public virtual async Task> CreateAsync(IQueryable e return entities; List newEntities = new(); - await using TContext ctx = Context; + TContext ctx = Context; foreach (TEntity entity in entities.ToList()) { ctx.Entry(entity).State = EntityState.Added; diff --git a/src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs index 0b611ff..e42e5e5 100644 --- a/src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/DeleteRepositoryAsync.cs @@ -4,17 +4,8 @@ using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; - -#if NET461 - -using System.Data.Entity; - -#else - using Microsoft.EntityFrameworkCore; -#endif - namespace Dime.Repositories { public partial class EfRepository @@ -28,7 +19,7 @@ public virtual async Task DeleteAsync(object? id) return; } - await using TContext ctx = Context; + TContext ctx = Context; TEntity item = await ctx.Set().FindAsync(id); if (item != default(TEntity)) { @@ -39,7 +30,7 @@ public virtual async Task DeleteAsync(object? id) public virtual async Task DeleteAsync(IEnumerable ids) { - await using TContext ctx = Context; + TContext ctx = Context; foreach (object id in ids.Distinct().ToList()) { TEntity item = await ctx.Set().FindAsync(id); @@ -54,7 +45,7 @@ public virtual async Task DeleteAsync(IEnumerable ids) public virtual async Task DeleteAsync(object? id, bool commit) { - await using TContext ctx = Context; + TContext ctx = Context; TEntity item = await ctx.Set().FindAsync(id); if (item != default(TEntity)) { @@ -66,7 +57,7 @@ public virtual async Task DeleteAsync(object? id, bool commit) public virtual async Task DeleteAsync(TEntity entity) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Set().Attach(entity); ctx.Set().Remove(entity); await SaveChangesAsync(ctx); @@ -77,7 +68,7 @@ public async Task DeleteAsync(IEnumerable entities) if (!entities.Any()) return; - await using TContext ctx = Context; + TContext ctx = Context; foreach (TEntity entity in entities) { ctx.Set().Attach(entity); @@ -89,7 +80,7 @@ public async Task DeleteAsync(IEnumerable entities) public virtual async Task DeleteAsync(TEntity entity, bool commit) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Set().Attach(entity); ctx.Set().Remove(entity); @@ -99,7 +90,7 @@ public virtual async Task DeleteAsync(TEntity entity, bool commit) public virtual async Task DeleteAsync(Expression> where) { - await using TContext ctx = Context; + TContext ctx = Context; IEnumerable entities = ctx.Set().With(where).AsNoTracking().ToList(); if (entities.Any()) { diff --git a/src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs index 65c3a74..beb2d31 100644 --- a/src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/GetRepositoryAsync.cs @@ -1,72 +1,39 @@ using System; using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; -using System.Data.Entity.Core.Metadata.Edm; -using System.Data.Entity.Infrastructure; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using LinqKit; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { public partial class EfRepository { - /// - /// Checks if there is any record that matches the query - /// - /// The query to execute in the Any method - /// True if there is at least one record public virtual async Task ExistsAsync(Expression> where) { - await using TContext ctx = Context; + TContext ctx = Context; return await ctx.Set().AsNoTracking().AnyAsync(where); } - /// - /// Gets the record by its identifier - /// - /// The identifier of the entity - /// The record of type that matches the id public virtual async Task FindByIdAsync(object? id) { - await using TContext ctx = Context; + TContext ctx = Context; return await ctx.Set().FindAsync(id); } - /// - /// Gets the record by its identifier - /// - /// The identifier of the entity - /// The optional list of related entities that should be eagerly loaded - /// The record of type that matches the id public virtual async Task FindByIdAsync(object? id, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; foreach (string include in includes) ctx.Set().Include(include).AsNoTracking(); return await ctx.Set().FindAsync(id); } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The expression to execute against the data store - /// The first record of type that matches the query public virtual async Task FindOneAsync(Expression> where) { - await using TContext ctx = Context; + TContext ctx = Context; TEntity query = ctx.Set() .AsNoTracking() .AsExpandable() @@ -76,37 +43,16 @@ public virtual async Task FindOneAsync(Expression> return await Task.Run(() => query); } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The expression to execute against the data store - /// The optional list of related entities that should be eagerly loaded - /// The first record of type that matches the query public virtual async Task FindOneAsync(Expression> where, params string[] includes) { - await using TContext ctx = Context; - IQueryable query = ctx.Set() + TContext ctx = Context; + return ctx.Set() .Include(ctx, includes) .AsNoTracking() .AsExpandable() - .With(where); - - IQueryable fullGraphQuery = await Task.Run(() => query); - return fullGraphQuery.FirstOrDefault(); + .WithFirst(where); } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The projected class - /// The expression to execute against the data store - /// The expression for the projection of type that should be executed against the data store - /// The sorting expression to execute against the data store - /// Indicates whether the sorting is ascending (true) or descending (false) - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An instance of with the mapped data from the record that matched all filters. public Task FindOneAsync( Expression> where = null, Expression> select = null, @@ -131,15 +77,9 @@ public Task FindOneAsync( return Task.FromResult(query.FirstOrDefault()); } - /// - /// Finds entities based on provided criteria. - /// - /// The expression to execute against the data store - /// The optional list of related entities that should be eagerly loaded - /// An collection of that matched all filters. public virtual async Task> FindAllAsync(Expression> where, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) .AsExpandable() @@ -150,18 +90,6 @@ public virtual async Task> FindAllAsync(Expression - /// Gets the first record from the data store that matches the parameter - /// - /// The projected class - /// The expression to execute against the data store - /// The expression for the projection of type that should be executed against the data store - /// The sorting expression to execute against the data store - /// Indicates whether the sorting is ascending (true) or descending (false) - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An instance of with the mapped data from the record that matched all filters. public virtual Task> FindAllAsync( Expression> where = null, Expression> select = null, @@ -186,16 +114,6 @@ public virtual Task> FindAllAsync( return Task.FromResult(query.ToList() as IEnumerable); } - /// - /// Retrieves a collection of paged, sorted and filtered items in a flat list - /// - /// The expression to execute against the data store - /// The order by. - /// - /// The page number which is multiplied by the page size to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An collection of with the mapped data from the records that matched all filters. public virtual async Task> FindAllAsync( Expression> where = null, Expression> orderBy = null, @@ -204,7 +122,7 @@ public virtual async Task> FindAllAsync( int? pageSize = null, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) .AsExpandable() diff --git a/src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs index 634b0a0..e42cdca 100644 --- a/src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/PagedRepositoryAsync.cs @@ -1,27 +1,15 @@ using System; using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; using LinqKit; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { public partial class EfRepository { - #region Projected Pages - /// /// Finds all asynchronous. /// @@ -129,7 +117,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) where TResult : class { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -171,7 +159,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) where TResult : class { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -186,10 +174,6 @@ public async Task> FindAllPagedAsync( return await Task.FromResult(new Page(query.ToList(), ctx.Count(count))); } - #endregion Projected Pages - - #region Unprojected Pages - /// /// /// @@ -208,7 +192,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -246,7 +230,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -281,7 +265,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -314,7 +298,7 @@ public async Task> FindAllPagedAsync( bool trackChanges = false, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -347,7 +331,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -379,7 +363,7 @@ public async Task> FindAllPagedAsync( int? pageSize = default, params string[] includes) { - await using TContext ctx = Context; + TContext ctx = Context; IQueryable query = ctx.Set() .Include(ctx, includes) @@ -393,7 +377,5 @@ public async Task> FindAllPagedAsync( return await Task.FromResult(new Page(query.ToList(), ctx.Count(where))); } - - #endregion Unprojected Pages } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs index 4179a76..945f944 100644 --- a/src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/RepositoryAsync.cs @@ -48,12 +48,8 @@ public virtual async Task SaveChangesAsync() throw sqlException.Number switch { 2627 => (Exception)new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , + 547 => new ConstraintViolationException(sqlException.Message, sqlException), + 2601 => new ConstraintViolationException(sqlException.Message, sqlException), _ => new DatabaseAccessException(sqlException.Message, sqlException) }; } @@ -105,12 +101,8 @@ public virtual async Task SaveChangesAsync(TContext context) throw sqlException.Number switch { 2627 => (Exception)new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , + 547 => new ConstraintViolationException(sqlException.Message, sqlException), + 2601 => new ConstraintViolationException(sqlException.Message, sqlException), _ => new DatabaseAccessException(sqlException.Message, sqlException) }; } diff --git a/src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs index 0acca46..c027775 100644 --- a/src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/SqlRepositoryAsync.cs @@ -12,7 +12,7 @@ public partial class EfRepository { public async Task ExecuteSqlAsync(string sql) { - await using TContext ctx = Context; + TContext ctx = Context; await ctx.Database.ExecuteSqlRawAsync(sql); } @@ -25,7 +25,7 @@ string ExecQuery(string x, DbParameter[] y) } string execQueryString = ExecQuery(name, parameters); - await using TContext ctx = Context; + TContext ctx = Context; return await ctx.Database.ExecuteSqlRawAsync(execQueryString, parameters); } @@ -38,7 +38,7 @@ string ExecQuery(string x, DbParameter[] y) } string execQueryString = ExecQuery(name, parameters); - await using TContext ctx = Context; + TContext ctx = Context; return await ctx.Database.ExecuteSqlRawAsync(execQueryString, parameters); } @@ -65,7 +65,7 @@ string ExecQuery(string x, DbParameter[] y) } string execQueryString = ExecQuery(nameof(name), parameters); - await using TContext ctx = Context; + TContext ctx = Context; return 1; } diff --git a/src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs b/src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs index 603dbd6..ff78814 100644 --- a/src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs +++ b/src/providers/EntityFramework/Repository/Async/UpdateRepositoryAsync.cs @@ -12,7 +12,7 @@ public partial class EfRepository { public virtual async Task UpdateAsync(TEntity entity, bool commitChanges = true) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Set().Attach(entity); ctx.Entry(entity).State = EntityState.Modified; @@ -27,7 +27,7 @@ public async Task UpdateAsync(IEnumerable entities, bool commitChanges if (!entities.Any()) return; - await using TContext ctx = Context; + TContext ctx = Context; foreach (TEntity entity in entities) { ctx.Set().Attach(entity); @@ -39,7 +39,7 @@ public async Task UpdateAsync(IEnumerable entities, bool commitChanges public virtual async Task UpdateAsync(TEntity entity, params string[] properties) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Set().Attach(entity); EntityEntry entry = ctx.Entry(entity); @@ -53,7 +53,7 @@ public virtual async Task UpdateAsync(TEntity entity, params string[] p public virtual async Task UpdateAsync(TEntity entity, params Expression>[] properties) { - await using TContext ctx = Context; + TContext ctx = Context; ctx.Set().Attach(entity); EntityEntry entry = ctx.Entry(entity); diff --git a/src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs b/src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs index 6aa199a..a783d5a 100644 --- a/src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/AggregateRepository.cs @@ -1,23 +1,12 @@ using System; using System.Linq; using System.Linq.Expressions; - -#if NET461 - -#else - using Microsoft.EntityFrameworkCore; -#endif - namespace Dime.Repositories { public partial class EfRepository { - /// - /// Counts the amount of records in the data store for the table that corresponds to the entity type . - /// - /// A number of the amount of records public long Count() { using TContext ctx = Context; @@ -26,11 +15,6 @@ public long Count() return count; } - /// - /// Counts the amount of records in the data store for the table that corresponds to the entity type . - /// - /// A number of the amount of records - /// The expression to execute against the data store public long Count(Expression> where) { using TContext ctx = Context; diff --git a/src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs b/src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs index 251b069..f2e45f1 100644 --- a/src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/DeleteRepository.cs @@ -1,28 +1,13 @@ using System; using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Linq; using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { public partial class EfRepository { - /// - /// Removes the record from the data store by its identifier - /// - /// The identifier of the entity - /// Void public virtual void Delete(object? id) { using TContext ctx = Context; @@ -34,12 +19,6 @@ public virtual void Delete(object? id) SaveChanges(ctx); } - /// - /// Removes the record from the data store by its identifier - /// - /// The identifier of the entity - /// Indicates whether or not SaveChanges should be called during this call - /// Void public virtual void Delete(object? id, bool commit) { using TContext ctx = Context; @@ -52,11 +31,6 @@ public virtual void Delete(object? id, bool commit) SaveChanges(ctx); } - /// - /// Removes the record from the data store - /// - /// The disconnected entity to remove - /// Void public virtual void Delete(TEntity entity) { using TContext ctx = Context; @@ -65,11 +39,6 @@ public virtual void Delete(TEntity entity) SaveChanges(ctx); } - /// - /// Removes the records - /// - /// The disconnected entities to remove - /// Void public void Delete(IEnumerable entities) { if (!entities.Any()) @@ -85,12 +54,6 @@ public void Delete(IEnumerable entities) SaveChanges(ctx); } - /// - /// Removes the record from the data store - /// - /// The disconnected entity to remove - /// Indicates whether or not SaveChanges should be called during this call - /// public virtual void Delete(TEntity entity, bool commit) { using TContext ctx = Context; @@ -101,11 +64,6 @@ public virtual void Delete(TEntity entity, bool commit) SaveChanges(ctx); } - /// - /// Removes the record from the data store - /// - /// The expression to execute against the data store - /// Void public virtual void Delete(Expression> where) { using TContext ctx = Context; diff --git a/src/providers/EntityFramework/Repository/Sync/GetRepository.cs b/src/providers/EntityFramework/Repository/Sync/GetRepository.cs index b7bcdb7..cd33ef4 100644 --- a/src/providers/EntityFramework/Repository/Sync/GetRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/GetRepository.cs @@ -1,52 +1,26 @@ using System; using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Linq; using System.Linq.Expressions; using LinqKit; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { public partial class EfRepository { - /// - /// Checks if there is any record that matches the query - /// - /// The query to execute in the Any method - /// True if there is at least one record public bool Exists(Expression> where) { using TContext ctx = Context; return ctx.Set().AsNoTracking().Any(where); } - /// - /// Gets the record by its identifier - /// - /// The identifier of the entity - /// The record of type that matches the id public virtual TEntity FindById(object? id) { using TContext ctx = Context; return ctx.Set().Find(id); } - /// - /// Gets the record by its identifier - /// - /// The identifier of the entity - /// The optional list of related entities that should be eagerly loaded - /// The record of type that matches the id public virtual TEntity FindById(object? id, params string[] includes) { using TContext ctx = Context; @@ -56,11 +30,6 @@ public virtual TEntity FindById(object? id, params string[] includes) return ctx.Set().Find(id); } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The expression to execute against the data store - /// The first record of type that matches the query public virtual TEntity FindOne(Expression> where) { using TContext ctx = Context; @@ -73,12 +42,6 @@ public virtual TEntity FindOne(Expression> where) return query; } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The expression to execute against the data store - /// The optional list of related entities that should be eagerly loaded - /// The first record of type that matches the query public virtual TEntity FindOne(Expression> where, params string[] includes) { using TContext ctx = Context; @@ -91,18 +54,6 @@ public virtual TEntity FindOne(Expression> where, params str return query.FirstOrDefault(); } - /// - /// Gets the first record from the data store that matches the parameter - /// - /// The projected class - /// The expression to execute against the data store - /// The expression for the projection of type that should be executed against the data store - /// The sorting expression to execute against the data store - /// Indicates whether the sorting is ascending (true) or descending (false) - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An instance of with the mapped data from the record that matched all filters. public TResult FindOne( Expression> where = null, Expression> select = null, @@ -127,11 +78,6 @@ public TResult FindOne( return query.FirstOrDefault(); } - /// - /// Finds entities based on provided criteria. - /// - /// The expression to execute against the data store - /// An collection of that matched all filters. public IEnumerable FindAll(Expression> where) { using TContext ctx = Context; @@ -145,12 +91,6 @@ public IEnumerable FindAll(Expression> where) return query.ToList(); } - /// - /// Finds entities based on provided criteria. - /// - /// The expression to execute against the data store - /// The optional list of related entities that should be eagerly loaded - /// An collection of that matched all filters. public virtual IEnumerable FindAll(Expression> where, params string[] includes) { using TContext ctx = Context; @@ -164,13 +104,6 @@ public virtual IEnumerable FindAll(Expression> wher return query.ToList(); } - /// - /// Finds entities based on provided criteria. - /// - /// The expression to execute against the data store - /// The flag to indicate if all navigation properties should be eagerly loaed - /// The optional list of related entities that should be eagerly loaded - /// An collection of that matched all filters. public IEnumerable FindAll(Expression> where, bool includeAll, params string[] includes) { using TContext ctx = Context; @@ -184,14 +117,6 @@ public IEnumerable FindAll(Expression> where, bool return query.ToList(); } - /// - /// Retrieves a collection of paged and filtered items in a flat list - /// - /// The expression to execute against the data store - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An collection of with the mapped data from the records that matched all filters. public IEnumerable FindAll(Expression> where, int? page, int? pageSize, string[] includes) { using TContext ctx = Context; @@ -205,18 +130,6 @@ public IEnumerable FindAll(Expression> where, int? .Include(Context, includes); } - /// - /// Gets the records from the data store that matches the parameter - /// - /// The projected class - /// The expression to execute against the data store - /// The expression for the projection of type that should be executed against the data store - /// The sorting expression to execute against the data store - /// Indicates whether the sorting is ascending (true) or descending (false) - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An instance of with the mapped data from the record that matched all filters. public virtual IEnumerable FindAll( Expression> where = null, Expression> select = null, @@ -240,16 +153,6 @@ public virtual IEnumerable FindAll( return query.ToList(); } - /// - /// Retrieves a collection of paged, sorted and filtered items in a flat list - /// - /// The expression to execute against the data store - /// The order by. - /// - /// The page number which is multiplied by the pagesize to calculate the amount of items to skip - /// The size of the batch of items that must be retrieved - /// The optional list of related entities that should be eagerly loaded - /// An collection of with the mapped data from the records that matched all filters. public virtual IEnumerable FindAll( Expression> where = null, Expression> orderBy = null, diff --git a/src/providers/EntityFramework/Repository/Sync/PagedRepository.cs b/src/providers/EntityFramework/Repository/Sync/PagedRepository.cs index a302068..fb3e17c 100644 --- a/src/providers/EntityFramework/Repository/Sync/PagedRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/PagedRepository.cs @@ -3,35 +3,12 @@ using System.Linq; using System.Linq.Expressions; using LinqKit; - -#if NET461 - -using System.Data.Entity; - -#else - using Microsoft.EntityFrameworkCore; -#endif - namespace Dime.Repositories { public partial class EfRepository { - #region Projected Pages - - /// - /// Finds all hronous. - /// - /// The type of the result. - /// The where. - /// The select. - /// The order by. - /// - /// The page. - /// Size of the page. - /// The includes. - /// public virtual Page FindAllPaged( Expression> where = null, Expression> select = null, @@ -57,19 +34,6 @@ public virtual Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Func groupBy = null, @@ -96,19 +60,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Expression> select = null, @@ -134,20 +85,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Expression> count = null, @@ -175,20 +112,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(count)); } - #endregion Projected Pages - - #region Unprojected Pages - - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Expression> orderBy = null, @@ -212,17 +135,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Expression> orderBy = null, @@ -246,16 +158,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, Expression> count = null, @@ -278,16 +180,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(count)); } - /// - /// Gets the items hronously. - /// - /// The type of the entity. - /// The where. - /// The order by. - /// The page. - /// Size of the page. - /// The includes. - /// public Page FindAllPaged( Expression> where = null, IEnumerable> orderBy = null, @@ -309,16 +201,6 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// public Page FindAllPaged( Expression> where = null, IEnumerable>> orderBy = null, @@ -342,6 +224,5 @@ public Page FindAllPaged( return new Page(query.ToList(), ctx.Count(where)); } - #endregion Unprojected Pages } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Repository/Sync/Repository.cs b/src/providers/EntityFramework/Repository/Sync/Repository.cs index eb784ac..1933915 100644 --- a/src/providers/EntityFramework/Repository/Sync/Repository.cs +++ b/src/providers/EntityFramework/Repository/Sync/Repository.cs @@ -67,6 +67,7 @@ public virtual bool SaveChanges(TContext context) failedEntry.OriginalValues.SetValues(dbValues); return SaveChanges(context); } + return true; } @@ -86,12 +87,8 @@ public virtual bool SaveChanges(TContext context) throw sqlException.Number switch { 2627 => (Exception)new ConcurrencyException(sqlException.Message, sqlException), - 547 => new ConstraintViolationException(sqlException.Message, - sqlException) - , - 2601 => new ConstraintViolationException(sqlException.Message, - sqlException) - , + 547 => new ConstraintViolationException(sqlException.Message, sqlException), + 2601 => new ConstraintViolationException(sqlException.Message, sqlException), _ => new DatabaseAccessException(sqlException.Message, sqlException) }; } diff --git a/src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs b/src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs index 103c4b0..dc5df2f 100644 --- a/src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/StoredProcedureRepository.cs @@ -1,15 +1,8 @@ using System.Collections.Generic; using System.Data.Common; using System.Linq; -using Microsoft.EntityFrameworkCore; - -#if NET461 -using System.Data.SqlClient; -#else - using Microsoft.Data.SqlClient; - -#endif +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { diff --git a/src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs b/src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs index 51a68fd..844642b 100644 --- a/src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs +++ b/src/providers/EntityFramework/Repository/Sync/UpdateRepository.cs @@ -1,35 +1,13 @@ using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; -using System.Data.Entity.Infrastructure; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Linq; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { public partial class EfRepository { - /// - /// Updates the entities - /// - /// The entities to update - /// public TEntity Update(TEntity entity) => Update(entity, true); - /// - /// Updates the existing entity. - /// - /// The entity to update - /// Indication whether or not the SaveChanges should be called during this call - /// public virtual TEntity Update(TEntity entity, bool commitChanges = true) { using TContext ctx = Context; @@ -42,12 +20,6 @@ public virtual TEntity Update(TEntity entity, bool commitChanges = true) return entity; } - /// - /// Updates the entities - /// - /// The entities to update - /// Indication whether or not the SaveChanges should be called during this call - /// public void Update(IEnumerable entities, bool commitChanges = true) { if (!entities.Any()) diff --git a/src/providers/EntityFramework/Utilities/DataReaderExtensions.cs b/src/providers/EntityFramework/Utilities/DataReaderExtensions.cs index 723da74..8bf3a17 100644 --- a/src/providers/EntityFramework/Utilities/DataReaderExtensions.cs +++ b/src/providers/EntityFramework/Utilities/DataReaderExtensions.cs @@ -7,12 +7,6 @@ namespace Dime.Repositories { internal static class DataReaderExtensions { - /// - /// - /// - /// - /// - /// internal static List GetRecords(this IDataReader reader) { List result = new(); @@ -33,12 +27,6 @@ internal static List GetRecords(this IDataReader reader) return result; } - /// - /// - /// - /// - /// - /// public static bool HasColumn(this IDataRecord dr, string columnName) { for (int i = 0; i < dr.FieldCount; i++) diff --git a/src/providers/EntityFramework/Utilities/DbContextExtensions.cs b/src/providers/EntityFramework/Utilities/DbContextExtensions.cs index ba9ed4f..ed86a7c 100644 --- a/src/providers/EntityFramework/Utilities/DbContextExtensions.cs +++ b/src/providers/EntityFramework/Utilities/DbContextExtensions.cs @@ -1,36 +1,26 @@ using System; - -#if NET461 - -using System.Data.Entity; - -#else - -using Microsoft.EntityFrameworkCore; - -#endif - using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; +using System.Threading.Tasks; using LinqKit; +using Microsoft.EntityFrameworkCore; namespace Dime.Repositories { [ExcludeFromCodeCoverage] internal static class DbContextExtensions { - /// - /// - /// - /// - /// - /// - /// internal static int Count(this DbContext ctx, Expression> query = null) where TEntity : class => query == null ? ctx.Set().AsExpandable().AsNoTracking().Count() : ctx.Set().AsExpandable().AsNoTracking().Count(query); + + internal static Task CountAsync(this DbContext ctx, Expression> query = null) + where TEntity : class + => query == null + ? ctx.Set().AsExpandable().AsNoTracking().CountAsync() + : ctx.Set().AsExpandable().AsNoTracking().CountAsync(query); } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Utilities/EFExtensions.cs b/src/providers/EntityFramework/Utilities/EFExtensions.cs index 06570f6..27d8e5e 100644 --- a/src/providers/EntityFramework/Utilities/EFExtensions.cs +++ b/src/providers/EntityFramework/Utilities/EFExtensions.cs @@ -1,6 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; @@ -51,5 +53,11 @@ internal static IQueryable IncludeView(this IQueryabl .GetNavigations() .Aggregate(query, (current, navigationProperty) => current.Include(context, navigationProperty.Name)); } + + public static Task> ToListAsyncSafe(this IQueryable source) + { + ArgumentNullException.ThrowIfNull(source); + return source is not IAsyncEnumerable ? Task.FromResult(source.ToList()) : source.ToListAsync(); + } } } \ No newline at end of file diff --git a/src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs index 0b5b073..75ac72e 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/GroupByQueryFactory.cs @@ -8,36 +8,12 @@ namespace Dime.Repositories [ExcludeFromCodeCoverage] internal static partial class QueryFactory { - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// public static IQueryable> WithGroup(this IQueryable source, Expression> predicate) => source.GroupBy(predicate); - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// public static IQueryable> WithGroup(this IQueryable source, Func predicate) => source.GroupBy(predicate).AsQueryable(); - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// - /// The source. - /// The predicate. - /// public static IQueryable> WithGroup(this IOrderedEnumerable source, Func predicate) => source.GroupBy(predicate).AsQueryable(); } diff --git a/src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs index 0810e97..d754219 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/SelectQueryFactory.cs @@ -7,82 +7,33 @@ namespace Dime.Repositories { internal static partial class QueryFactory { - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// - /// The source. - /// The selector. - /// internal static IQueryable WithSelect( this IQueryable> source, Expression, IEnumerable>> selector) => selector == null ? default : source.SelectMany(selector); - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// internal static IQueryable WithSelect( this IQueryable> source, Expression, int, TResult>> selector) => selector == null ? default : source.Select(selector); - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// internal static IQueryable WithSelect(this IOrderedEnumerable source, Func selector) where TSource : class where TResult : class => selector == null ? default : source.Select(selector).AsQueryable(); - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// internal static IQueryable WithSelect(this IQueryable source, Expression> selector) where TSource : class => selector == null ? default : source.Select(selector).AsQueryable(); - /// - /// Withes the specified selector. - /// - /// The type of the source. - /// The type of the result. - /// The source. - /// The selector. - /// internal static IQueryable WithSelect(this IQueryable source, Func selector) where TSource : class where TResult : class => selector == null ? default : source.Select(selector).AsQueryable(); - /// - /// - /// - /// - /// - /// - /// - /// internal static TResult WithFirstSelect(this TSource source, Expression> selector) where TSource : class diff --git a/src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs index 6daf9c9..333407d 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/SkipQueryFactory.cs @@ -1,14 +1,5 @@ using System; using System.Collections.Generic; - -#if NET461 - -using System.Data.Entity; - -#else - -#endif - using System.Linq; using System.Linq.Expressions; @@ -16,15 +7,6 @@ namespace Dime.Repositories { internal static partial class QueryFactory { - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. internal static IQueryable With(this IQueryable source, int? page, int? pageSize, IEnumerable> orderBy) { int pageToApply = page.GetValueOrDefault(); @@ -40,15 +22,6 @@ internal static IQueryable With(this IQueryable sourc : source.Skip(itemsToSkip); } - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. internal static IQueryable With(this IQueryable source, int? page, int? pageSize, Expression> orderBy) { int pageToApply = page.GetValueOrDefault(); @@ -64,15 +37,6 @@ internal static IQueryable With(this IQueryable sourc source.Skip(itemsToSkip); } - /// - /// Bypasses a specified number of elements in a sequence and then returns the remaining elements. - /// - /// The type of the elements of source. - /// An System.Linq.IQueryable`1 to return elements from. - /// The number of elements to skip before returning the remaining elements. - /// Size of the page. - /// - /// An System.Linq.IQueryable`1 that contains elements that occur after the specified index in the input sequence. internal static IQueryable With(this IQueryable source, int? page, int? pageSize, IEnumerable>> orderBy) { int pageToApply = page.GetValueOrDefault(); diff --git a/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs index 6e1f5f7..fa5421c 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/SortingQueryFactory.cs @@ -7,13 +7,6 @@ namespace Dime.Repositories { internal static partial class QueryFactory { - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// internal static IQueryable WithOrder(this IQueryable source, IEnumerable> orderByExpression) { if (orderByExpression != null && orderByExpression.Count() > 1) @@ -38,14 +31,6 @@ internal static IQueryable WithOrder(this IQueryable return source.OrderBy(x => true); } - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// internal static IQueryable WithOrder(this IQueryable source, IEnumerable>> orderByExpression, bool ascending) { if (orderByExpression == null) @@ -69,14 +54,6 @@ internal static IQueryable WithOrder(this IQueryable } } - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// internal static IQueryable WithOrder(this IQueryable source, Expression> orderByExpression, bool ascending) { if (orderByExpression == null) @@ -87,14 +64,6 @@ internal static IQueryable WithOrder(this IQueryable : source.OrderByDescending(compiledExpression).AsQueryable(); } - /// - /// Wrapper around LINQ ORDER BY - /// - /// The type of the source. - /// The source. - /// The order by expression. - /// - /// internal static IQueryable WithOrder(this IQueryable source, Func orderByExpression, bool ascending) { if (orderByExpression == null) diff --git a/src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs index 47ac7ff..726872c 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/TakeQueryFactory.cs @@ -1,10 +1,4 @@ -#if NET461 - -using System.Data.Entity; - -#endif - -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace Dime.Repositories @@ -12,13 +6,6 @@ namespace Dime.Repositories [ExcludeFromCodeCoverage] internal static class TakeQueryFactory { - /// - /// Returns a specified number of contiguous elements from the start of a sequence. - /// - /// The type of the source. - /// The sequence to return elements from. - /// The number of elements to return. - /// An System.Linq.IQueryable`1 that contains the specified number of elements from the start of source. internal static IQueryable With(this IQueryable source, int? takeCount) { int itemsToTake = takeCount.GetValueOrDefault(); diff --git a/src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs b/src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs index ee48f92..d17088e 100644 --- a/src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs +++ b/src/providers/EntityFramework/Utilities/Query Factory/WhereQueryFactory.cs @@ -6,23 +6,9 @@ namespace Dime.Repositories { internal static partial class QueryFactory { - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// The source. - /// The predicate. - /// internal static IQueryable With(this IQueryable source, Expression> predicate) => predicate == null ? source : source.Where(predicate); - /// - /// Wrapper around LINQ WHERE - /// - /// The type of the source. - /// The source. - /// The predicate. - /// internal static TSource WithFirst(this IQueryable source, Expression> predicate) => predicate == null ? source.FirstOrDefault() : source.FirstOrDefault(predicate); } diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountAsyncTests.cs new file mode 100644 index 0000000..7ea3e0f --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountAsyncTests.cs @@ -0,0 +1,29 @@ +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class CountAsyncTests + { + [TestMethod] + public async Task CountAsync_NoPredicate_ShouldCountAll() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = await repo.CountAsync(); + Assert.AreEqual(3, result); + } + + [TestMethod] + public async Task CountAsync_Predicate_ShouldCountCorrectly() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + long result = await repo.CountAsync(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs index ee67f4c..069b5dc 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CountTests.cs @@ -1,8 +1,8 @@ -using System.Threading.Tasks; -using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Dime.Repositories.Sql.EntityFramework.Tests { + [TestClass] public partial class CountTests { [TestMethod] @@ -15,26 +15,6 @@ public void Count_NoPredicate_ShouldCountAll() Assert.AreEqual(3, result); } - [TestMethod] - public async Task CountAsync_NoPredicate_ShouldCountAll() - { - using TestDatabase testDb = new(); - - using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - long result = await repo.CountAsync(); - Assert.AreEqual(3, result); - } - - [TestMethod] - public async Task CountAsync_Predicate_ShouldCountCorrectly() - { - using TestDatabase testDb = new(); - - using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - long result = await repo.CountAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result); - } - [TestMethod] public void Count_Predicate_ShouldCountCorrectly() { diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateAsyncTests.cs new file mode 100644 index 0000000..8ad5f5f --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateAsyncTests.cs @@ -0,0 +1,39 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class CreateAsyncTests + { + [TestMethod] + public void Create_ShouldAddOne() + { + using TestDatabase testDb = new(); + + // Run the test against one instance of the context + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + repo.Create(new Blog { Url = "http://sample.com" }); + + // Use a separate instance of the context to verify correct data was saved to database + using BloggingContext context = new(testDb.Options); + Assert.AreEqual(4, context.Blogs.Count()); + Assert.AreEqual("http://sample.com", context.Blogs.OrderByDescending(x => x.BlogId).First().Url); + } + + [TestMethod] + public async Task CreateAsync_ShouldAddOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.CreateAsync(new Blog { Url = "http://sample.com" }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(4, context.Blogs.Count()); + Assert.AreEqual("http://sample.com", context.Blogs.OrderByDescending(x => x.BlogId).First().Url); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs index 24967ef..91ab750 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/CreateTests.cs @@ -4,7 +4,8 @@ namespace Dime.Repositories.Sql.EntityFramework.Tests { - public partial class RepositoryTests + [TestClass] + public partial class CreateTests { [TestMethod] public void Create_ShouldAddOne() diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteAsyncTests.cs new file mode 100644 index 0000000..3824a6f --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteAsyncTests.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class DeleteAsyncTests + { + [TestMethod] + public async Task DeleteAsync_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.DeleteAsync(new Blog { BlogId = 1 }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(2, context.Blogs.Count()); + } + + [TestMethod] + public async Task DeleteAsync_ByIds_ShouldRemoveList() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + + List ids = new() { 1, 2 }; + await repo.DeleteAsync(ids); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Assert.AreEqual(1, context.Blogs.Count()); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs index 364235d..145a506 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/DeleteTests.cs @@ -1,12 +1,11 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Dime.Repositories.Sql.EntityFramework.Tests { - public partial class RepositoryTests + [TestClass] + public partial class DeleteTests { [TestMethod] public void Delete_ByEntity_ShouldRemoveOne() @@ -16,35 +15,8 @@ public void Delete_ByEntity_ShouldRemoveOne() using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); repo.Delete(new Blog { BlogId = 1 }); - // Use a separate instance of the context to verify correct data was saved to database using BloggingContext context = new(testDb.Options); Assert.AreEqual(2, context.Blogs.Count()); } - - [TestMethod] - public async Task DeleteAsync_ByEntity_ShouldRemoveOne() - { - using TestDatabase testDb = new(); - using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - await repo.DeleteAsync(new Blog { BlogId = 1 }); - - // Use a separate instance of the context to verify correct data was saved to database - await using BloggingContext context = new(testDb.Options); - Assert.AreEqual(2, context.Blogs.Count()); - } - - [TestMethod] - public async Task DeleteAsync_ByIds_ShouldRemoveList() - { - using TestDatabase testDb = new(); - using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - - List ids = new() { 1, 2 }; - await repo.DeleteAsync(ids); - - // Use a separate instance of the context to verify correct data was saved to database - await using BloggingContext context = new(testDb.Options); - Assert.AreEqual(1, context.Blogs.Count()); - } } } \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj index 87daa7f..5b198f3 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Dime.Repositories.Sql.EntityFramework.Tests.csproj @@ -7,7 +7,6 @@ - diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetAsyncTests.cs new file mode 100644 index 0000000..f6f8e91 --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetAsyncTests.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class GetAsyncTests + { + [TestMethod] + public async Task FindAllAsync_Contains_ShouldFindMatches() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + IEnumerable result = await repo.FindAllAsync(x => x.Url.Contains("cat")); + Assert.AreEqual(2, result.Count()); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs index 3296def..c0d6f25 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/GetTests.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Dime.Repositories.Sql.EntityFramework.Tests { [TestClass] - public partial class RepositoryTests + public partial class GetTests { [TestMethod] public void FindAll_Contains_ShouldFindMatches() @@ -16,14 +15,5 @@ public void FindAll_Contains_ShouldFindMatches() IEnumerable result = repo.FindAll(x => x.Url.Contains("cat")); Assert.AreEqual(2, result.Count()); } - - [TestMethod] - public async Task FindAllAsync_Contains_ShouldFindMatches() - { - using TestDatabase testDb = new(); - using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - IEnumerable result = await repo.FindAllAsync(x => x.Url.Contains("cat")); - Assert.AreEqual(2, result.Count()); - } } } \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs index 2cf0ebf..f866479 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/Helpers/BloggingContext.cs @@ -3,6 +3,7 @@ namespace Dime.Repositories.Sql.EntityFramework.Tests { + public class BloggingContext : DbContext { public BloggingContext() diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/PagedAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/PagedAsyncTests.cs new file mode 100644 index 0000000..ce50974 --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/PagedAsyncTests.cs @@ -0,0 +1,30 @@ +using System.Linq; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class PagedAsyncTests + { + [TestMethod] + public async Task FindAllPagedAsync_All_ShouldFindMatches() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + IPage result = await repo.FindAllPagedAsync(null, null, 1, 2); + Assert.AreEqual(3, result.Total); + Assert.AreEqual(2, result.Data.Count()); + } + + [TestMethod] + public async Task FindAllPagedAsync_Contains_ShouldFindMatches() + { + using TestDatabase testDb = new(); + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + IPage result = await repo.FindAllPagedAsync(x => x.Url.Contains("cat"), null, 1, 1); + Assert.AreEqual(2, result.Total); + Assert.AreEqual(1, result.Data.Count()); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateAsyncTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateAsyncTests.cs new file mode 100644 index 0000000..8ba45e1 --- /dev/null +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateAsyncTests.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Dime.Repositories.Sql.EntityFramework.Tests +{ + [TestClass] + public partial class UpdateAsyncTests + { + [TestMethod] + public async Task UpdateAsync_ByEntity_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Blog blog = await context.Blogs.FindAsync(1); + Assert.IsTrue(blog.Url == "http://sample.com/zebras"); + } + + [TestMethod] + public async Task UpdateAsync_Collection_ShouldUpdateAll() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); + await repo.UpdateAsync(new Blog { BlogId = 2, Url = "http://sample.com/lions" }); + + // Use a separate instance of the context to verify correct data was saved to database + await using BloggingContext context = new(testDb.Options); + Blog blog1 = await context.Blogs.FindAsync(1); + Assert.IsTrue(blog1.Url == "http://sample.com/zebras"); + + Blog blog2 = await context.Blogs.FindAsync(2); + Assert.IsTrue(blog2.Url == "http://sample.com/lions"); + } + } +} \ No newline at end of file diff --git a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs index 155eb54..ced76fb 100644 --- a/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs +++ b/src/test/Dime.Repositories.Sql.EntityFramework.Tests/UpdateTests.cs @@ -3,7 +3,8 @@ namespace Dime.Repositories.Sql.EntityFramework.Tests { - public partial class RepositoryTests + [TestClass] + public partial class UpdateTests { [TestMethod] public void Update_ByEntity_ShouldRemoveOne() @@ -20,17 +21,31 @@ public void Update_ByEntity_ShouldRemoveOne() } [TestMethod] - public async Task UpdateAsync_ByEntity_ShouldRemoveOne() + public void Update_ByEntity_Commit_ShouldRemoveOne() { using TestDatabase testDb = new(); using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); - await repo.UpdateAsync(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }); + repo.Update(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }, true); // Use a separate instance of the context to verify correct data was saved to database - await using BloggingContext context = new(testDb.Options); - Blog blog = await context.Blogs.FindAsync(1); + using BloggingContext context = new(testDb.Options); + Blog blog = context.Blogs.Find(1); Assert.IsTrue(blog.Url == "http://sample.com/zebras"); } + + [TestMethod] + public void Update_ByEntity_DoNotCommit_ShouldRemoveOne() + { + using TestDatabase testDb = new(); + + using IRepository repo = new EfRepository(new BloggingContext(testDb.Options)); + repo.Update(new Blog { BlogId = 1, Url = "http://sample.com/zebras" }, false); + + // Use a separate instance of the context to verify correct data was saved to database + using BloggingContext context = new(testDb.Options); + Blog blog = context.Blogs.Find(1); + Assert.IsTrue(blog.Url == "http://sample.com/cats"); + } } } \ No newline at end of file From 690442ca7480971e899563407ffa06e37b296d86 Mon Sep 17 00:00:00 2001 From: Hendrik Bulens Date: Thu, 7 Dec 2023 10:05:08 +0100 Subject: [PATCH 5/5] Bump and publish 2.0.0.0 --- src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj | 2 +- src/core/Dime.Repositories/Dime.Repositories.csproj | 2 +- .../Dime.Repositories.Sql.EntityFramework.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj index b15e37c..227acf9 100644 --- a/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj +++ b/src/core/Dime.Repositories.Sql/Dime.Repositories.Sql.csproj @@ -5,7 +5,7 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.40 + 2.0.0.0 Dime Software net8.0 Dime.Repositories.Sql diff --git a/src/core/Dime.Repositories/Dime.Repositories.csproj b/src/core/Dime.Repositories/Dime.Repositories.csproj index 9bbd428..00a0a6a 100644 --- a/src/core/Dime.Repositories/Dime.Repositories.csproj +++ b/src/core/Dime.Repositories/Dime.Repositories.csproj @@ -4,7 +4,7 @@ 2.0.0.0 2.0.0.0 Dime Software - 2.0.0.0-alpha.50 + 2.0.0.0 net8.0 Dime.Repositories Dime.Repositories diff --git a/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj index 397cf8e..32bd780 100644 --- a/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj +++ b/src/providers/EntityFramework/Dime.Repositories.Sql.EntityFramework.csproj @@ -24,7 +24,7 @@ Dime.Repositories.Sql.EntityFramework Entity Framework;Repository;SQL https://cdn.dime-software.com/dime-software/logo-shape.png - 2.0.0.0-alpha.49 + 2.0.0.0 Implementation of the repository pattern with Microsoft SQL using Entity Framework Core Copyright © 2023 https://github.com/dimesoftware/repository