66 Commits

Author SHA1 Message Date
Developer 02
e0737299cf Bump version to 1.1.1 in project file
Updated DigitalData.Core.Exceptions.csproj to set <Version>, <AssemblyVersion>, and <FileVersion> to 1.1.1, replacing the previous 1.1.0 values. No other changes were made.
2026-01-21 22:06:35 +01:00
Developer 02
2db99edcda Add constructor to BadRequestException for inner exceptions
Added a new constructor to BadRequestException that accepts both a message and an inner exception, enabling improved exception chaining and more detailed error reporting.
2026-01-21 22:05:16 +01:00
65186b4f47 chore(Infrastructure): bump to 2.6.1 2026-01-12 10:23:26 +01:00
eb3a5b8991 Apply AsNoTracking to raw/interpolated queries in DbRepository
QueryRaw and QueryInterpolated now always return results with AsNoTracking applied, ensuring entities are not tracked by EF Core. Both methods are now virtual, allowing for easier customization in derived classes. This change improves performance and prevents unintended side effects from entity tracking.
2026-01-12 10:02:56 +01:00
908d85c648 refactor(Core.Abstraction.Application): bump to 1.6.0 2025-12-19 10:23:45 +01:00
d0f055e066 Bump version to 2.6.0 in csproj metadata
Updated <Version>, <AssemblyVersion>, and <FileVersion> fields in DigitalData.Core.Infrastructure.csproj from 2.5.2 to 2.6.0 to reflect the new release.
2025-12-19 10:13:25 +01:00
7f9e6155fe Add repository registration to DI setup
Added RegsRepository.InvokeAll(services) to the dependency injection configuration, ensuring repository types are registered alongside entities and DbSet factories.
2025-12-19 10:11:14 +01:00
1e3cba6fdf Refactor DependencyInjection and RepositoryConfiguration classes
Restructure code for improved readability and maintainability. Move RepositoryConfiguration as a nested public class inside DependencyInjection, group related service registration logic, and clarify method organization. No functional changes.
2025-12-19 10:09:04 +01:00
daa36d767d Refactor repository registration to be context-based
Changed RegisterDefaultRepository to register repositories by DbContext only, removing the entity-specific generic parameter. Added a private InvokeAll<T> helper to process queued service registrations. This streamlines repository registration and improves code maintainability.
2025-12-19 10:03:25 +01:00
3021fd36f6 Implement IRepository in DbRepository<TDbContext>
DbRepository<TDbContext> now implements the IRepository interface, ensuring it conforms to the expected repository contract and interface requirements. This change improves consistency and enforces interface adherence across repository implementations.
2025-12-19 10:01:20 +01:00
2c704c1231 Add raw SQL support to repository interfaces and DI
Extended IRepository interfaces and DbRepository implementation to support raw and interpolated SQL query execution (sync/async) and querying. Updated DependencyInjection to allow registration of default repository implementations. Added Microsoft.EntityFrameworkCore.Abstractions as a dependency. Performed minor refactoring and cleanup.
2025-12-19 09:54:56 +01:00
2bf2bb2276 Refactor namespace and add System.Linq import
Removed conditional compilation around the namespace in DbRepository.cs, making it always use standard braces. Added System.Linq using directive to support LINQ operations.
2025-12-19 09:51:22 +01:00
Developer 02
d2302560f1 Add repository registration to DependencyInjection
Added a `RegsRepository` queue to manage repository registrations.
Introduced `RegisterDefaultRepository<TDbContext, TEntity>()` to
enqueue scoped repository registrations. Updated `RegisterAllServices`
to process the `RegsRepository` queue during service registration.
2025-12-19 09:25:58 +01:00
Developer 02
42c0dc7206 Refactor method names for clarity in IRepository/DbRepository
Renamed methods in `IRepository` and `DbRepository` to replace
`Sql` with `Query` for improved clarity and consistency.

- Updated `IRepository` methods:
  - `ExecuteSqlRawAsync` -> `ExecuteQueryRawAsync`
  - `ExecuteSqlInterpolatedAsync` -> `ExecuteQueryInterpolatedAsync`
  - `ExecuteSqlRaw` -> `ExecuteQueryRaw`
  - `ExecuteSqlInterpolated` -> `ExecuteQueryInterpolated`

- Updated `DbRepository` methods:
  - `ExecuteSqlRawAsync` -> `ExecuteQueryRawAsync`
  - `ExecuteSqlInterpolatedAsync` -> `ExecuteQueryInterpolatedAsync`
  - `ExecuteSqlRaw` -> `ExecuteQueryRaw`
  - `ExecuteSqlInterpolated` -> `ExecuteQueryInterpolated`

- Fixed `DbRepository` class declaration by removing the incorrect
  constraint requiring `TDbContext` to implement `IRepository`.
2025-12-19 09:20:18 +01:00
Developer 02
82686db38b Add QueryRaw and QueryInterpolated methods to repository
Added `QueryRaw` and `QueryInterpolated` methods to the
`IRepository` interface for raw and interpolated SQL queries,
conditionally compiled for the `NET` target framework.

Removed the `Sql` method from `DbRepository` and replaced it
with implementations of `QueryRaw` and `QueryInterpolated`
using `Entities.FromSqlRaw` and `Entities.FromSqlInterpolated`.

Updated the `Query` property in `DbRepository` to use
`Entities.AsNoTracking()` for read-only queries.
2025-12-19 09:05:59 +01:00
Developer 02
ce5c59dfc2 Update DbRepository to require IRepository constraint
The `DbRepository` class now enforces that the generic type
parameter `TDbContext` must implement the `IRepository`
interface in addition to inheriting from `DbContext`. This
ensures that `DbRepository` can leverage functionality
provided by the `IRepository` interface, improving type
safety and consistency.
2025-12-19 08:57:18 +01:00
Developer 02
5c3db6886a Add EF Core support and refactor IRepository interface
Updated project dependencies to include `Microsoft.EntityFrameworkCore.Abstractions` for multiple target frameworks (`net462`, `net7.0`, `net8.0`, `net9.0`). Added `Microsoft.Extensions.*` package references to the project file.

Enhanced `IRepository` interface with methods for executing raw and interpolated SQL queries (`ExecuteSqlRawAsync`, `ExecuteSqlInterpolatedAsync`, etc.). Adjusted method declarations to support conditional compilation for `NET` and `NETFRAMEWORK`.

Refactored namespace structure in `IRepository.cs` to simplify and remove unnecessary conditional compilation directives.
2025-12-18 22:09:45 +01:00
Developer 02
144178a504 Add async SQL execution methods to DbRepository
Added `ExecuteSqlRawAsync` and `ExecuteSqlInterpolatedAsync` methods to the `DbRepository` class for asynchronous SQL execution. Updated the `System.Linq` namespace import to support LINQ operations. Simplified conditional compilation directives by consistently enclosing the class declaration in braces `{}` across frameworks. Adjusted closing braces to align with the updated structure.
2025-12-18 21:54:22 +01:00
6717aa37ab Add generic DbRepository for raw SQL execution
Introduced DbRepository<TDbContext> to provide basic database operations, including methods for executing raw SQL commands via ExecuteSqlRaw and ExecuteSqlInterpolated. The class exposes the underlying DbContext for use in derived classes. Existing DbRepository<TDbContext, TEntity> remains unchanged.
2025-12-18 13:44:45 +01:00
bf418e986b Add Sql method to DbRepository and EFCore.Relational refs
Added a Sql method to DbRepository for executing raw SQL queries
via FromSqlRaw. Included Microsoft.EntityFrameworkCore.Relational
package references for all target frameworks to support this
functionality. Cleaned up unused using directives.
2025-12-18 13:21:35 +01:00
9f2a13df6f bump to 2.5.2 2025-11-06 00:25:15 +01:00
0cf6d2690a chore(Infrastructure): bump to 2.5.0 2025-11-05 14:22:22 +01:00
afbbac7b81 core(Abstraction.Application): bump to 1.5.0 2025-11-05 14:21:29 +01:00
d16a4b6bb4 refactor(repository): make UpdateAsync more flexible with Action<TEntity>
- Introduced Action<TEntity>-based UpdateAsync method for more flexible updates
- Updated TDto-based UpdateAsync to delegate to the new method
- Reduced code duplication and Mapper dependency
- Preserved existing Create, Read, and Delete functionality
2025-11-05 14:20:12 +01:00
5567f6731b chore(Core.Abstractions): bump to 4.3.0 2025-10-29 16:25:32 +01:00
fc297d92fa feat(factory): add IServiceScopeFactory property to Factory class
Added ScopeFactory property to the Factory class to expose IServiceScopeFactory through GetRequiredService<IServiceScopeFactory>().
This enables scoped service creation from the factory instance.
2025-10-29 16:08:43 +01:00
50a056d110 feat(Factory): add PostBuildBehavior to control post-build modification behavior in Factory
Introduced PostBuildBehavior enum and BehaveOnPostBuild() method to configure behavior when modifying the service collection after the service provider is built.
Replaced EnsureNotBuilt() with EnsureBuilt() to support configurable handling (throw exception or ignore).
This improves flexibility in scenarios where post-build service modifications should be optionally allowed or silently ignored.
2025-10-29 15:57:36 +01:00
d87f36898b chore(Abstraction.Application): bump to 1.4.0 2025-10-23 10:30:50 +02:00
62e43024a6 chore(Abstractions): bump to 4.2.0 2025-10-23 10:29:41 +02:00
daa3f4d5be feat: add IServiceProvider extension to resolve IRepository<TEntity> 2025-10-23 10:27:57 +02:00
10ff9b9745 refactor(Factory): rename Instance as Shared 2025-10-23 10:19:59 +02:00
ea2340974a refactor(FactoryTests): created unit tests for Factory 2025-10-23 10:18:14 +02:00
fbf9488c55 feat(Factory): prevent service modifications after provider is built
Added an `IsBuilt` property and `EnsureNotBuilt()` helper to `Factory` class
to restrict modifications to the service collection once the service provider
has been built. This ensures immutability and prevents runtime inconsistencies
after initialization.
2025-10-23 09:53:50 +02:00
ddbb70081e fix(factory): return requested service from IServiceProvider in GetService method 2025-10-23 09:49:38 +02:00
c538a8df8c feat(Factory): add Factory class implementing IServiceProvider and IServiceCollection
- Introduced Factory class in DigitalData.Core.Abstractions namespace
- Implements both IServiceProvider and IServiceCollection interfaces
- Provides singleton instance via static readonly Instance property
- Supports lazy initialization of IServiceProvider using Lazy<T>
- Includes full collection management (Add, Remove, Clear, etc.)
- Ensures compatibility with both .NET and .NET Framework via conditional directives
2025-10-23 09:47:18 +02:00
f500d9d974 chore(Abstractions): update package references and add dependency injection packages
- Added Microsoft.Extensions.DependencyInjection and Abstractions for net462
- Added Microsoft.Extensions.DependencyInjection for net7.0, net8.0, and net9.0
- Updated Microsoft.Extensions.* package versions for net8.0 and net9.0 to 9.0.10
2025-10-23 09:27:26 +02:00
f2808d090f refactor(Infrastructure): bump to 2.4.5 2025-10-22 17:42:57 +02:00
0084e6f758 refactor(Abstraction.Application): bump to 1.3.7 2025-10-22 17:37:39 +02:00
90ce4e487c refactor: remove IRepository and DbRepository 2025-10-22 17:34:28 +02:00
1febae72c2 refactor(DbRepositoryFactory): remvoed 2025-10-22 17:26:31 +02:00
3b825d4ea3 refactor(Abstraction.Repository): removed 2025-10-13 13:48:46 +02:00
a06adfdcdf chore: bump Abstraction.Application to v1.3.6 and Infrastructure to v2.4.4 2025-10-13 12:05:03 +02:00
00e5f6c0e9 refactor(Repository): add Query getter metod to Repository calss and interface to be able to create read-only query without expression 2025-10-13 11:55:49 +02:00
1e35692a02 bump to 1.3.5 2025-10-01 15:14:25 +02:00
a6048238d4 refactor(IFactory): add Repository getter method 2025-10-01 15:14:01 +02:00
8708f54996 chore(Abstraction.Application): bump to 1.3.4 2025-10-01 15:09:48 +02:00
568f5990b2 refactor: replace Static service accessor with Factory implementation
- Replaced `Static` class with `Factory` class to unify service collection and provider handling.
- Introduced `Factory.Shared` singleton for centralized access to `IServiceProvider`.
- Updated generic repository access to use `Factory<TEntity>` instead of `Static<TEntity>`.
- Simplified lazy initialization of the service provider.
- Maintains compatibility with .NET Framework and .NET conditional compilation.
2025-10-01 15:08:54 +02:00
Developer 02
b8751379c5 Bump version numbers for core projects
Updated `DigitalData.Core.Abstraction.Application` to version 1.3.3 and `DigitalData.Core.Infrastructure` to version 2.4.3. These changes reflect minor updates, likely including bug fixes and small enhancements.
2025-09-30 21:08:32 +02:00
Developer 02
a38447a36f Refactor for .NET framework compatibility
Added preprocessor directives to conditionally include
code for different .NET frameworks. Adjusted namespace
declarations and organized the `Static` class for
improved clarity and maintainability.
2025-09-30 21:04:32 +02:00
Developer 02
fde0398c89 Bump version to 2.4.2 in project file
Updated the version number, assembly version, and file version from 2.4.1 to 2.4.2 in the DigitalData.Core.Infrastructure.csproj file.
2025-09-30 20:00:36 +02:00
Developer 02
ebf79309d1 Refactor Static class to be generic
Updated the `Static` class in the `DigitalData.Core.Infrastructure` namespace to a generic version `Static<TEntity>`. Removed the original non-generic class and the `GetRepository<TEntity>` method. The new generic class provides a method to retrieve a repository for a specific entity type `TEntity`, enhancing type safety and usability.
2025-09-30 19:58:22 +02:00
Developer 02
5a3cbe8ecf Improve exception message in Static.cs
Updated the exception message in the `Static` class to clarify that services cannot be accessed after the service provider has been created. This change enhances the clarity of the error for developers.
2025-09-30 19:56:58 +02:00
Developer 02
d5a8619b4d Add repository access methods to Static class
This commit introduces two new properties in the `Static` class: `Repository` and `GetRepository<TEntity>()`. The `Repository` property allows retrieval of an `IRepository` instance from the service provider, while `GetRepository<TEntity>()` provides access to a specific `IRepository<TEntity>`. These additions improve the ease of accessing repository instances for data operations.
2025-09-30 19:55:40 +02:00
Developer 02
7689005a14 Add conditional compilation for framework-specific code
Introduce framework-specific handling in `Static.cs` to support both NET and NETFRAMEWORK. Implement lazy initialization for service collection and provider, ensuring proper access and error handling.
2025-09-30 19:48:00 +02:00
Developer 02
05568b1551 Add conditional compilation for .NET Framework support
Updated code to support conditional compilation for .NET Framework and .NET.
Introduced nullable reference types in `DbRepository.cs` and ensured proper initialization of service registration queues in `DependencyInjection.cs`.
Modified `DbRepositoryFactory.cs` and `DbSetFactory.cs` to maintain consistent structure across frameworks.
These changes enhance compatibility and improve type safety.
2025-09-30 18:36:15 +02:00
Developer 02
e0ca11ffc0 Enhance CRUDRepository with conditional compilation
Added preprocessor directive for NET framework support.
Updated using directives to include repository and EF Core
namespaces. Adjusted code structure for improved compatibility.
2025-09-30 18:18:14 +02:00
Developer 02
f1ab8db710 Add support for .NET Framework 4.6.2 in project file
Updated `DigitalData.Core.Infrastructure.csproj` to include .NET Framework version 4.6.2 alongside net7.0, net8.0, and net9.0. Configured framework-specific settings for nullable reference types, implicit usings, and language versions. Added a new `<ItemGroup>` for net462 to reference `Microsoft.EntityFrameworkCore` version 3.1.32.
2025-09-30 18:11:38 +02:00
Developer 02
ca08709d99 Bump version to 1.3.2 in project configuration
Updated the version number, assembly version, and file version in DigitalData.Core.Abstraction.Application.csproj from 1.3.1 to 1.3.2 to reflect the new release of the application.
2025-09-30 17:52:24 +02:00
Developer 02
28e415dee1 Add .NET Framework support in Extensions.cs
Introduce conditional compilation directives for the .NET Framework, including necessary using statements and a modified namespace declaration. Add a closing brace for the class definition to ensure compatibility across different target frameworks.
2025-09-30 17:50:53 +02:00
Developer 02
2cedfbe91b Add conditional compilation for IRepositoryFactory
Updated the namespace declaration in `IRepositoryFactory.cs` to support conditional compilation for .NET and .NET Framework. The `Get<TEntity>()` method now conditionally includes the `public` access modifier based on the target framework. Adjusted the interface structure to ensure proper compilation and organization.
2025-09-30 17:49:26 +02:00
Developer 02
d4d1d2b69f Add conditional compilation for .NET Framework support
Introduce conditional compilation directives in the IRepository interface to support .NET Framework. Update method signatures to conditionally include the `public` access modifier for compatibility across frameworks. Adjust the Entity method to remove the `public` modifier, enhancing flexibility while maintaining existing functionality.
2025-09-30 17:47:24 +02:00
Developer 02
74a625a863 Enhance framework compatibility and code readability
Added preprocessor directives for .NET framework compatibility.
Modified `using` directives to be framework-specific.
Improved code formatting for better readability.
Introduced obsolete attributes for deprecated methods,
recommending `MediatR` as an alternative.
Added XML documentation for clarity and maintainability.
2025-09-30 17:33:51 +02:00
Developer 02
07ab7f0c62 refactor(DataResult): update to execute if NET 2025-09-30 17:03:39 +02:00
Developer 02
e74a740abd refactor(CookieConsentSettings): update to be executed only on NET 2025-09-30 16:57:54 +02:00
Developer 02
dfa3cd1a58 refactor(BaseDto): update to compile only in net 2025-09-30 16:57:05 +02:00
Developer 02
0dd4930f1b refactor(DigitalData.Core.Abstraction.Application): update to support net 462 2025-09-30 16:52:02 +02:00
37 changed files with 1103 additions and 699 deletions

View File

@@ -1,4 +1,5 @@
namespace DigitalData.Core.Abstraction.Application.DTO; #if NET
namespace DigitalData.Core.Abstraction.Application.DTO;
/// <summary> /// <summary>
/// Represents a base Data Transfer Object (DTO) with an identifier. /// Represents a base Data Transfer Object (DTO) with an identifier.
@@ -15,3 +16,4 @@ public record BaseDTO<TId>(TId Id) where TId : notnull
/// <returns>A hash code for the current object, derived from the identifier.</returns> /// <returns>A hash code for the current object, derived from the identifier.</returns>
public override int GetHashCode() => Id.GetHashCode(); public override int GetHashCode() => Id.GetHashCode();
} }
#endif

View File

@@ -1,5 +1,6 @@
namespace DigitalData.Core.Abstraction.Application.DTO #if NET
{ namespace DigitalData.Core.Abstraction.Application.DTO;
/// <summary> /// <summary>
/// Represents settings related to user cookie consent dialogs. Designed to be serialized into JSON format for use with JavaScript frontend libraries, /// Represents settings related to user cookie consent dialogs. Designed to be serialized into JSON format for use with JavaScript frontend libraries,
/// such as the bootstrap-cookie-consent-settings at the GitHub repository: https://github.com/shaack/bootstrap-cookie-consent-settings /// such as the bootstrap-cookie-consent-settings at the GitHub repository: https://github.com/shaack/bootstrap-cookie-consent-settings
@@ -71,4 +72,4 @@
/// </summary> /// </summary>
public List<string>? Categories { get; set; } public List<string>? Categories { get; set; }
} }
} #endif

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Configuration; #if NET
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.Configuration; using System.Configuration;
@@ -32,3 +33,4 @@ public static class DIExtensions
return services; return services;
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; #if NET
using Microsoft.Extensions.Logging;
using System.Text; using System.Text;
namespace DigitalData.Core.Abstraction.Application.DTO; namespace DigitalData.Core.Abstraction.Application.DTO;
@@ -391,3 +392,4 @@ public static class DTOExtensions
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")] [Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public static bool IsWrong(this DataResult<bool> bResult) => !bResult.Data; public static bool IsWrong(this DataResult<bool> bResult) => !bResult.Data;
} }
#endif

View File

@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; #if NET
using System.Text.Json.Serialization;
namespace DigitalData.Core.Abstraction.Application.DTO; namespace DigitalData.Core.Abstraction.Application.DTO;
@@ -26,3 +27,4 @@ public class DataResult<T> : Result
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")] [Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public DataResult<I> ToFail<I>() => Fail<I>().Message(Messages).Notice(Notices); public DataResult<I> ToFail<I>() => Fail<I>().Message(Messages).Notice(Notices);
} }
#endif

View File

@@ -1,4 +1,5 @@
namespace DigitalData.Core.Abstraction.Application.DTO; #if NET
namespace DigitalData.Core.Abstraction.Application.DTO;
/// <summary> /// <summary>
/// Defines flags that indicate specific types of status or conditions in a service operation. /// Defines flags that indicate specific types of status or conditions in a service operation.
@@ -48,3 +49,4 @@ public enum Flag
/// </summary> /// </summary>
NotFound NotFound
} }
#endif

View File

@@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; #if NET
using Microsoft.Extensions.Logging;
namespace DigitalData.Core.Abstraction.Application.DTO; namespace DigitalData.Core.Abstraction.Application.DTO;
@@ -26,3 +27,4 @@ public class Notice
[Obsolete("Use DigitalData.Core.Exceptions and .Middleware")] [Obsolete("Use DigitalData.Core.Exceptions and .Middleware")]
public List<string> Messages { get; init; } = new(); public List<string> Messages { get; init; } = new();
} }
#endif

View File

@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; #if NET
using System.Text.Json.Serialization;
namespace DigitalData.Core.Abstraction.Application.DTO; namespace DigitalData.Core.Abstraction.Application.DTO;
@@ -106,3 +107,4 @@ public class Result
}; };
#pragma warning restore CS8601 // Possible null reference assignment. #pragma warning restore CS8601 // Possible null reference assignment.
} }
#endif

View File

@@ -1,9 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Description>This package defines the abstraction layer for the DigitalData.Core.Application module, providing interfaces and contracts for application services, repositories, and infrastructure components in alignment with Clean Architecture principles.</Description> <Description>This package defines the abstraction layer for the DigitalData.Core.Application module, providing interfaces and contracts for application services, repositories, and infrastructure components in alignment with Clean Architecture principles.</Description>
<PackageId>DigitalData.Core.Abstraction.Application</PackageId> <PackageId>DigitalData.Core.Abstraction.Application</PackageId>
@@ -14,9 +12,9 @@
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackageTags>digital data core application clean architecture abstraction</PackageTags> <PackageTags>digital data core application clean architecture abstraction</PackageTags>
<Version>1.3.1</Version> <Version>1.6.0</Version>
<AssemblyVersion>1.3.1</AssemblyVersion> <AssemblyVersion>1.6.0</AssemblyVersion>
<FileVersion>1.3.1</FileVersion> <FileVersion>1.6.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -26,6 +24,20 @@
</None> </None>
</ItemGroup> </ItemGroup>
<!-- disable for net462 -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<!-- enable for net7 and more -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0' Or '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net9.0'">
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" /> <PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="7.0.16" />
@@ -36,22 +48,32 @@
<PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" /> <PackageReference Include="System.Security.Cryptography.Cng" Version="5.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="AutoMapper" Version="10.1.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="AutoMapper" Version="13.0.1" /> <PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="7.0.20" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" /> <PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="8.0.15" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="AutoMapper" Version="14.0.0" /> <PackageReference Include="AutoMapper" Version="14.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.5" /> <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Abstractions" Version="9.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,5 @@
namespace DigitalData.Core.Abstraction.Application; #if NET
namespace DigitalData.Core.Abstraction.Application;
/// <summary> /// <summary>
/// Provides extension methods for retrieving the value of an 'Id' property from objects. /// Provides extension methods for retrieving the value of an 'Id' property from objects.
@@ -92,3 +93,4 @@ public static class EntityExtensions
return id is not null; return id is not null;
} }
} }
#endif

View File

@@ -1,4 +1,5 @@
namespace DigitalData.Core.Abstraction.Application #if NET
namespace DigitalData.Core.Abstraction.Application
{ {
/// <summary> /// <summary>
/// Implements a simplified CRUD service interface that uses a single Data Transfer Object (DTO) type for all CRUD operations, /// Implements a simplified CRUD service interface that uses a single Data Transfer Object (DTO) type for all CRUD operations,
@@ -19,3 +20,4 @@
{ {
} }
} }
#endif

View File

@@ -1,7 +1,8 @@
using DigitalData.Core.Abstraction.Application.DTO; #if NET
using DigitalData.Core.Abstraction.Application.DTO;
namespace DigitalData.Core.Abstraction.Application;
namespace DigitalData.Core.Abstraction.Application
{
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public interface ICRUDService<TCreateDto, TReadDto, TEntity, TId> : IReadService<TReadDto, TEntity, TId> public interface ICRUDService<TCreateDto, TReadDto, TEntity, TId> : IReadService<TReadDto, TEntity, TId>
where TCreateDto : class where TReadDto : class where TEntity : class where TCreateDto : class where TReadDto : class where TEntity : class
@@ -22,4 +23,4 @@ namespace DigitalData.Core.Abstraction.Application
/// <returns>An Result indicating the outcome of the update operation, with an appropriate message.</returns> /// <returns>An Result indicating the outcome of the update operation, with an appropriate message.</returns>
Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto); Task<Result> UpdateAsync<TUpdateDto>(TUpdateDto updateDto);
} }
} #endif

View File

@@ -1,8 +1,10 @@
using DigitalData.Core.Abstraction.Application.DTO; #if NET
using DigitalData.Core.Abstraction.Application.DTO;
using System.DirectoryServices; using System.DirectoryServices;
namespace DigitalData.Core.Abstraction.Application namespace DigitalData.Core.Abstraction.Application;
{
[Obsolete("Use DigitalData.ActiveDirectory")]
public interface IDirectorySearchService public interface IDirectorySearchService
{ {
public string ServerName { get; } public string ServerName { get; }
@@ -91,4 +93,4 @@ namespace DigitalData.Core.Abstraction.Application
/// <returns>The cached <see cref="DirectoryEntry"/> if found; otherwise, null.</returns> /// <returns>The cached <see cref="DirectoryEntry"/> if found; otherwise, null.</returns>
DirectoryEntry? GetSearchRootCache(string username); DirectoryEntry? GetSearchRootCache(string username);
} }
} #endif

View File

@@ -1,9 +1,10 @@
using Microsoft.IdentityModel.Tokens; #if NET
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt; using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography; using System.Security.Cryptography;
namespace DigitalData.Core.Abstraction.Application namespace DigitalData.Core.Abstraction.Application;
{
/// <summary> /// <summary>
/// Defines the operations for JWT service handling claims of type <typeparamref name="TClaimValue"/>. /// Defines the operations for JWT service handling claims of type <typeparamref name="TClaimValue"/>.
/// </summary> /// </summary>
@@ -38,4 +39,4 @@ namespace DigitalData.Core.Abstraction.Application
/// <returns>A <see cref="JwtSecurityToken"/> if the token is valid; otherwise, null.</returns> /// <returns>A <see cref="JwtSecurityToken"/> if the token is valid; otherwise, null.</returns>
JwtSecurityToken? ReadSecurityToken(string token); JwtSecurityToken? ReadSecurityToken(string token);
} }
} #endif

View File

@@ -1,7 +1,8 @@
using DigitalData.Core.Abstraction.Application.DTO; #if NET
using DigitalData.Core.Abstraction.Application.DTO;
namespace DigitalData.Core.Abstraction.Application;
namespace DigitalData.Core.Abstraction.Application
{
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public interface IReadService<TReadDto, TEntity, TId> public interface IReadService<TReadDto, TEntity, TId>
where TReadDto : class where TEntity : class where TReadDto : class where TEntity : class
@@ -36,4 +37,4 @@ namespace DigitalData.Core.Abstraction.Application
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains a boolean value indicating whether the entity exists.</returns>
Task<bool> HasEntity(TId id); Task<bool> HasEntity(TId id);
} }
} #endif

View File

@@ -1,63 +0,0 @@
using System.Linq.Expressions;
using DigitalData.Core.Abstractions.Interfaces;
namespace DigitalData.Core.Abstraction.Application.Repository;
public static class Extensions
{
#region IRepository
#region Read
public static IEnumerable<TEntity> GetAll<TEntity>(this IRepository repository) where TEntity : IEntity
=> repository.Entity<TEntity>().GetAll();
public static Task<IEnumerable<TEntity>> GetAllAsync<TEntity>(this IRepository repository, CancellationToken cancel = default) where TEntity : IEntity
=> repository.Entity<TEntity>().GetAllAsync(cancel);
public static IQueryable<TEntity> Where<TEntity>(this IRepository repository, Expression<Func<TEntity, bool>> expression)
where TEntity : IEntity
=> repository.Entity<TEntity>().Where(expression);
#endregion
#region Create
public static Task<TEntity> CreateAsync<TEntity>(this IRepository repository, TEntity entity, CancellationToken cancel = default)
where TEntity : IEntity
=> repository.Entity<TEntity>().CreateAsync(entity, cancel);
public static Task<TEntity> CreateAsync<TEntity, TDto>(this IRepository repository, TDto dto, CancellationToken cancel = default)
where TEntity : IEntity
where TDto : IDto<TEntity>
=> repository.Entity<TEntity>().CreateAsync(dto, cancel);
public static Task<IEnumerable<TEntity>> CreateAsync<TEntity>(this IRepository repository, IEnumerable<TEntity> entities, CancellationToken cancel = default)
where TEntity : IEntity
=> repository.Entity<TEntity>().CreateAsync(entities, cancel);
public static Task<IEnumerable<TEntity>> CreateAsync<TEntity, TDto>(this IRepository repository, IEnumerable<TDto> dtos, CancellationToken cancel = default)
where TEntity : IEntity
where TDto : IDto<TEntity>
=> repository.Entity<TEntity>().CreateAsync(dtos, cancel);
#endregion Create
#region Update
public static Task UpdateAsync<TEntity, TDto>(this IRepository repository, TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default)
where TEntity : IEntity
where TDto : IDto<TEntity>
=> repository.Entity<TEntity>().UpdateAsync(dto, expression, cancel);
public static Task UpdateAsync<TEntity, TDto>(this IRepository repository, TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
where TEntity : IEntity
where TDto : IDto<TEntity>
=> repository.Entity<TEntity>().UpdateAsync(dto, query, cancel);
#endregion
#region Delete
public static Task DeleteAsync<TEntity>(this IRepository repository, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default)
where TEntity : IEntity
=> repository.Entity<TEntity>().DeleteAsync(expression, cancel);
public static Task DeleteAsync<TEntity>(this IRepository repository, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
where TEntity : IEntity
=> repository.Entity<TEntity>().DeleteAsync(query, cancel);
#endregion
#endregion
}

View File

@@ -1,4 +1,5 @@
namespace DigitalData.Core.Abstraction.Application.Repository #if NET
namespace DigitalData.Core.Abstraction.Application.Repository
{ {
/// <summary> /// <summary>
/// Defines the contract for CRUD operations on a repository for entities of type TEntity. /// Defines the contract for CRUD operations on a repository for entities of type TEntity.
@@ -61,3 +62,4 @@
Task<int> CountAsync(TId id); Task<int> CountAsync(TId id);
} }
} }
#endif

View File

@@ -1,4 +1,7 @@
namespace DigitalData.Core.Abstraction.Application.Repository #if NETFRAMEWORK
using System.Collections.Generic;
#endif
namespace DigitalData.Core.Abstraction.Application.Repository
{ {
/// <summary> /// <summary>
/// Defines methods for mapping between entities and Data Transfer Objects (DTOs). /// Defines methods for mapping between entities and Data Transfer Objects (DTOs).

View File

@@ -1,50 +1,140 @@
using DigitalData.Core.Abstractions.Interfaces; using System.Linq.Expressions;
using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Query;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
#endif
namespace DigitalData.Core.Abstraction.Application.Repository; namespace DigitalData.Core.Abstraction.Application.Repository
{
public interface IRepository
{
#if NET
public
#endif
Task<int> ExecuteQueryRawAsync([NotParameterized] string sql, IEnumerable<object> parameters, CancellationToken cancel = default);
#if NET
public
#endif
Task<int> ExecuteQueryInterpolatedAsync(FormattableString sql, CancellationToken cancel = default);
#if NET
public
#endif
int ExecuteQueryRaw([NotParameterized] string sql, params object[] parameters);
#if NET
public
#endif
int ExecuteQueryInterpolated(FormattableString sql);
}
public interface IRepository<TEntity> public interface IRepository<TEntity>
{ {
#region Create #region Create
public Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancel = default); #if NET
public
#endif
Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancel = default);
public Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken cancel = default); #if NET
public
#endif
Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken cancel = default);
public Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default); #if NET
public
#endif
Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default);
public Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default); #if NET
public
#endif
Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default);
#endregion Create #endregion Create
#region Read #region Read
public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression); #if NET
public
#endif
IQueryable<TEntity> Query { get; }
public IEnumerable<TEntity> GetAll(); #if NET
public
#endif
IQueryable<TEntity> QueryRaw([NotParameterized] string sql, params object[] parameters);
public Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancel = default); #if NET
public
#endif
IQueryable<TEntity> QueryInterpolated([NotParameterized] FormattableString sql);
#if NET
public
#endif
IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression);
#if NET
public
#endif
IEnumerable<TEntity> GetAll();
#if NET
public
#endif
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancel = default);
#endregion Read #endregion Read
#region Update #region Update
public Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default); #if NET
public
#endif
Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default);
public Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default); #if NET
public
#endif
Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default);
#if NET
public
#endif
Task UpdateAsync(Action<TEntity> modification, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default);
#if NET
public
#endif
Task UpdateAsync(Action<TEntity> modification, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default);
#endregion Update #endregion Update
#region Delete #region Delete
public Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default); #if NET
public
#endif
Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default);
public Task DeleteAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default); #if NET
public
#endif
Task DeleteAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default);
#endregion Delete #endregion Delete
#region Obsolete #region Obsolete
[Obsolete("Use CreateAsync, UpdateAsync or DeleteAsync")] [Obsolete("Use CreateAsync, UpdateAsync or DeleteAsync")]
public IQueryable<TEntity> Read(); #if NET
public
#endif
IQueryable<TEntity> Read();
[Obsolete("Use IRepository<TEntity>.Where")] [Obsolete("Use IRepository<TEntity>.Where")]
public IQueryable<TEntity> ReadOnly(); #if NET
public
#endif
IQueryable<TEntity> ReadOnly();
#endregion #endregion
} }
public interface IRepository
{
public IRepository<TEntity> Entity<TEntity>() where TEntity : IEntity;
} }

View File

@@ -1,6 +0,0 @@
namespace DigitalData.Core.Abstraction.Application.Repository;
public interface IRepositoryFactory
{
public IRepository<TEntity> Get<TEntity>();
}

View File

@@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
#if NETFRAMEWORK
using System;
#endif
namespace DigitalData.Core.Abstraction.Application.Repository
{
public static class ServiceProviderExtensions
{
public static IRepository<TEntity> Repository<TEntity>(this IServiceProvider serviceProvider)
{
return serviceProvider.GetRequiredService<IRepository<TEntity>>();
}
}
}

View File

@@ -16,11 +16,17 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool> <PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<Version>4.1.1</Version> <Version>4.3.0</Version>
<AssemblyVersion>4.1.1</AssemblyVersion> <AssemblyVersion>4.3.0</AssemblyVersion>
<FileVersion>4.1.1</FileVersion> <FileVersion>4.3.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<None Include="..\Assets\core_icon.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>
<!-- disable for net462 --> <!-- disable for net462 -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'"> <PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
@@ -36,26 +42,27 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<None Include="..\..\nuget-package-icons\core_icon.png"> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<Pack>True</Pack> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
<PackagePath>\</PackagePath>
</None>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.5" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,142 @@
using Microsoft.Extensions.DependencyInjection;
using System.Collections;
#if NETFRAMEWORK
using System;
using System.Collections.Generic;
#endif
namespace DigitalData.Core.Abstractions
{
public class Factory : IServiceProvider, IServiceCollection
{
public static readonly Factory Shared = new Factory();
private readonly IServiceCollection _serviceCollection = new ServiceCollection();
private readonly Lazy<IServiceProvider> _lazyServiceProvider;
private bool IsBuilt => _lazyServiceProvider.IsValueCreated;
private PostBuildBehavior _pbBehavior = PostBuildBehavior.ThrowException;
public Factory BehaveOnPostBuild(PostBuildBehavior postBuildBehavior)
{
_pbBehavior = postBuildBehavior;
return this;
}
public Factory()
{
_lazyServiceProvider = new Lazy<IServiceProvider>(() => _serviceCollection.BuildServiceProvider());
}
#region Service Provider
public object
#if NET
?
#endif
GetService(Type serviceType)
{
return _lazyServiceProvider.Value.GetService(serviceType);
}
public IServiceScopeFactory ScopeFactory => this.GetRequiredService<IServiceScopeFactory>();
#endregion
#region Service Collection
public int Count => _serviceCollection.Count;
public bool IsReadOnly => _serviceCollection.IsReadOnly;
public ServiceDescriptor this[int index]
{
get => _serviceCollection[index];
set
{
if (EnsureBuilt())
return;
_serviceCollection[index] = value;
}
}
public int IndexOf(ServiceDescriptor item)
{
return _serviceCollection.IndexOf(item);
}
public void Insert(int index, ServiceDescriptor item)
{
if (EnsureBuilt())
return;
_serviceCollection.Insert(index, item);
}
public void RemoveAt(int index)
{
if (EnsureBuilt())
return;
_serviceCollection.RemoveAt(index);
}
public void Add(ServiceDescriptor item)
{
if (EnsureBuilt())
return;
_serviceCollection.Add(item);
}
public void Clear()
{
if (EnsureBuilt())
return;
_serviceCollection.Clear();
}
public bool Contains(ServiceDescriptor item)
{
return _serviceCollection.Contains(item);
}
public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
{
_serviceCollection.CopyTo(array, arrayIndex);
}
public bool Remove(ServiceDescriptor item)
{
if (EnsureBuilt())
return false;
return _serviceCollection.Remove(item);
}
public IEnumerator<ServiceDescriptor> GetEnumerator()
{
return _serviceCollection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return (_serviceCollection as IEnumerable).GetEnumerator();
}
#endregion
#region Helpers
private bool EnsureBuilt()
{
if (IsBuilt)
{
return _pbBehavior == PostBuildBehavior.ThrowException
? throw new InvalidOperationException("Service provider has already been built. No further service modifications are allowed.")
: true;
}
else
return false;
}
#endregion
}
public enum PostBuildBehavior
{
ThrowException,
Ignore
}
}

View File

@@ -19,4 +19,13 @@ public class BadRequestException : Exception
public BadRequestException(string? message) : base(message) public BadRequestException(string? message) : base(message)
{ {
} }
/// <summary>
/// Initializes a new instance of the <see cref="BadRequestException"/> class with a specified error message and inner exception.
/// </summary>
/// <param name="message">The message that describes the error.</param>
/// <param name="innerException">The exception that caused the current exception.</param>
public BadRequestException(string? message, Exception? innerException) : base(message, innerException)
{
}
} }

View File

@@ -17,9 +17,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool> <PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<Version>1.1.0</Version> <Version>1.1.1</Version>
<AssemblyVersion>1.1.0</AssemblyVersion> <AssemblyVersion>1.1.1</AssemblyVersion>
<FileVersion>1.1.0</FileVersion> <FileVersion>1.1.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,4 +1,5 @@
using DigitalData.Core.Abstraction.Application; #if NET
using DigitalData.Core.Abstraction.Application;
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstraction.Application.Repository;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -111,3 +112,4 @@ namespace DigitalData.Core.Infrastructure
public virtual async Task<int> CountAsync(TId id) => await _dbSet.Where(e => e.GetId().Equals(id)).CountAsync(); public virtual async Task<int> CountAsync(TId id) => await _dbSet.Where(e => e.GetId().Equals(id)).CountAsync();
} }
} }
#endif

View File

@@ -1,11 +1,48 @@
using AutoMapper; using AutoMapper;
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Abstractions.Interfaces;
using DigitalData.Core.Infrastructure.Factory; using DigitalData.Core.Infrastructure.Factory;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions; using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
#if NETFRAMEWORK
using System.Collections.Generic;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
#endif
namespace DigitalData.Core.Infrastructure; namespace DigitalData.Core.Infrastructure
{
public class DbRepository<TDbContext> : IRepository where TDbContext : DbContext
{
protected internal readonly TDbContext Context;
public DbRepository(TDbContext context)
{
Context = context;
}
public Task<int> ExecuteQueryRawAsync([NotParameterized] string sql, IEnumerable<object> parameters, CancellationToken cancel = default)
{
return Context.Database.ExecuteSqlRawAsync(sql, parameters, cancel);
}
public Task<int> ExecuteQueryInterpolatedAsync(FormattableString sql, CancellationToken cancel = default)
{
return Context.Database.ExecuteSqlInterpolatedAsync(sql, cancel);
}
public int ExecuteQueryRaw([NotParameterized] string sql, params object[] parameters)
{
return Context.Database.ExecuteSqlRaw(sql, parameters);
}
public int ExecuteQueryInterpolated(FormattableString sql)
{
return Context.Database.ExecuteSqlInterpolated(sql);
}
}
public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbContext : DbContext where TEntity : class
{ {
@@ -13,9 +50,17 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
protected internal readonly DbSet<TEntity> Entities; protected internal readonly DbSet<TEntity> Entities;
public IMapper? Mapper { get; } public IMapper
#if NET
?
#endif
Mapper { get; }
public DbRepository(TDbContext context, DbSetFactory<TDbContext, TEntity> factory, IMapper? mapper = null) public DbRepository(TDbContext context, DbSetFactory<TDbContext, TEntity> factory, IMapper
#if NET
?
#endif
mapper = null)
{ {
Context = context; Context = context;
Entities = factory.Create(context); Entities = factory.Create(context);
@@ -37,13 +82,28 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
return entities; return entities;
} }
public virtual Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default) => CreateAsync(Mapper!.Map<TEntity>(dto), cancel); public virtual Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancel = default)
=> CreateAsync(Mapper
#if NET
!
#endif
.Map<TEntity>(dto), cancel);
public virtual Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default) public virtual Task<IEnumerable<TEntity>> CreateAsync<TDto>(IEnumerable<TDto> dtos, CancellationToken cancel = default)
=> CreateAsync(Mapper!.Map<IEnumerable<TEntity>>(dtos), cancel); => CreateAsync(Mapper
#if NET
!
#endif
.Map<IEnumerable<TEntity>>(dtos), cancel);
#endregion Create #endregion Create
#region Read #region Read
public virtual IQueryable<TEntity> Query => Entities.AsNoTracking();
public virtual IQueryable<TEntity> QueryRaw([NotParameterized] string sql, params object[] parameters) => Entities.FromSqlRaw(sql, parameters).AsNoTracking();
public virtual IQueryable<TEntity> QueryInterpolated([NotParameterized] FormattableString sql) => Entities.FromSqlInterpolated(sql).AsNoTracking();
public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsNoTracking().Where(expression); public virtual IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> expression) => Entities.AsNoTracking().Where(expression);
public virtual IEnumerable<TEntity> GetAll() => Entities.AsNoTracking().ToList(); public virtual IEnumerable<TEntity> GetAll() => Entities.AsNoTracking().ToList();
@@ -54,18 +114,25 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
#region Update #region Update
public virtual Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => UpdateAsync(dto, q => q.Where(expression), cancel); public virtual Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default) => UpdateAsync(dto, q => q.Where(expression), cancel);
public virtual async Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default) public virtual Task UpdateAsync<TDto>(TDto dto, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
=> UpdateAsync(entity => Mapper
#if NET
!
#endif
.Map(dto, entity), query, cancel);
public virtual async Task UpdateAsync(Action<TEntity> modification, Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel = default)
{ {
var entities = await query(Entities).ToListAsync(cancel); var entities = await query(Entities).ToListAsync(cancel);
for (int i = entities.Count - 1; i >= 0; i--) for (int i = entities.Count - 1; i >= 0; i--)
{ modification.Invoke(entities[i]);
Mapper!.Map(dto, entities[i]);
Entities.Update(entities[i]);
}
await Context.SaveChangesAsync(cancel); await Context.SaveChangesAsync(cancel);
} }
public virtual Task UpdateAsync(Action<TEntity> modification, Expression<Func<TEntity, bool>> expression, CancellationToken cancel = default)
=> UpdateAsync(modification, q => q.Where(expression), cancel);
#endregion Update #endregion Update
#region Delete #region Delete
@@ -92,15 +159,4 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
public virtual IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking(); public virtual IQueryable<TEntity> ReadOnly() => Entities.AsNoTracking();
#endregion #endregion
} }
public class DbRepository : IRepository
{
private readonly IRepositoryFactory _factory;
public DbRepository(IRepositoryFactory factory)
{
_factory = factory;
}
public IRepository<TEntity> Entity<TEntity>() where TEntity : IEntity => _factory.Get<TEntity>();
} }

View File

@@ -4,9 +4,14 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection; using System.Reflection;
#if NETFRAMEWORK
using System.Collections.Generic;
using System;
using System.Linq;
#endif
namespace DigitalData.Core.Infrastructure; namespace DigitalData.Core.Infrastructure
{
public static class DependencyInjection public static class DependencyInjection
{ {
public static IServiceCollection AddDbRepository(this IServiceCollection services, Action<RepositoryConfiguration> options) public static IServiceCollection AddDbRepository(this IServiceCollection services, Action<RepositoryConfiguration> options)
@@ -16,25 +21,22 @@ public static class DependencyInjection
options.Invoke(cfg); options.Invoke(cfg);
cfg.RegisterAllServices(services); cfg.RegisterAllServices(services);
// register db repository
services.AddSingleton<IRepository, DbRepository>();
// register db repository factory
services.AddSingleton<IRepositoryFactory, DbRepositoryFactory>();
return services; return services;
} }
public class RepositoryConfiguration public class RepositoryConfiguration
{ {
// 1. register from assembly // 1. register from assembly
private readonly Queue<Action<IServiceCollection>> RegsFromAssembly = new(); private readonly Queue<Action<IServiceCollection>> RegsFromAssembly = new Queue<Action<IServiceCollection>>();
// 2. register entities (can overwrite) // 2. register entities (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsEntity = new(); private readonly Queue<Action<IServiceCollection>> RegsEntity = new Queue<Action<IServiceCollection>>();
// 3. register db set factories (can overwrite) // 3. register db set factories (can overwrite)
private readonly Queue<Action<IServiceCollection>> RegsDbSetFactory = new(); private readonly Queue<Action<IServiceCollection>> RegsDbSetFactory = new Queue<Action<IServiceCollection>>();
// 4. register repository
private readonly Queue<Action<IServiceCollection>> RegsRepository = new Queue<Action<IServiceCollection>>();
internal void RegisterAllServices(IServiceCollection services) internal void RegisterAllServices(IServiceCollection services)
{ {
@@ -46,6 +48,9 @@ public static class DependencyInjection
// 3. register db set factories (can overwrite) // 3. register db set factories (can overwrite)
RegsDbSetFactory.InvokeAll(services); RegsDbSetFactory.InvokeAll(services);
// 4. register repository
RegsRepository.InvokeAll(services);
} }
internal RepositoryConfiguration() { } internal RepositoryConfiguration() { }
@@ -76,7 +81,11 @@ public static class DependencyInjection
.GetMethod(nameof(AddDbSetFactory), .GetMethod(nameof(AddDbSetFactory),
BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public); BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
var genericMethod = addDbSetFactoryMethod!.MakeGenericMethod(typeof(TDbContext), entityType); var genericMethod = addDbSetFactoryMethod
#if NET
!
#endif
.MakeGenericMethod(typeof(TDbContext), entityType);
genericMethod.Invoke(null, new [] { services, null }); genericMethod.Invoke(null, new [] { services, null });
#endregion DbSetFactory #endregion DbSetFactory
} }
@@ -85,7 +94,11 @@ public static class DependencyInjection
RegsFromAssembly.Enqueue(reg); RegsFromAssembly.Enqueue(reg);
} }
public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>? dbSetFactory = null) public void RegisterEntity<TDbContext, TEntity>(Func<TDbContext, DbSet<TEntity>>
#if NET
?
#endif
dbSetFactory = null)
where TDbContext : DbContext where TDbContext : DbContext
where TEntity : class where TEntity : class
{ {
@@ -101,6 +114,10 @@ public static class DependencyInjection
where TDbContext : DbContext where TDbContext : DbContext
where TEntity : class where TEntity : class
=> RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory)); => RegsDbSetFactory.Enqueue(s => s.AddDbSetFactory(dbSetFactory));
public void RegisterDefaultRepository<TDbContext>()
where TDbContext : DbContext
=> RegsRepository.Enqueue(s => s.AddScoped<IRepository, DbRepository<TDbContext>>());
} }
private static void InvokeAll<T>(this Queue<Action<T>> queue, T services) private static void InvokeAll<T>(this Queue<Action<T>> queue, T services)
@@ -109,12 +126,22 @@ public static class DependencyInjection
queue.Dequeue().Invoke(services); queue.Dequeue().Invoke(services);
} }
internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>? create = null) internal static IServiceCollection AddDbSetFactory<TDbContext, TEntity>(this IServiceCollection services, Func<TDbContext, DbSet<TEntity>>
#if NET
?
#endif
create = null)
where TDbContext : DbContext where TDbContext : DbContext
where TEntity : class where TEntity : class
{ {
#if NET
create ??= ctx => ctx.Set<TEntity>(); create ??= ctx => ctx.Set<TEntity>();
#elif NETFRAMEWORK
if(create is null)
create = ctx => ctx.Set<TEntity>();
#endif
services.AddSingleton(_ => new DbSetFactory<TDbContext, TEntity>(create)); services.AddSingleton(_ => new DbSetFactory<TDbContext, TEntity>(create));
return services; return services;
} }
} }
}

View File

@@ -1,9 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net7.0;net8.0;net9.0</TargetFrameworks> <TargetFrameworks>net462;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>DigitalData.Core.Infrastructure</PackageId> <PackageId>DigitalData.Core.Infrastructure</PackageId>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
@@ -15,9 +13,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<RepositoryType>digital data core abstractions clean architecture</RepositoryType> <RepositoryType>digital data core abstractions clean architecture</RepositoryType>
<PackageTags>digital data core infrastructure clean architecture</PackageTags> <PackageTags>digital data core infrastructure clean architecture</PackageTags>
<Version>2.4.1</Version> <Version>2.6.1</Version>
<AssemblyVersion>2.4.1</AssemblyVersion> <AssemblyVersion>2.6.1</AssemblyVersion>
<FileVersion>2.4.1</FileVersion> <FileVersion>2.6.1</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -27,16 +25,38 @@
</None> </None>
</ItemGroup> </ItemGroup>
<!-- disable for net462 -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net462'">
<Nullable>disable</Nullable>
<ImplicitUsings>disable</ImplicitUsings>
<LangVersion>7.3</LangVersion>
</PropertyGroup>
<!-- enable for net7 and more -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net7.0' Or '$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net9.0'">
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net462'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.32" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.20" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.20" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.15" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" /> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,16 +0,0 @@
using DigitalData.Core.Abstraction.Application.Repository;
using Microsoft.Extensions.DependencyInjection;
namespace DigitalData.Core.Infrastructure.Factory;
public class DbRepositoryFactory : IRepositoryFactory
{
private readonly IServiceProvider _provider;
public DbRepositoryFactory(IServiceProvider provider)
{
_provider = provider;
}
public IRepository<TEntity> Get<TEntity>() => _provider.GetRequiredService<IRepository<TEntity>>();
}

View File

@@ -1,6 +1,14 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
#if NETFRAMEWORK
using System;
#endif
namespace DigitalData.Core.Infrastructure.Factory; namespace DigitalData.Core.Infrastructure.Factory
#if NET
;
#elif NETFRAMEWORK
{
#endif
public class DbSetFactory<TDbContext,TEntity> where TDbContext : DbContext where TEntity : class public class DbSetFactory<TDbContext,TEntity> where TDbContext : DbContext where TEntity : class
{ {
@@ -11,3 +19,7 @@ public class DbSetFactory<TDbContext,TEntity> where TDbContext : DbContext where
Create = create; Create = create;
} }
} }
#if NETFRAMEWORK
}
#endif

View File

@@ -0,0 +1,173 @@
using Microsoft.Extensions.DependencyInjection;
using DigitalData.Core.Abstractions;
namespace DigitalData.Core.Tests.Abstractions
{
[TestFixture]
public class FactoryTests
{
private Factory _factory;
[SetUp]
public void Setup()
{
_factory = new Factory();
}
[Test]
public void Add_ServiceDescriptor_ShouldIncreaseCount()
{
// Arrange
var descriptor = ServiceDescriptor.Singleton(typeof(string), "test");
// Act
_factory.Add(descriptor);
// Assert
Assert.That(_factory.Count, Is.EqualTo(1));
Assert.That(_factory.Contains(descriptor), Is.True);
}
[Test]
public void Clear_ShouldRemoveAllServices()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "test"));
_factory.Add(ServiceDescriptor.Singleton(typeof(int), 42));
// Act
_factory.Clear();
// Assert
Assert.That(_factory.Count, Is.EqualTo(0));
}
[Test]
public void GetService_ShouldReturnRegisteredInstance()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "Hello World"));
// Act
var result = _factory.GetService(typeof(string));
// Assert
Assert.That(result, Is.EqualTo("Hello World"));
}
[Test]
public void GetService_UnregisteredType_ShouldReturnNull()
{
// Act
var result = _factory.GetService(typeof(int));
// Assert
Assert.That(result, Is.Null);
}
[Test]
public void ModifyAfterBuild_ShouldThrowInvalidOperationException()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
var _ = _factory.GetService(typeof(string)); // trigger build
// Act & Assert
Assert.Throws<InvalidOperationException>(() => _factory.Add(ServiceDescriptor.Singleton(typeof(int), 1)));
}
[Test]
public void Remove_ShouldWorkBeforeBuild()
{
// Arrange
var descriptor = ServiceDescriptor.Singleton(typeof(string), "Test");
_factory.Add(descriptor);
// Act
var removed = _factory.Remove(descriptor);
// Assert
Assert.That(removed, Is.True);
Assert.That(_factory.Count, Is.EqualTo(0));
}
[Test]
public void Insert_ShouldInsertAtCorrectIndex()
{
// Arrange
var d1 = ServiceDescriptor.Singleton(typeof(string), "A");
var d2 = ServiceDescriptor.Singleton(typeof(int), 1);
_factory.Add(d1);
_factory.Insert(0, d2);
// Act
var first = _factory[0];
// Assert
Assert.That(first.ServiceType, Is.EqualTo(typeof(int)));
}
[Test]
public void Enumerator_ShouldIterateOverServices()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "a"));
_factory.Add(ServiceDescriptor.Singleton(typeof(int), 5));
// Act
var list = _factory.ToList();
// Assert
Assert.That(list.Count, Is.EqualTo(2));
Assert.That(list.Any(sd => sd.ServiceType == typeof(string)), Is.True);
Assert.That(list.Any(sd => sd.ServiceType == typeof(int)), Is.True);
}
[Test]
public void CopyTo_ShouldCopyElements()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
_factory.Add(ServiceDescriptor.Singleton(typeof(int), 10));
var array = new ServiceDescriptor[2];
// Act
_factory.CopyTo(array, 0);
// Assert
Assert.That(array[0].ServiceType, Is.EqualTo(typeof(string)));
Assert.That(array[1].ServiceType, Is.EqualTo(typeof(int)));
}
[Test]
public void RemoveAt_ShouldRemoveItemAtIndex()
{
// Arrange
_factory.Add(ServiceDescriptor.Singleton(typeof(string), "x"));
_factory.Add(ServiceDescriptor.Singleton(typeof(int), 5));
// Act
_factory.RemoveAt(0);
// Assert
Assert.That(_factory.Count, Is.EqualTo(1));
Assert.That(_factory[0].ServiceType, Is.EqualTo(typeof(int)));
}
[Test]
public void Indexer_Set_ShouldReplaceItem()
{
// Arrange
var original = ServiceDescriptor.Singleton(typeof(string), "A");
var replacement = ServiceDescriptor.Singleton(typeof(string), "B");
_factory.Add(original);
// Act
_factory[0] = replacement;
// Assert
Assert.That(_factory[0], Is.EqualTo(replacement));
}
}
}

View File

@@ -1,115 +0,0 @@
using DigitalData.Core.Tests.Mock;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Reflection;
using DigitalData.Core.Abstraction.Application.Repository;
using DigitalData.Core.Infrastructure;
namespace DigitalData.Core.Tests.Infrastructure;
public class DbRepositoryTests
{
private IHost _host;
private IRepository Repo;
[SetUp]
public void Setup()
{
var builder = Host.CreateApplicationBuilder();
builder.Services.AddDbContext<MockDbContext>(opt => opt.UseInMemoryDatabase("MockDB"));
builder.Services.AddDbRepository(opt =>
{
opt.RegisterFromAssembly<MockDbContext>(typeof(User).Assembly);
});
builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly());
_host = builder.Build();
Repo = _host.Services.GetRequiredService<IRepository>();
}
[TearDown]
public void TearDown()
{
if (_host is IDisposable disposableHost)
disposableHost.Dispose();
}
[TestCase(true, TestName = "WhenGivenMultipleUsers")]
[TestCase(false, TestName = "WhenGivenSingleUser")]
public void CreateAsync_ShouldNotThrow(bool multiple)
{
// Arrange
var faker = Fake.CreateUserFaker();
// Act & Assert
if (multiple)
Assert.DoesNotThrowAsync(async () => await Repo.CreateAsync(faker.Generate(Random.Shared.Next(1, 10))));
else
Assert.DoesNotThrowAsync(async () => await Repo.CreateAsync(faker.Generate()));
}
[TestCase(true, TestName = "WhenDtoUsed")]
[TestCase(false, TestName = "WhenEntityUsed")]
public async Task ReadAsync_ShouldReturnCreated(bool useDto)
{
// Act
var createdUser = useDto
? await Repo.CreateAsync<User, UserCreateDto>(Fake.UserCreateDto)
: await Repo.CreateAsync(Fake.User);
var readUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>
{
Assert.That(readUser, Is.Not.Null);
Assert.That(readUser, Is.EqualTo(createdUser));
});
}
[Test]
public async Task ReadAsync_ShouldReturnUpdated()
{
// Arrange
var createdUser = await Repo.CreateAsync<User, UserCreateDto>(Fake.UserCreateDto);
var userUpdateDto = new UserUpdateDto() { Age = 10, Email = "Bar", FirstName = "Foo" };
// Act
await Repo.UpdateAsync<User, UserUpdateDto>(userUpdateDto, u => u.Id == createdUser!.Id);
var upToDateUser = await Repo.Where<User>(u => u.Id == createdUser!.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>
{
Assert.That(upToDateUser, Is.Not.Null);
Assert.That(upToDateUser?.FirstName, Is.EqualTo(userUpdateDto.FirstName));
Assert.That(upToDateUser?.Email, Is.EqualTo(userUpdateDto.Email));
Assert.That(upToDateUser?.Age, Is.EqualTo(userUpdateDto.Age));
});
}
[Test]
public async Task ReadAsync_ShouldNotReturnDeleted()
{
// Arrange
var createdUser = await Repo.CreateAsync<User, UserCreateDto>(Fake.UserCreateDto);
var readUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Act
await Repo.DeleteAsync<User>(u => u.Id == createdUser.Id);
var deletedUser = await Repo.Where<User>(u => u.Id == createdUser.Id).FirstOrDefaultAsync();
// Assert
Assert.Multiple(() =>
{
Assert.That(readUser, Is.Not.Null);
Assert.That(deletedUser, Is.Null);
});
}
}

View File

@@ -1,4 +1,4 @@
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstractions.Interfaces;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
namespace DigitalData.Core.Tests.Mock; namespace DigitalData.Core.Tests.Mock;

View File

@@ -1,4 +1,4 @@
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstractions.Interfaces;
namespace DigitalData.Core.Tests.Mock; namespace DigitalData.Core.Tests.Mock;

View File

@@ -1,4 +1,4 @@
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstractions.Interfaces;
namespace DigitalData.Core.Tests.Mock; namespace DigitalData.Core.Tests.Mock;

View File

@@ -1,4 +1,4 @@
using DigitalData.Core.Abstraction.Application.Repository; using DigitalData.Core.Abstractions.Interfaces;
namespace DigitalData.Core.Tests.Mock; namespace DigitalData.Core.Tests.Mock;