diff --git a/.vs/KSFramework/xs/UserPrefs.xml b/.vs/KSFramework/xs/UserPrefs.xml index 140fba5..e04c12d 100644 --- a/.vs/KSFramework/xs/UserPrefs.xml +++ b/.vs/KSFramework/xs/UserPrefs.xml @@ -1,27 +1,15 @@  - - - - + - - - - - - - - - - + - + diff --git a/.vs/KSFramework/xs/project-cache/KSFramework-Debug.json b/.vs/KSFramework/xs/project-cache/KSFramework-Debug.json index 53cd802..2e19ab1 100644 --- a/.vs/KSFramework/xs/project-cache/KSFramework-Debug.json +++ b/.vs/KSFramework/xs/project-cache/KSFramework-Debug.json @@ -1 +1 @@ -{"Format":1,"ProjectReferences":[],"MetadataReferences":[{"FilePath":"/Users/sadin/.nuget/packages/azure.core/1.38.0/lib/net6.0/Azure.Core.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/azure.identity/1.11.4/lib/netstandard2.0/Azure.Identity.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/fluentvalidation/12.0.0/lib/net8.0/FluentValidation.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/mediatr.contracts/2.0.1/lib/netstandard2.0/MediatR.Contracts.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/mediatr/12.5.0/lib/net6.0/MediatR.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.antiforgery/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.authentication.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.authentication.core/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.authorization/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.authorization.policy/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.cors/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.cryptography.internal/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.dataprotection.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.dataprotection/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.diagnostics.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.hosting.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.hosting.server.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.html.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.http.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.http/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Http.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.http.extensions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.http.features/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.jsonpatch/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.localization/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.apiexplorer/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.core/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.cors/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.dataannotations/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.formatters.json/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.localization/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.razor/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.razor.extensions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.razorpages/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.taghelpers/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.mvc.viewfeatures/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.razor/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.razor.language/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.razor.runtime/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.responsecaching.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.routing.abstractions/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.routing/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.aspnetcore.webutilities/2.3.0/lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.bcl.asyncinterfaces/1.1.1/ref/netstandard2.1/Microsoft.Bcl.AsyncInterfaces.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.codeanalysis.csharp/2.8.2/lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.codeanalysis.common/2.8.2/lib/netstandard1.3/Microsoft.CodeAnalysis.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.codeanalysis.razor/2.3.0/lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.data.sqlclient/5.1.6/ref/net6.0/Microsoft.Data.SqlClient.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.dotnet.platformabstractions/2.1.0/lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.abstractions/9.0.5/lib/net8.0/Microsoft.EntityFrameworkCore.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore/9.0.5/lib/net8.0/Microsoft.EntityFrameworkCore.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.relational.design/1.1.6/lib/netstandard1.3/Microsoft.EntityFrameworkCore.Relational.Design.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.relational/9.0.5/lib/net8.0/Microsoft.EntityFrameworkCore.Relational.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.sqlserver.design/1.1.6/lib/netstandard1.3/Microsoft.EntityFrameworkCore.SqlServer.Design.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.sqlserver/9.0.5/lib/net8.0/Microsoft.EntityFrameworkCore.SqlServer.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.caching.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.Caching.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.caching.memory/9.0.5/lib/net9.0/Microsoft.Extensions.Caching.Memory.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.binder/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.Binder.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.commandline/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.CommandLine.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.environmentvariables/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.fileextensions/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.FileExtensions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.json/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.Json.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.configuration.usersecrets/9.0.5/lib/net9.0/Microsoft.Extensions.Configuration.UserSecrets.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.dependencyinjection.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.dependencyinjection/9.0.5/lib/net9.0/Microsoft.Extensions.DependencyInjection.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.dependencymodel/2.1.0/lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.diagnostics.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.Diagnostics.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.diagnostics/9.0.5/lib/net9.0/Microsoft.Extensions.Diagnostics.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.fileproviders.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.FileProviders.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.fileproviders.composite/8.0.0/lib/net8.0/Microsoft.Extensions.FileProviders.Composite.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.fileproviders.physical/9.0.5/lib/net9.0/Microsoft.Extensions.FileProviders.Physical.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.filesystemglobbing/9.0.5/lib/net9.0/Microsoft.Extensions.FileSystemGlobbing.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.hosting.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.Hosting.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.hosting/9.0.5/lib/net9.0/Microsoft.Extensions.Hosting.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.localization.abstractions/8.0.11/lib/net8.0/Microsoft.Extensions.Localization.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.localization/8.0.11/lib/net8.0/Microsoft.Extensions.Localization.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.abstractions/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.configuration/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.Configuration.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.console/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.Console.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.debug/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.Debug.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.eventlog/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.EventLog.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.logging.eventsource/9.0.5/lib/net9.0/Microsoft.Extensions.Logging.EventSource.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.objectpool/8.0.11/lib/net8.0/Microsoft.Extensions.ObjectPool.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.options.configurationextensions/9.0.5/lib/net9.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.options/9.0.5/lib/net9.0/Microsoft.Extensions.Options.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.primitives/9.0.5/lib/net9.0/Microsoft.Extensions.Primitives.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.extensions.webencoders/8.0.11/lib/net8.0/Microsoft.Extensions.WebEncoders.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identity.client/4.61.3/lib/net6.0/Microsoft.Identity.Client.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identity.client.extensions.msal/4.61.3/lib/net6.0/Microsoft.Identity.Client.Extensions.Msal.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.abstractions/6.35.0/lib/net6.0/Microsoft.IdentityModel.Abstractions.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.jsonwebtokens/6.35.0/lib/net6.0/Microsoft.IdentityModel.JsonWebTokens.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.logging/6.35.0/lib/net6.0/Microsoft.IdentityModel.Logging.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.protocols/6.35.0/lib/net6.0/Microsoft.IdentityModel.Protocols.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.protocols.openidconnect/6.35.0/lib/net6.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.identitymodel.tokens/6.35.0/lib/net6.0/Microsoft.IdentityModel.Tokens.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.net.http.headers/2.3.0/lib/netstandard2.0/Microsoft.Net.Http.Headers.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/microsoft.sqlserver.server/1.0.0/lib/netstandard2.0/Microsoft.SqlServer.Server.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/newtonsoft.json.bson/1.0.2/lib/netstandard2.0/Newtonsoft.Json.Bson.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/newtonsoft.json/13.0.3/lib/net6.0/Newtonsoft.Json.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/pluralize.net/1.0.2/lib/netstandard2.0/Pluralize.NET.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.clientmodel/1.0.0/lib/net6.0/System.ClientModel.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.componentmodel.annotations/5.0.0/ref/netstandard2.1/System.ComponentModel.Annotations.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.diagnostics.eventlog/9.0.5/lib/net9.0/System.Diagnostics.EventLog.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.identitymodel.tokens.jwt/6.35.0/lib/net6.0/System.IdentityModel.Tokens.Jwt.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.memory.data/1.0.2/lib/netstandard2.0/System.Memory.Data.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.security.cryptography.pkcs/8.0.1/lib/net8.0/System.Security.Cryptography.Pkcs.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.security.cryptography.protecteddata/6.0.0/lib/net6.0/System.Security.Cryptography.ProtectedData.dll","Aliases":[],"Framework":null},{"FilePath":"/Users/sadin/.nuget/packages/system.security.cryptography.xml/8.0.2/lib/net8.0/System.Security.Cryptography.Xml.dll","Aliases":[],"Framework":null}],"Files":["/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Api/ApiResult.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/AggregatesHelper/IAggregateRoot.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/AggregatesHelper/ValueObject.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/BaseEntity.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/DomainServices/DomainService.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/DomainServices/IDomainService.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/Enumeration.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/IDomainEvent.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/IRepository.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/IUnitOfWork.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/AndNotSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/AndSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/AnySpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/CompositeSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ExpressionFuncExtender.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ExpressionSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ICompositeSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ISpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ISpecificationParser.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/NoneSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/NotSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/OrSpecification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/ParameterRebinder.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/Specification.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Domain/SpecificationsHelpers/SpecificationExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Enums/ApiResultStatusCode.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSArgumentNullException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSBadRequestException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSNotFoundException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSServerErrorException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSUnauthorizedAccessException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Exceptions/KSValidationException.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/GenericRepository/IRepository.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/GenericRepository/Repository.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Pagination/Paginated.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Pagination/PaginatedList.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Pagination/QueryableExtension.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/BaseResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/BadRequestResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/FailedResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/ForbiddenAccessResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/FormValidationResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/NotFoundResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/ServerErrorResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/FailedResponses/UnauthorizedAccessResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/AcceptedResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/CreatedResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/NoContentResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/NonAuthoritativeInformationResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/OkResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/PartialContentResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Responses/SuccessResponses/ResetContentResponse.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/ResutlMessage.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/TagHelpers/ActiveRouteTagHelper.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/TagHelpers/CheckMarkTagHelper.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/Assert.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/DateTimeUtilityExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/EnumExcentions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/IdentityExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/LinqExtentions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/ModelBuilderExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/RandomGenerator.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/SecurityHelper.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/SnakeCasePropertyNamingPolicy.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/StringExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/Utilities/ValidationExtensions.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.GlobalUsings.g.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/.NETCoreApp,Version=v10.0.AssemblyAttributes.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.AssemblyInfo.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.AssemblyInfo.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.AssemblyInfo.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.AssemblyInfo.cs"],"BuildActions":["Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile","Compile"],"Analyzers":["/usr/local/share/dotnet/sdk/7.0.304/Sdks/Microsoft.NET.Sdk/analyzers/Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll","/usr/local/share/dotnet/sdk/7.0.304/Sdks/Microsoft.NET.Sdk/analyzers/Microsoft.CodeAnalysis.NetAnalyzers.dll","/Users/sadin/.nuget/packages/microsoft.codeanalysis.analyzers/1.1.0/analyzers/dotnet/cs/Microsoft.CodeAnalysis.Analyzers.dll","/Users/sadin/.nuget/packages/microsoft.codeanalysis.analyzers/1.1.0/analyzers/dotnet/cs/Microsoft.CodeAnalysis.CSharp.Analyzers.dll","/Users/sadin/.nuget/packages/microsoft.entityframeworkcore.analyzers/9.0.5/analyzers/dotnet/cs/Microsoft.EntityFrameworkCore.Analyzers.dll","/Users/sadin/.nuget/packages/microsoft.extensions.logging.abstractions/9.0.5/analyzers/dotnet/roslyn4.4/cs/Microsoft.Extensions.Logging.Generators.dll","/Users/sadin/.nuget/packages/microsoft.extensions.options/9.0.5/analyzers/dotnet/roslyn4.4/cs/Microsoft.Extensions.Options.SourceGeneration.dll"],"AdditionalFiles":[],"EditorConfigFiles":["/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.GeneratedMSBuildEditorConfig.editorconfig"],"DefineConstants":["TRACE","DEBUG","NET","NET10_0","NETCOREAPP","NET5_0_OR_GREATER","NET6_0_OR_GREATER","NET7_0_OR_GREATER","NETCOREAPP1_0_OR_GREATER","NETCOREAPP1_1_OR_GREATER","NETCOREAPP2_0_OR_GREATER","NETCOREAPP2_1_OR_GREATER","NETCOREAPP2_2_OR_GREATER","NETCOREAPP3_0_OR_GREATER","NETCOREAPP3_1_OR_GREATER"],"IntermediateAssembly":"/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net10.0/KSFramework.dll"} \ No newline at end of file +{"Format":1,"ProjectReferences":[],"MetadataReferences":[],"Files":["/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net8.0/KSFramework.AssemblyInfo.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net8.0/KSFramework.AssemblyInfo.cs","/Users/sadin/Projects/Personal Projects/NugetPackages/KSFramework/src/KSFramework/obj/Debug/net8.0/KSFramework.AssemblyInfo.cs"],"BuildActions":["Compile","Compile","Compile"],"Analyzers":[],"AdditionalFiles":[],"EditorConfigFiles":[],"DefineConstants":[],"IntermediateAssembly":""} \ No newline at end of file diff --git a/KSFramework.sln b/KSFramework.sln index 4a7b249..a31a944 100644 --- a/KSFramework.sln +++ b/KSFramework.sln @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "KSFramework", "KSFramework" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KSFramework.UnitTests", "tests\KSFramework\KSFramework.UnitTests\KSFramework.UnitTests.csproj", "{48D1B7B2-D99B-4554-BBFB-95E75720B8E5}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KSFramework.IntegrationTests", "tests\KSFramework\KSFramework.IntegrationTests\KSFramework.IntegrationTests.csproj", "{C990672B-5ECD-4123-AF33-1F5CE23318E9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -65,6 +67,18 @@ Global {48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x64.Build.0 = Release|Any CPU {48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x86.ActiveCfg = Release|Any CPU {48D1B7B2-D99B-4554-BBFB-95E75720B8E5}.Release|x86.Build.0 = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|x64.Build.0 = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|x86.ActiveCfg = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Debug|x86.Build.0 = Debug|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|Any CPU.Build.0 = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|x64.ActiveCfg = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|x64.Build.0 = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|x86.ActiveCfg = Release|Any CPU + {C990672B-5ECD-4123-AF33-1F5CE23318E9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -75,5 +89,6 @@ Global {D849CD75-73AE-4F1A-80EA-0FC596C80723} = {B36C9166-6687-1700-0084-D88BD073518A} {6F746C7E-99E8-D040-B792-CDA2514E83CE} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {48D1B7B2-D99B-4554-BBFB-95E75720B8E5} = {6F746C7E-99E8-D040-B792-CDA2514E83CE} + {C990672B-5ECD-4123-AF33-1F5CE23318E9} = {6F746C7E-99E8-D040-B792-CDA2514E83CE} EndGlobalSection EndGlobal diff --git a/README.md b/README.md index a47e7cd..3647e97 100644 --- a/README.md +++ b/README.md @@ -1,226 +1,289 @@ -# KSFramework 🧩 +# KSFramework -**KSFramework** is a clean, extensible, and testable foundation for building scalable .NET Core applications. It provides a custom implementation of the MediatR pattern, along with well-known enterprise patterns such as Repository, Unit of Work, and Specification. It is designed to be modular, testable, and ready for production use. +**KSFramework** is a lightweight, extensible .NET framework designed to accelerate clean architecture and domain-driven development. It offers generic repository support, pagination utilities, and helpful abstractions to simplify the implementation of enterprise-grade backend systems using modern .NET patterns. --- ## ✨ Features -- ✅ Custom Mediator pattern implementation (Send / Publish / Stream) -- ✅ Pipeline behaviors (Validation, Logging, Exception handling, Pre/Post-processors) -- ✅ FluentValidation integration -- ✅ Notification pipeline behaviors -- ✅ Repository Pattern -- ✅ Unit of Work Pattern -- ✅ Specification Pattern -- ✅ Scrutor-based automatic registration -- ✅ File-scoped namespaces and XML documentation for every component -- ✅ Full unit test coverage using xUnit and Moq +- 🧱 **Domain-Driven Design (DDD) Friendly** +- 🧼 **Clean Architecture Support** +- 🧰 **Generic Repository Pattern** +- 📄 **Built-in Pagination Support** +- 🧪 **Unit Testable Core Interfaces** +- 🧩 **Customizable and Extensible by Design** --- ## 📦 Installation -Add the package reference (once published): +Install via NuGet: ```bash -dotnet add package KSFramework.Messaging -dotnet add package KSFramework.Data +dotnet add package KSFramework ``` -Or reference the source projects directly in your solution. -⸻ +Or via the Package Manager Console: -🧠 Project Structure +```powershell +Install-Package KSFramework ``` -src/ -KSFramework.Messaging/ → Custom mediator, behaviors, contracts -KSFramework.Data/ → Repository, UnitOfWork, Specification -KSFramework.SharedKernel/ → Domain base types, entities, value objects -tests/ -KSFramework.UnitTests/ → xUnit unit tests +--- + +## 📂 Project Structure + +The KSFramework package is modular and consists of: + +- `KSFramework.GenericRepository` — Contains generic repository interfaces and base implementations. +- `KSFramework.Pagination` — Provides interfaces and classes for paginated queries. +- `KSFramework.KSDomain` — Base types for entities, aggregate roots, and value objects. + +--- -samples/ -MediatorSampleApp/ → Console app to demonstrate usage -``` +## 🚀 Getting Started -### 🚀 Mediator Usage +### 1. Define Your Entities -### 1. Define a request ```csharp -public class MultiplyByTwoRequest : IRequest +public class BlogPost { - public int Input { get; } - public MultiplyByTwoRequest(int input) => Input = input; + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + public DateTime PublishedAt { get; set; } } -``` +``` + +### 2. Setup Your DbContext -### 2. Create a handler ```csharp -public class MultiplyByTwoHandler : IRequestHandler +public class AppDbContext : DbContext { - public Task Handle(MultiplyByTwoRequest request, CancellationToken cancellationToken) - => Task.FromResult(request.Input * 2); + public DbSet BlogPosts { get; set; } + + public AppDbContext(DbContextOptions options) : base(options) { } } -``` +``` -### 3. Send the request -```csharp -var result = await mediator.Send(new MultiplyByTwoRequest(5)); -Console.WriteLine(result); // Output: 10 -``` +### 3. Create Your Repository -### 📤 Notifications +You can use the generic repository directly: -### Define a notification and handler ```csharp -public class UserRegisteredNotification : INotification +public class BlogPostRepository : Repository { - public string Username { get; } - public UserRegisteredNotification(string username) => Username = username; + public BlogPostRepository(AppDbContext context) : base(context) { } } +``` -public class SendWelcomeEmailHandler : INotificationHandler -{ - public Task Handle(UserRegisteredNotification notification, CancellationToken cancellationToken) - { - Console.WriteLine($"Welcome email sent to {notification.Username}"); - return Task.CompletedTask; - } -} -``` +Or inject `IRepository` if you're using dependency injection with Scrutor or your own DI container. + +--- + +## 🧪 Common Repository Operations + +```csharp +await _repository.AddAsync(new BlogPost { Title = "Welcome", Content = "This is the first post." }); + +var post = await _repository.GetByIdAsync(1); + +await _repository.UpdateAsync(post); + +await _repository.DeleteAsync(post); + +var allPosts = await _repository.GetAllAsync(); + +var filtered = _repository.Find(p => p.PublishedAt > DateTime.UtcNow.AddDays(-30)); +``` + +--- + +## 📘 Pagination Example -### Publish the notification ```csharp -await mediator.Publish(new UserRegisteredNotification("john")); -``` +var query = _repository.Query(); +var paginated = await query.PaginateAsync(pageNumber: 1, pageSize: 10); +``` + +`PaginateAsync` is an extension method provided by the `KSFramework.Pagination` namespace. + +--- + +## 📚 Full Example: Building a Blog with Weekly Newsletter + +### Project Overview -### 🔁 Streaming +This sample demonstrates how to build a simple blog platform using `KSFramework` with: + +- Post publishing +- Subscriber registration +- Weekly newsletter delivery to subscribers + +--- + +### Step 1: Define Entities -### Define a stream request and handler ```csharp -public class CounterStreamRequest : IStreamRequest +public class BlogPost { - public int Count { get; init; } + public int Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + public DateTime PublishedAt { get; set; } } -public class CounterStreamHandler : IStreamRequestHandler +public class Subscriber { - public async IAsyncEnumerable Handle(CounterStreamRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) - { - for (int i = 1; i <= request.Count; i++) - { - yield return i; - await Task.Delay(10, cancellationToken); - } - } + public int Id { get; set; } + public string Email { get; set; } } -``` +``` + +--- + +### Step 2: Repositories -### Consume the stream ```csharp -await foreach (var number in mediator.CreateStream(new CounterStreamRequest { Count = 3 })) +public class BlogPostRepository : Repository { - Console.WriteLine($"Streamed: {number}"); + public BlogPostRepository(AppDbContext context) : base(context) { } } -``` -## 🧩 Built-in Pipeline Behaviors - -### All behaviors are automatically registered via AddKSMediator(). +public class SubscriberRepository : Repository +{ + public SubscriberRepository(AppDbContext context) : base(context) { } +} ``` -| Behavior | Description | -|---------------------------|-------------------------------------------------| -| RequestValidationBehavior | Validates incoming requests using FluentValidation | -| ExceptionHandlingBehavior | Logs and rethrows exceptions from handlers | -| RequestProcessorBehavior | Executes pre- and post-processors | -| LoggingBehavior | Logs request and response types | -| NotificationLoggingBehavior | Logs notification handling stages | -``` - -## 🧰 Configuration - -## Register services in Program.cs -```csharp -services.AddLogging(); -services.AddValidatorsFromAssembly(typeof(Program).Assembly); -services.AddKSMediator(Assembly.GetExecutingAssembly()); -``` -## 🧪 Unit Testing +--- + +### Step 3: API Controllers -### Example behavior test ```csharp -[Fact] -public async Task Handle_WithInvalidRequest_ThrowsValidationException() +[ApiController] +[Route("api/[controller]")] +public class BlogPostsController : ControllerBase { - var validator = new Mock>(); - validator.Setup(v => v.ValidateAsync(It.IsAny(), It.IsAny())) - .ReturnsAsync(new ValidationResult(new[] { new ValidationFailure("Name", "Required") })); + private readonly IRepository _repository; - var logger = new Mock>>(); + public BlogPostsController(IRepository repository) + { + _repository = repository; + } - var behavior = new RequestValidationBehavior( - new[] { validator.Object }, logger.Object); + [HttpGet] + public async Task> GetAll() => await _repository.GetAllAsync(); - await Assert.ThrowsAsync(() => - behavior.Handle(new TestRequest(), CancellationToken.None, () => Task.FromResult(new TestResponse()))); + [HttpPost] + public async Task Create(BlogPost post) + { + post.PublishedAt = DateTime.UtcNow; + await _repository.AddAsync(post); + return Ok(post); + } } -``` +``` + +--- + +### Step 4: Subscription Controller -## 📦 Repository & Unit of Work ```csharp -public class ProductService +[ApiController] +[Route("api/[controller]")] +public class NewsletterController : ControllerBase { - private readonly IRepository _repository; - private readonly IUnitOfWork _unitOfWork; + private readonly IRepository _subRepo; - public ProductService(IRepository repository, IUnitOfWork unitOfWork) + public NewsletterController(IRepository subRepo) { - _repository = repository; - _unitOfWork = unitOfWork; + _subRepo = subRepo; } - public async Task AddAsync(Product product) + [HttpPost("subscribe")] + public async Task Subscribe(string email) { - await _repository.AddAsync(product); - await _unitOfWork.CommitAsync(); + await _subRepo.AddAsync(new Subscriber { Email = email }); + return Ok("Subscribed"); } } -``` +``` + +--- + +### Step 5: Weekly Newsletter Job (Example) + +You can use [Hangfire](https://www.hangfire.io/) or any scheduler to run this task weekly: -## 🔍 Specification Pattern ```csharp -public class ActiveProductSpec : Specification +public class WeeklyNewsletterJob { - public ActiveProductSpec() => Criteria = p => p.IsActive; -} -``` + private readonly IRepository _subRepo; + private readonly IRepository _postRepo; -```csharp -var products = await _repository.ListAsync(new ActiveProductSpec()); -``` + public WeeklyNewsletterJob(IRepository subRepo, IRepository postRepo) + { + _subRepo = subRepo; + _postRepo = postRepo; + } + + public async Task Send() + { + var lastWeek = DateTime.UtcNow.AddDays(-7); + var posts = (await _postRepo.GetAllAsync()) + .Where(p => p.PublishedAt > lastWeek) + .ToList(); + + var subscribers = await _subRepo.GetAllAsync(); + + foreach (var sub in subscribers) + { + // send email (via SMTP or 3rd party) + await EmailSender.Send(sub.Email, "Weekly Newsletter", RenderPosts(posts)); + } + } -## ✅ Test Coverage Summary + private string RenderPosts(IEnumerable posts) + { + return string.Join(" +", posts.Select(p => $"{p.Title} +{p.Content}")); + } +} ``` -| Component | Test Status | -|------------------------|-------------| -| Request handling | ✅ | -| Notification publishing| ✅ | -| Streaming requests | ✅ | -| Pipeline behaviors | ✅ | -| Validation | ✅ | -| Exception handling | ✅ | -| Logging | ✅ | -| Repository/UoW/Spec | ✅ | -``` -## 📚 License +--- + +## 📌 Roadmap + +- [x] Generic Repository +- [x] Pagination +- [ ] Specification Pattern +- [ ] Auditing Support +- [ ] Integration with MediatR & CQRS + +--- + +## 🤝 Contributing + +Contributions are welcome! -### This project is licensed under the MIT License. +1. Fork the repository +2. Create a feature branch +3. Submit a pull request + +--- + +## 📄 License + +This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. + +--- -## 👥 Contributing +## 🔗 Related Projects -### Feel free to fork and submit PRs or issues. Contributions are always welcome! \ No newline at end of file +- [MediatR](https://github.com/jbogard/MediatR) +- [Scrutor](https://github.com/khellang/Scrutor) +- [Hangfire](https://www.hangfire.io/) \ No newline at end of file diff --git a/Samples/MediatorSampleApp/Mediator.csproj b/Samples/MediatorSampleApp/Mediator.csproj index 6a2e3de..7c622c1 100644 --- a/Samples/MediatorSampleApp/Mediator.csproj +++ b/Samples/MediatorSampleApp/Mediator.csproj @@ -1,8 +1,8 @@ - + Exe - net10.0 + net8.0 enable enable diff --git a/Samples/MediatorSampleApp/Program.cs b/Samples/MediatorSampleApp/Program.cs index 8d73d6d..6e5f80d 100644 --- a/Samples/MediatorSampleApp/Program.cs +++ b/Samples/MediatorSampleApp/Program.cs @@ -1,8 +1,8 @@ -using System.Reflection; +using System.Reflection; using FluentValidation; -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Extensions; -using KSFramework.Messaging.Samples; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Extensions; +using KSFramework.KSMessaging.Samples; using Microsoft.Extensions.DependencyInjection; var services = new ServiceCollection(); diff --git a/src/KSFramework/Api/ApiResult.cs b/src/KSFramework/Api/ApiResult.cs deleted file mode 100644 index 2f6a3a7..0000000 --- a/src/KSFramework/Api/ApiResult.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System.Net; -using KSFramework.Utilities; -using Microsoft.AspNetCore.Mvc; -using Newtonsoft.Json; - -namespace KSFramework.Api; - -public class ApiResult - { - public bool Status { get; set; } - public HttpStatusCode StatusCode { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public string Message { get; set; } - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public List? Errors { get; set; } - - public ApiResult(bool isSucceeded, HttpStatusCode statusCode, List? errors = null, string? message = null) - { - Status = isSucceeded; - StatusCode = statusCode; - Message = message ?? statusCode.ToDisplay(); - Errors = errors; - } - #region Implicit Operators - public static implicit operator ApiResult(OkResult result) - { - return new ApiResult(true, HttpStatusCode.OK, null); - } - - public static implicit operator ApiResult(BadRequestResult result) - { - return new ApiResult(false, HttpStatusCode.BadRequest, null); - } - - public static implicit operator ApiResult(BadRequestObjectResult result) - { - var errorsList = new List(); - if (result.Value is SerializableError errors) - { - errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); - } - else - { - errorsList.Add(result.Value.ToString()); - } - return new ApiResult(false, HttpStatusCode.BadRequest, errorsList); - } - - public static implicit operator ApiResult(ContentResult result) - { - return new ApiResult(true, HttpStatusCode.OK, null, result.Content); - } - - public static implicit operator ApiResult(NotFoundResult result) - { - return new ApiResult(false, HttpStatusCode.NotFound, null); - } - - public static implicit operator ApiResult(UnauthorizedResult result) - { - return new ApiResult(false, HttpStatusCode.Unauthorized, null); - } - - public static implicit operator ApiResult(UnauthorizedObjectResult result) - { - var errorsList = new List(); - if (result.Value is SerializableError errors) - { - errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); - } - else - { - errorsList.Add(result.Value.ToString()); - } - return new ApiResult(false, HttpStatusCode.Unauthorized, errorsList, null); - } - #endregion - } - - - public class ApiResult : ApiResult - where TData : class - { - [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] - public TData Data { get; set; } - - public ApiResult(bool isSucceeded, HttpStatusCode statusCode, TData data, List? errors = null, string? message = null) - : base(isSucceeded, statusCode, errors, message) - { - Data = data; - } - - #region Implicit Operators - public static implicit operator ApiResult(TData data) - { - return new ApiResult(true, HttpStatusCode.OK, data, null); - } - - public static implicit operator ApiResult(OkResult result) - { - return new ApiResult(true, HttpStatusCode.OK, null, null); - } - - public static implicit operator ApiResult(OkObjectResult result) - { - return new ApiResult(true, HttpStatusCode.OK, (TData)result.Value, null); - } - - public static implicit operator ApiResult(BadRequestResult result) - { - return new ApiResult(false, HttpStatusCode.BadRequest, null, null); - } - - public static implicit operator ApiResult(BadRequestObjectResult result) - { - var errorsList = new List(); - if (result.Value is SerializableError errors) - { - errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); - } - else - { - errorsList.Add(result.Value.ToString()); - } - return new ApiResult(false, HttpStatusCode.BadRequest, null, errorsList); - } - - public static implicit operator ApiResult(ContentResult result) - { - return new ApiResult(true, HttpStatusCode.OK, null, null, result.Content); - } - - public static implicit operator ApiResult(NotFoundResult result) - { - return new ApiResult(false, HttpStatusCode.NotFound, null, null); - } - - public static implicit operator ApiResult(NotFoundObjectResult result) - { - var errors = new List(); - errors.Add(result.Value.ToString()); - return new ApiResult(false, HttpStatusCode.NotFound, (TData)result.Value, errors); - } - - - public static implicit operator ApiResult(UnauthorizedResult result) - { - return new ApiResult(false, HttpStatusCode.Unauthorized, null, null); - } - - public static implicit operator ApiResult(UnauthorizedObjectResult result) - { - var errorsList = new List(); - if (result.Value is SerializableError errors) - { - errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); - } - else - { - errorsList.Add(result.Value.ToString()); - } - return new ApiResult(false, HttpStatusCode.Unauthorized, null, errorsList); - } - #endregion - } \ No newline at end of file diff --git a/src/KSFramework/Domain/AggregatesHelper/IAggregateRoot.cs b/src/KSFramework/Domain/AggregatesHelper/IAggregateRoot.cs deleted file mode 100644 index 9d7e7f4..0000000 --- a/src/KSFramework/Domain/AggregatesHelper/IAggregateRoot.cs +++ /dev/null @@ -1,5 +0,0 @@ - -namespace KSFramework.Domain.AggregatesHelper; -public interface IAggregateRoot -{ -} \ No newline at end of file diff --git a/src/KSFramework/Domain/BaseEntity.cs b/src/KSFramework/Domain/BaseEntity.cs deleted file mode 100644 index 643280a..0000000 --- a/src/KSFramework/Domain/BaseEntity.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -namespace KSFramework.Domain; -public interface IEntity -{ - -} - -public abstract class BaseEntity : BaseEntity -{ - public new Guid Id { get; set; } = Guid.NewGuid(); -} - -public abstract class BaseEntity : IEntity -{ - public TKey Id { get; set; } - public int Version { get; private set; } = 0; - - private List _domainEvents; - public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly(); - - protected void AddDomainEvent(IDomainEvent domainEvent) - { - _domainEvents ??= new List(); - _domainEvents.Add(domainEvent); - } - - protected void IncreaseVersion() - { - Version++; - } - - public void ClearDomainEvents() - { - _domainEvents?.Clear(); - } -} \ No newline at end of file diff --git a/src/KSFramework/Domain/DomainServices/IDomainService.cs b/src/KSFramework/Domain/DomainServices/IDomainService.cs deleted file mode 100644 index ffd5dc1..0000000 --- a/src/KSFramework/Domain/DomainServices/IDomainService.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System; -namespace KSFramework.Domain.DomainServices; -public interface IDomainService -{ -} \ No newline at end of file diff --git a/src/KSFramework/Domain/IRepository.cs b/src/KSFramework/Domain/IRepository.cs deleted file mode 100644 index c107449..0000000 --- a/src/KSFramework/Domain/IRepository.cs +++ /dev/null @@ -1,8 +0,0 @@ -using KSFramework.Domain.AggregatesHelper; - -namespace KSFramework.Domain; - -public interface IRepository where T : IAggregateRoot -{ -} - diff --git a/src/KSFramework/Domain/IUnitOfWork.cs b/src/KSFramework/Domain/IUnitOfWork.cs deleted file mode 100644 index 54aadde..0000000 --- a/src/KSFramework/Domain/IUnitOfWork.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace KSFramework.Domain; - -public interface IUnitOfWork : IDisposable -{ - Task SaveChangesAsync(CancellationToken cancellationToken = default); -} - diff --git a/src/KSFramework/Exceptions/KSValidationException.cs b/src/KSFramework/Exceptions/KSValidationException.cs index 9849343..0067b77 100644 --- a/src/KSFramework/Exceptions/KSValidationException.cs +++ b/src/KSFramework/Exceptions/KSValidationException.cs @@ -4,34 +4,38 @@ namespace KSFramework.Exceptions; public class KSValidationException : KSException { - public IEnumerable Errors; + private IEnumerable? _validationErrors; + public new IEnumerable? Errors + { + get => _validationErrors; + set => _validationErrors = value; + } public KSValidationException() : base(code: 400) { - + } public KSValidationException(string message) : base(code: 400, message) { - + } public KSValidationException(string message, T? errors) : base(code: 400, errors: errors, message) { - + } } -public class KSValidationException: KSException +public class KSValidationException : KSException> { - public IEnumerable Errors; public KSValidationException() : base(code: 400) { - + } public KSValidationException(string message) : base(code: 400, message) { - + } } \ No newline at end of file diff --git a/src/KSFramework/GenericRepository/GenericRepository.cs b/src/KSFramework/GenericRepository/GenericRepository.cs new file mode 100644 index 0000000..ff2b392 --- /dev/null +++ b/src/KSFramework/GenericRepository/GenericRepository.cs @@ -0,0 +1,21 @@ +using System.Linq.Expressions; +using KSFramework.KSDomain.AggregatesHelper; +using KSFramework.Pagination; +using Microsoft.EntityFrameworkCore; + +namespace KSFramework.GenericRepository; + +/// +/// Generic repository implementation for entity operations. +/// +/// The type of the entity. +public class GenericRepository : Repository, IGenericRepository where TEntity : class +{ + /// + /// Initializes a new instance of the GenericRepository class. + /// + /// The database context. + public GenericRepository(DbContext context) : base(context) + { + } +} \ No newline at end of file diff --git a/src/KSFramework/GenericRepository/IGenericRepository.cs b/src/KSFramework/GenericRepository/IGenericRepository.cs new file mode 100644 index 0000000..9e13440 --- /dev/null +++ b/src/KSFramework/GenericRepository/IGenericRepository.cs @@ -0,0 +1,91 @@ +using System.Linq.Expressions; +using KSFramework.Pagination; + +namespace KSFramework.GenericRepository; + +/// +/// Represents a generic repository contract for performing common data access operations on entities. +/// +/// The type of the entity. +public interface IGenericRepository where TEntity : class +{ + /// + /// Asynchronously retrieves an entity by its unique identifier. + /// + /// The unique identifier of the entity. + /// A task representing the asynchronous operation, containing the entity if found; otherwise, null. + ValueTask GetByIdAsync(object id); + + /// + /// Asynchronously retrieves all entities. + /// + /// A task representing the asynchronous operation, containing a collection of all entities. + Task> GetAllAsync(); + + /// + /// Asynchronously retrieves a paginated list of entities with optional filtering and ordering. + /// + /// The index of the page (starting from 1). + /// The number of items per page. + /// Optional filter expression. + /// Optional property name to order by. + /// Indicates if the order should be descending. + /// A task representing the asynchronous operation, containing a paginated list of entities. + Task> GetPagedAsync(int pageIndex, int pageSize, Expression>? where = null, string? orderBy = "", bool desc = false); + + /// + /// Synchronously retrieves a paginated list of entities with optional filtering and ordering. + /// + /// The index of the page (starting from 1). + /// The number of items per page. + /// Optional filter expression. + /// Optional property name to order by. + /// Indicates if the order should be descending. + /// A paginated list of entities. + PaginatedList GetPaged(int pageIndex, int pageSize, Expression>? where = null, string? orderBy = "", bool desc = false); + + /// + /// Finds entities matching the given predicate. + /// + /// The condition to match. + /// A collection of matching entities. + IEnumerable Find(Expression> predicate); + + /// + /// Asynchronously returns a single entity matching the given predicate, or null if none match. + /// + /// The condition to match. + /// A task representing the asynchronous operation, containing a single entity or null. + Task SingleOrDefaultAsync(Expression> predicate); + + /// + /// Asynchronously adds an entity to the repository. + /// + /// The entity to add. + Task AddAsync(TEntity entity); + + /// + /// Asynchronously adds multiple entities to the repository. + /// + /// The entities to add. + Task AddRangeAsync(IEnumerable entities); + + /// + /// Removes an entity from the repository. + /// + /// The entity to remove. + void Remove(TEntity entity); + + /// + /// Removes multiple entities from the repository. + /// + /// The entities to remove. + void RemoveRange(IEnumerable entities); + + /// + /// Asynchronously checks whether any entity matches the given predicate. + /// + /// The condition to match. + /// A task representing the asynchronous operation, containing true if any entity matches; otherwise, false. + Task IsExistValueForPropertyAsync(Expression> predicate); +} \ No newline at end of file diff --git a/src/KSFramework/GenericRepository/IRepository.cs b/src/KSFramework/GenericRepository/IRepository.cs index b9db045..75bdaef 100644 --- a/src/KSFramework/GenericRepository/IRepository.cs +++ b/src/KSFramework/GenericRepository/IRepository.cs @@ -1,21 +1,34 @@ using System.Linq.Expressions; -using KSFramework.Domain.AggregatesHelper; using KSFramework.Pagination; namespace KSFramework.GenericRepository; -public interface IRepository where TEntity : class, IAggregateRoot + +/// +/// Defines the contract for a generic repository to handle basic CRUD and query operations. +/// +/// The entity type. +public interface IRepository where TEntity : class { - ValueTask GetByIdAsync(object id); - Task> GetAllAsync(); - Task> GetPagedAsync(int pageIndex, int pageSize, Expression> where = null, string orderBy = "", bool desc = false); - PaginatedList GetPaged(int pageIndex, int pageSize, Expression> where = null, string orderBy = "", bool desc = false); - IEnumerable Find(Expression> predicate); - Task SingleOrDefaultAsync(Expression> predicate); Task AddAsync(TEntity entity); Task AddRangeAsync(IEnumerable entities); - + IEnumerable Find(Expression> predicate, bool asNoTracking = true); + Task> GetAllAsync(bool asNoTracking = true); + Task> GetPagedAsync( + int pageIndex, + int pageSize, + Expression>? where = null, + string? orderBy = "", + bool desc = false); + PaginatedList GetPaged( + int pageIndex, + int pageSize, + Expression>? where = null, + string? orderBy = "", + bool desc = false); + ValueTask GetByIdAsync(object id); void Remove(TEntity entity); void RemoveRange(IEnumerable entities); - Task IsExistValuForPropertyAsync(Expression> predicate); - + Task SingleOrDefaultAsync(Expression> predicate); + Task IsExistValueForPropertyAsync(Expression> predicate); + void Update(TEntity entity); } \ No newline at end of file diff --git a/src/KSFramework/GenericRepository/IUnitOfWork.cs b/src/KSFramework/GenericRepository/IUnitOfWork.cs new file mode 100644 index 0000000..516108e --- /dev/null +++ b/src/KSFramework/GenericRepository/IUnitOfWork.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; + +namespace KSFramework.GenericRepository; + +/// +/// Represents the Unit of Work pattern, encapsulating the transaction and saving changes. +/// +public interface IUnitOfWork : IDisposable +{ + /// + /// Saves all changes made in this unit of work to the underlying data store asynchronously. + /// + /// A task that represents the asynchronous save operation. The task result contains the number of state entries written to the database. + Task SaveChangesAsync(); + + /// + /// Gets a generic repository for the specified entity type. + /// + /// The type of the entity. + /// An instance of for the specified entity type. + IGenericRepository GetRepository() where TEntity : class; +} \ No newline at end of file diff --git a/src/KSFramework/GenericRepository/Repository.cs b/src/KSFramework/GenericRepository/Repository.cs index fa44b76..d43d42b 100644 --- a/src/KSFramework/GenericRepository/Repository.cs +++ b/src/KSFramework/GenericRepository/Repository.cs @@ -1,70 +1,98 @@ using System.Linq.Expressions; -using KSFramework.Domain.AggregatesHelper; using KSFramework.Pagination; using Microsoft.EntityFrameworkCore; namespace KSFramework.GenericRepository; -public abstract class Repository : IRepository where TEntity : class, IAggregateRoot + +/// +/// Provides a base implementation of the interface using Entity Framework Core. +/// +/// The type of the entity. +public abstract class Repository : IGenericRepository where TEntity : class { private readonly DbContext Context; - protected DbSet Entity; - public Repository(DbContext context) + /// + /// Gets the underlying for querying and persisting entities. + /// + protected readonly DbSet Entity; + + /// + /// Initializes a new instance of the class. + /// + /// The database context. + protected Repository(DbContext context) { - this.Context = context; - Entity = Context.Set(); + Context = context; + Entity = context.Set(); } + /// public virtual async Task AddAsync(TEntity entity) { await Entity.AddAsync(entity); } + /// public virtual async Task AddRangeAsync(IEnumerable entities) { await Entity.AddRangeAsync(entities); } + /// public virtual IEnumerable Find(Expression> predicate) { return Entity.Where(predicate); } + /// public virtual async Task> GetAllAsync() { return await Entity.ToListAsync(); } - public async Task> GetPagedAsync(int pageIndex, int pageSize, Expression> where = null, string orderBy = "", bool desc = false) + /// + public virtual async Task> GetPagedAsync(int pageIndex, int pageSize, Expression>? where = null, string? orderBy = "", bool desc = false) { - return await PaginatedList.CreateAsync(Entity.AsQueryable(), pageIndex, pageSize, where, orderBy, desc); + var query = Entity.AsQueryable(); + if (where != null) + query = query.Where(where); + + return await PaginatedList.CreateAsync(query, pageIndex, pageSize, where, orderBy, desc); } - public PaginatedList GetPaged(int pageIndex, int pageSize, Expression> where = null, string orderBy = "", bool desc = false) + /// + public virtual PaginatedList GetPaged(int pageIndex, int pageSize, Expression>? where = null, string? orderBy = "", bool desc = false) { - return PaginatedList.Create(Entity.AsQueryable(), pageIndex, pageSize, where, orderBy, desc); + var query = Entity.AsQueryable(); + return PaginatedList.Create(query, pageIndex, pageSize, where, orderBy, desc); } - public virtual async ValueTask GetByIdAsync(object id) + /// + public virtual async ValueTask GetByIdAsync(object id) { return await Entity.FindAsync(id); } - public void Remove(TEntity entity) + /// + public virtual void Remove(TEntity entity) { Entity.Remove(entity); } - public void RemoveRange(IEnumerable entities) + /// + public virtual void RemoveRange(IEnumerable entities) { Entity.RemoveRange(entities); } - public async Task SingleOrDefaultAsync(Expression> predicate) + /// + public virtual async Task SingleOrDefaultAsync(Expression> predicate) { return await Entity.SingleOrDefaultAsync(predicate); } - public async Task IsExistValuForPropertyAsync(Expression> predicate) + /// + public virtual async Task IsExistValueForPropertyAsync(Expression> predicate) { return await Entity.AnyAsync(predicate); } diff --git a/src/KSFramework/GenericRepository/UnitOfWork.cs b/src/KSFramework/GenericRepository/UnitOfWork.cs new file mode 100644 index 0000000..57ef87c --- /dev/null +++ b/src/KSFramework/GenericRepository/UnitOfWork.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using KSFramework.KSDomain.AggregatesHelper; +using Microsoft.EntityFrameworkCore; + +namespace KSFramework.GenericRepository; + +/// +/// Implements the Unit of Work pattern, managing a DbContext and providing access to generic repositories. +/// +public class UnitOfWork : IUnitOfWork +{ + private readonly DbContext _context; + private readonly Dictionary _repositories; + /// + /// Initializes a new instance of the UnitOfWork class. + /// + /// The database context. + public UnitOfWork(DbContext context) + { + _context = context; + _repositories = new Dictionary(); + } + + /// + /// Saves all changes made in this unit of work to the underlying data store asynchronously. + /// + /// A task that represents the asynchronous save operation. The task result contains the number of state entries written to the database. + public async Task SaveChangesAsync() + { + return await _context.SaveChangesAsync(); + } + + /// + /// Retrieves a generic repository for the specified entity type. + /// + /// The type of the entity. + /// An instance of for the specified entity type. + IGenericRepository IUnitOfWork.GetRepository() where TEntity : class + { + if (_repositories.TryGetValue(typeof(TEntity), out var repository)) + { + return (IGenericRepository)repository; + } + + var newRepository = new GenericRepository((DbContext)_context); + _repositories.Add(typeof(TEntity), newRepository); + return newRepository; + } + + private bool _disposed = false; + + /// + /// Disposes the current instance of the UnitOfWork. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Disposes the current instance of the UnitOfWork. + /// + /// True to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _context.Dispose(); + } + } + _disposed = true; + } +} \ No newline at end of file diff --git a/src/KSFramework/KSApi/ApiResult.cs b/src/KSFramework/KSApi/ApiResult.cs new file mode 100644 index 0000000..703c32d --- /dev/null +++ b/src/KSFramework/KSApi/ApiResult.cs @@ -0,0 +1,165 @@ +using System.Net; +using KSFramework.Utilities; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; + +namespace KSFramework.KSApi; + +public class ApiResult +{ + public bool Status { get; set; } + public HttpStatusCode StatusCode { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Message { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public List? Errors { get; set; } + + public ApiResult(bool isSucceeded, HttpStatusCode statusCode, List? errors = null, string? message = null) + { + Status = isSucceeded; + StatusCode = statusCode; + Message = message ?? statusCode.ToDisplay(); + Errors = errors; + } + #region Implicit Operators + public static implicit operator ApiResult(OkResult result) + { + return new ApiResult(true, HttpStatusCode.OK, null); + } + + public static implicit operator ApiResult(BadRequestResult result) + { + return new ApiResult(false, HttpStatusCode.BadRequest, null); + } + + public static implicit operator ApiResult(BadRequestObjectResult result) + { + var errorsList = new List(); + if (result.Value is SerializableError errors) + { + errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); + } + else + { + errorsList.Add(result.Value.ToString()); + } + return new ApiResult(false, HttpStatusCode.BadRequest, errorsList); + } + + public static implicit operator ApiResult(ContentResult result) + { + return new ApiResult(true, HttpStatusCode.OK, null, result.Content); + } + + public static implicit operator ApiResult(NotFoundResult result) + { + return new ApiResult(false, HttpStatusCode.NotFound, null); + } + + public static implicit operator ApiResult(UnauthorizedResult result) + { + return new ApiResult(false, HttpStatusCode.Unauthorized, null); + } + + public static implicit operator ApiResult(UnauthorizedObjectResult result) + { + var errorsList = new List(); + if (result.Value is SerializableError errors) + { + errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); + } + else + { + errorsList.Add(result.Value.ToString()); + } + return new ApiResult(false, HttpStatusCode.Unauthorized, errorsList, null); + } + #endregion +} + + +public class ApiResult : ApiResult + where TData : class +{ + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public TData Data { get; set; } + + public ApiResult(bool isSucceeded, HttpStatusCode statusCode, TData data, List? errors = null, string? message = null) + : base(isSucceeded, statusCode, errors, message) + { + Data = data; + } + + #region Implicit Operators + public static implicit operator ApiResult(TData data) + { + return new ApiResult(true, HttpStatusCode.OK, data, null); + } + + public static implicit operator ApiResult(OkResult result) + { + return new ApiResult(true, HttpStatusCode.OK, null, null); + } + + public static implicit operator ApiResult(OkObjectResult result) + { + return new ApiResult(true, HttpStatusCode.OK, (TData)result.Value, null); + } + + public static implicit operator ApiResult(BadRequestResult result) + { + return new ApiResult(false, HttpStatusCode.BadRequest, null, null); + } + + public static implicit operator ApiResult(BadRequestObjectResult result) + { + var errorsList = new List(); + if (result.Value is SerializableError errors) + { + errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); + } + else + { + errorsList.Add(result.Value.ToString()); + } + return new ApiResult(false, HttpStatusCode.BadRequest, null, errorsList); + } + + public static implicit operator ApiResult(ContentResult result) + { + return new ApiResult(true, HttpStatusCode.OK, null, null, result.Content); + } + + public static implicit operator ApiResult(NotFoundResult result) + { + return new ApiResult(false, HttpStatusCode.NotFound, null, null); + } + + public static implicit operator ApiResult(NotFoundObjectResult result) + { + var errors = new List(); + errors.Add(result.Value.ToString()); + return new ApiResult(false, HttpStatusCode.NotFound, (TData)result.Value, errors); + } + + + public static implicit operator ApiResult(UnauthorizedResult result) + { + return new ApiResult(false, HttpStatusCode.Unauthorized, null, null); + } + + public static implicit operator ApiResult(UnauthorizedObjectResult result) + { + var errorsList = new List(); + if (result.Value is SerializableError errors) + { + errorsList = errors.SelectMany(p => (string[])p.Value).Distinct().ToList(); + } + else + { + errorsList.Add(result.Value.ToString()); + } + return new ApiResult(false, HttpStatusCode.Unauthorized, null, errorsList); + } + #endregion +} \ No newline at end of file diff --git a/src/KSFramework/KSDomain/AggregatesHelper/IAggregateRoot.cs b/src/KSFramework/KSDomain/AggregatesHelper/IAggregateRoot.cs new file mode 100644 index 0000000..3a0f2c4 --- /dev/null +++ b/src/KSFramework/KSDomain/AggregatesHelper/IAggregateRoot.cs @@ -0,0 +1,5 @@ + +namespace KSFramework.KSDomain.AggregatesHelper; +public interface IAggregateRoot +{ +} \ No newline at end of file diff --git a/src/KSFramework/Domain/AggregatesHelper/ValueObject.cs b/src/KSFramework/KSDomain/AggregatesHelper/ValueObject.cs similarity index 93% rename from src/KSFramework/Domain/AggregatesHelper/ValueObject.cs rename to src/KSFramework/KSDomain/AggregatesHelper/ValueObject.cs index 2a4b658..1e9825f 100644 --- a/src/KSFramework/Domain/AggregatesHelper/ValueObject.cs +++ b/src/KSFramework/KSDomain/AggregatesHelper/ValueObject.cs @@ -1,8 +1,8 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -namespace KSFramework.Domain.AggregatesHelper; +namespace KSFramework.KSDomain.AggregatesHelper; public abstract class ValueObject { protected static bool EqualOperator(ValueObject left, ValueObject right) @@ -21,7 +21,7 @@ protected static bool NotEqualOperator(ValueObject left, ValueObject right) protected abstract IEnumerable GetEqualityComponents(); - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj == null || obj.GetType() != GetType()) { diff --git a/src/KSFramework/KSDomain/BaseEntity.cs b/src/KSFramework/KSDomain/BaseEntity.cs new file mode 100644 index 0000000..af6a379 --- /dev/null +++ b/src/KSFramework/KSDomain/BaseEntity.cs @@ -0,0 +1,59 @@ +using System; +namespace KSFramework.KSDomain; +/// +/// Represents the base interface for all entities in the domain. +/// +public interface IEntity +{ +} + +/// +/// Base class for entities with GUID as the primary key. +/// +public abstract class BaseEntity : BaseEntity +{ + /// + /// Gets or sets the unique identifier for the entity. + /// + public override required Guid Id { get; set; } = Guid.NewGuid(); +} + +/// +/// Generic base class for entities with a specified key type. +/// +/// The type of the entity's primary key. +public abstract class BaseEntity : IEntity +{ + /// + /// Gets or sets the unique identifier for the entity. + /// + public virtual required TKey Id { get; set; } + + /// + /// Gets the version number of the entity for optimistic concurrency. + /// + public int Version { get; private set; } = 0; + + private List _domainEvents = new List(); + + /// + /// Gets the collection of domain events associated with this entity. + /// + public IReadOnlyCollection DomainEvents => _domainEvents.AsReadOnly(); + + protected void AddDomainEvent(IDomainEvent domainEvent) + { + _domainEvents ??= new List(); + _domainEvents.Add(domainEvent); + } + + protected void IncreaseVersion() + { + Version++; + } + + public void ClearDomainEvents() + { + _domainEvents?.Clear(); + } +} \ No newline at end of file diff --git a/src/KSFramework/Domain/DomainServices/DomainService.cs b/src/KSFramework/KSDomain/DomainServices/DomainService.cs similarity index 60% rename from src/KSFramework/Domain/DomainServices/DomainService.cs rename to src/KSFramework/KSDomain/DomainServices/DomainService.cs index f2ddc4b..707550b 100644 --- a/src/KSFramework/Domain/DomainServices/DomainService.cs +++ b/src/KSFramework/KSDomain/DomainServices/DomainService.cs @@ -1,5 +1,5 @@ -using System; -namespace KSFramework.Domain.DomainServices; +using System; +namespace KSFramework.KSDomain.DomainServices; public abstract class DomainService : IDomainService { public DomainService() diff --git a/src/KSFramework/KSDomain/DomainServices/IDomainService.cs b/src/KSFramework/KSDomain/DomainServices/IDomainService.cs new file mode 100644 index 0000000..b753d7b --- /dev/null +++ b/src/KSFramework/KSDomain/DomainServices/IDomainService.cs @@ -0,0 +1,5 @@ +using System; +namespace KSFramework.KSDomain.DomainServices; +public interface IDomainService +{ +} \ No newline at end of file diff --git a/src/KSFramework/KSDomain/Entity.cs b/src/KSFramework/KSDomain/Entity.cs new file mode 100644 index 0000000..dfbf2f7 --- /dev/null +++ b/src/KSFramework/KSDomain/Entity.cs @@ -0,0 +1,10 @@ +using KSFramework.KSDomain.AggregatesHelper; + +namespace KSFramework.KSDomain; + +/// +/// Base class for entities with GUID as the primary key that can be used with the generic repository. +/// +public abstract class Entity : BaseEntity, IAggregateRoot +{ +} \ No newline at end of file diff --git a/src/KSFramework/Domain/Enumeration.cs b/src/KSFramework/KSDomain/Enumeration.cs similarity index 92% rename from src/KSFramework/Domain/Enumeration.cs rename to src/KSFramework/KSDomain/Enumeration.cs index cac9cdb..6f3c66b 100644 --- a/src/KSFramework/Domain/Enumeration.cs +++ b/src/KSFramework/KSDomain/Enumeration.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace KSFramework.Domain; +namespace KSFramework.KSDomain; public abstract class Enumeration : IComparable { public int Id { get; private set; } @@ -17,7 +17,7 @@ public static IEnumerable GetAll() where T : Enumeration => .Select(f => f.GetValue(null)) .Cast(); - public override bool Equals(object obj) + public override bool Equals(object? obj) { if (obj is not Enumeration otherValue) { @@ -60,6 +60,6 @@ private static T Parse(K value, string description, Func predicat return matchingItem; } - public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id); + public int CompareTo(object? other) => Id.CompareTo(((Enumeration)other!).Id); } \ No newline at end of file diff --git a/src/KSFramework/Domain/IDomainEvent.cs b/src/KSFramework/KSDomain/IDomainEvent.cs similarity index 52% rename from src/KSFramework/Domain/IDomainEvent.cs rename to src/KSFramework/KSDomain/IDomainEvent.cs index 09c10b2..3a3c530 100644 --- a/src/KSFramework/Domain/IDomainEvent.cs +++ b/src/KSFramework/KSDomain/IDomainEvent.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Domain; +namespace KSFramework.KSDomain; public interface IDomainEvent : INotification { DateTime OccurredOn { get; } diff --git a/src/KSFramework/KSDomain/IRepository.cs b/src/KSFramework/KSDomain/IRepository.cs new file mode 100644 index 0000000..09d491e --- /dev/null +++ b/src/KSFramework/KSDomain/IRepository.cs @@ -0,0 +1,8 @@ +using KSFramework.KSDomain.AggregatesHelper; + +namespace KSFramework.KSDomain; + +public interface IRepository where T : IAggregateRoot +{ +} + diff --git a/src/KSFramework/Domain/SpecificationsHelpers/AndNotSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/AndNotSpecification.cs similarity index 93% rename from src/KSFramework/Domain/SpecificationsHelpers/AndNotSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/AndNotSpecification.cs index 033ef51..ef714a6 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/AndNotSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/AndNotSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the combined specification which indicates that the first specification /// can be satisifed by the given object whereas the second one cannot. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/AndSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/AndSpecification.cs similarity index 91% rename from src/KSFramework/Domain/SpecificationsHelpers/AndSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/AndSpecification.cs index 4bbb6d6..df0c18e 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/AndSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/AndSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the combined specification which indicates that both of the given /// specifications should be satisfied by the given object. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/AnySpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/AnySpecification.cs similarity index 86% rename from src/KSFramework/Domain/SpecificationsHelpers/AnySpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/AnySpecification.cs index c2e0352..e2eadf1 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/AnySpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/AnySpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the specification that can be satisfied by the given object /// in any circumstance. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/CompositeSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/CompositeSpecification.cs similarity index 94% rename from src/KSFramework/Domain/SpecificationsHelpers/CompositeSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/CompositeSpecification.cs index 1248b98..eef1bff 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/CompositeSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/CompositeSpecification.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the base class for composite specifications. /// diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ExpressionFuncExtender.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionFuncExtender.cs similarity index 96% rename from src/KSFramework/Domain/SpecificationsHelpers/ExpressionFuncExtender.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionFuncExtender.cs index 0850c66..7d530d5 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ExpressionFuncExtender.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionFuncExtender.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the extender for Expression[Func[T, bool]] type. /// This is part of the solution which solves diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ExpressionSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionSpecification.cs similarity index 91% rename from src/KSFramework/Domain/SpecificationsHelpers/ExpressionSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionSpecification.cs index 1bf9394..878019a 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ExpressionSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ExpressionSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the specification which is represented by the corresponding /// LINQ expression. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ICompositeSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ICompositeSpecification.cs similarity index 90% rename from src/KSFramework/Domain/SpecificationsHelpers/ICompositeSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ICompositeSpecification.cs index 5622943..9c8e550 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ICompositeSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ICompositeSpecification.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents that the implemented classes are composite specifications. /// diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ISpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ISpecification.cs similarity index 91% rename from src/KSFramework/Domain/SpecificationsHelpers/ISpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ISpecification.cs index d0f9e1e..1bc4ed5 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ISpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ISpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents that the implemented classes are specifications. For more /// information about the specification pattern, please refer to diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ISpecificationParser.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ISpecificationParser.cs similarity index 93% rename from src/KSFramework/Domain/SpecificationsHelpers/ISpecificationParser.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ISpecificationParser.cs index 969f23d..81aaafc 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ISpecificationParser.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ISpecificationParser.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents that the implemented classes are specification parsers that /// parses the given specification to a domain specific criteria object, such diff --git a/src/KSFramework/Domain/SpecificationsHelpers/NoneSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/NoneSpecification.cs similarity index 86% rename from src/KSFramework/Domain/SpecificationsHelpers/NoneSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/NoneSpecification.cs index 879bdf2..7e55e2c 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/NoneSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/NoneSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the specification that can be satisfied by the given object /// in no circumstance. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/NotSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/NotSpecification.cs similarity index 92% rename from src/KSFramework/Domain/SpecificationsHelpers/NotSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/NotSpecification.cs index 7ccea8e..620cd38 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/NotSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/NotSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the specification which indicates the semantics opposite to the given specification. /// diff --git a/src/KSFramework/Domain/SpecificationsHelpers/OrSpecification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/OrSpecification.cs similarity index 91% rename from src/KSFramework/Domain/SpecificationsHelpers/OrSpecification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/OrSpecification.cs index dda3c63..502c1ac 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/OrSpecification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/OrSpecification.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the combined specification which indicates that either of the given /// specification should be satisfied by the given object. diff --git a/src/KSFramework/Domain/SpecificationsHelpers/ParameterRebinder.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/ParameterRebinder.cs similarity index 93% rename from src/KSFramework/Domain/SpecificationsHelpers/ParameterRebinder.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/ParameterRebinder.cs index e912435..05df649 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/ParameterRebinder.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/ParameterRebinder.cs @@ -1,6 +1,6 @@ -using System.Linq.Expressions; +using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the parameter rebinder used for rebinding the parameters /// for the given expressions. This is part of the solution which solves diff --git a/src/KSFramework/Domain/SpecificationsHelpers/Specification.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/Specification.cs similarity index 95% rename from src/KSFramework/Domain/SpecificationsHelpers/Specification.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/Specification.cs index d5eccd2..9d0e655 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/Specification.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/Specification.cs @@ -1,6 +1,6 @@ using System.Linq.Expressions; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; /// /// Represents the base class for specifications. /// diff --git a/src/KSFramework/Domain/SpecificationsHelpers/SpecificationExtensions.cs b/src/KSFramework/KSDomain/SpecificationsHelpers/SpecificationExtensions.cs similarity index 97% rename from src/KSFramework/Domain/SpecificationsHelpers/SpecificationExtensions.cs rename to src/KSFramework/KSDomain/SpecificationsHelpers/SpecificationExtensions.cs index 6c3ad34..a0eb820 100644 --- a/src/KSFramework/Domain/SpecificationsHelpers/SpecificationExtensions.cs +++ b/src/KSFramework/KSDomain/SpecificationsHelpers/SpecificationExtensions.cs @@ -1,6 +1,6 @@ -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; -namespace KSFramework.Domain.SpecificationsHelpers; +namespace KSFramework.KSDomain.SpecificationsHelpers; public static class SpecificationExtensions { /// diff --git a/src/KSFramework/KSFramework.csproj b/src/KSFramework/KSFramework.csproj index 1bd184e..2ef1e4d 100644 --- a/src/KSFramework/KSFramework.csproj +++ b/src/KSFramework/KSFramework.csproj @@ -1,11 +1,13 @@ - + net8.0 enable enable + true + $(NoWarn);1591 - + KSFramework Kamran Sadin @@ -30,15 +32,19 @@ - - - - - + + + + + + + + + diff --git a/src/KSFramework/KSMessaging/Abstraction/ICommand.cs b/src/KSFramework/KSMessaging/Abstraction/ICommand.cs new file mode 100644 index 0000000..e2e7610 --- /dev/null +++ b/src/KSFramework/KSMessaging/Abstraction/ICommand.cs @@ -0,0 +1,9 @@ +namespace KSFramework.KSMessaging.Abstraction; + +public interface ICommand : IRequest +{ +} + +public interface ICommand : IRequest +{ +} \ No newline at end of file diff --git a/src/KSFramework/KSMessaging/Abstraction/ICommandHandler.cs b/src/KSFramework/KSMessaging/Abstraction/ICommandHandler.cs new file mode 100644 index 0000000..381f89d --- /dev/null +++ b/src/KSFramework/KSMessaging/Abstraction/ICommandHandler.cs @@ -0,0 +1,13 @@ +using KSFramework.KSMessaging.Abstraction; + +namespace KSFramework.KSMessaging.Abstraction; + +public interface ICommandHandler : IRequestHandler + where TCommand : ICommand +{ +} + +public interface ICommandHandler : IRequestHandler + where TCommand : ICommand +{ +} \ No newline at end of file diff --git a/src/KSFramework/Messaging/Abstraction/IMediator.cs b/src/KSFramework/KSMessaging/Abstraction/IMediator.cs similarity index 97% rename from src/KSFramework/Messaging/Abstraction/IMediator.cs rename to src/KSFramework/KSMessaging/Abstraction/IMediator.cs index 1df2a1a..eb63b46 100644 --- a/src/KSFramework/Messaging/Abstraction/IMediator.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IMediator.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Represents a mediator that supports sending requests and publishing notifications. @@ -14,7 +14,7 @@ public interface IMediator : ISender /// A task representing the asynchronous operation. Task Publish(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification; - + /// /// Sends a request without knowing the response type at compile time. /// @@ -22,7 +22,7 @@ Task Publish(TNotification notification, CancellationToken cancel /// Cancellation token. /// A task that represents the response. Task Send(object request, CancellationToken cancellationToken = default); - + /// /// Publishes a notification without knowing the notification type at compile time. /// @@ -30,7 +30,7 @@ Task Publish(TNotification notification, CancellationToken cancel /// Cancellation token. /// A task representing the asynchronous operation. Task Publish(object notification, CancellationToken cancellationToken = default); - + /// /// Creates a stream of responses for a given stream request. /// diff --git a/src/KSFramework/Messaging/Abstraction/INotification.cs b/src/KSFramework/KSMessaging/Abstraction/INotification.cs similarity index 72% rename from src/KSFramework/Messaging/Abstraction/INotification.cs rename to src/KSFramework/KSMessaging/Abstraction/INotification.cs index d49298a..a7674c5 100644 --- a/src/KSFramework/Messaging/Abstraction/INotification.cs +++ b/src/KSFramework/KSMessaging/Abstraction/INotification.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Marker interface to represent a notification message. diff --git a/src/KSFramework/Messaging/Abstraction/INotificationBehavior.cs b/src/KSFramework/KSMessaging/Abstraction/INotificationBehavior.cs similarity index 91% rename from src/KSFramework/Messaging/Abstraction/INotificationBehavior.cs rename to src/KSFramework/KSMessaging/Abstraction/INotificationBehavior.cs index 0fd7dd8..fbb2315 100644 --- a/src/KSFramework/Messaging/Abstraction/INotificationBehavior.cs +++ b/src/KSFramework/KSMessaging/Abstraction/INotificationBehavior.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Delegate that represents the next notification handler or behavior in the pipeline. diff --git a/src/KSFramework/Messaging/Abstraction/INotificationHandler.cs b/src/KSFramework/KSMessaging/Abstraction/INotificationHandler.cs similarity index 91% rename from src/KSFramework/Messaging/Abstraction/INotificationHandler.cs rename to src/KSFramework/KSMessaging/Abstraction/INotificationHandler.cs index ce192f6..b54e2ec 100644 --- a/src/KSFramework/Messaging/Abstraction/INotificationHandler.cs +++ b/src/KSFramework/KSMessaging/Abstraction/INotificationHandler.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Defines a handler for a specific type of notification. diff --git a/src/KSFramework/Messaging/Abstraction/IPipelineBehavior.cs b/src/KSFramework/KSMessaging/Abstraction/IPipelineBehavior.cs similarity index 96% rename from src/KSFramework/Messaging/Abstraction/IPipelineBehavior.cs rename to src/KSFramework/KSMessaging/Abstraction/IPipelineBehavior.cs index 6899e6f..1763fdc 100644 --- a/src/KSFramework/Messaging/Abstraction/IPipelineBehavior.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IPipelineBehavior.cs @@ -1,7 +1,7 @@ using System.Threading; using System.Threading.Tasks; -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Defines a pipeline behavior for requests that can run code before and after the handler is invoked. /// diff --git a/src/KSFramework/Messaging/Abstraction/IPostProcessor.cs b/src/KSFramework/KSMessaging/Abstraction/IPostProcessor.cs similarity index 84% rename from src/KSFramework/Messaging/Abstraction/IPostProcessor.cs rename to src/KSFramework/KSMessaging/Abstraction/IPostProcessor.cs index bcde205..39c4a2a 100644 --- a/src/KSFramework/Messaging/Abstraction/IPostProcessor.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IPostProcessor.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Defines a post-processor that runs after the request handler. diff --git a/src/KSFramework/KSMessaging/Abstraction/IQuery.cs b/src/KSFramework/KSMessaging/Abstraction/IQuery.cs new file mode 100644 index 0000000..8d0ffa7 --- /dev/null +++ b/src/KSFramework/KSMessaging/Abstraction/IQuery.cs @@ -0,0 +1,11 @@ +using KSFramework.KSMessaging.Abstraction; + +namespace KSFramework.KSMessaging.Abstraction; + +public interface IQuery : IRequest +{ +} + +public interface IQuery : IRequest +{ +} \ No newline at end of file diff --git a/src/KSFramework/KSMessaging/Abstraction/IQueryHandler.cs b/src/KSFramework/KSMessaging/Abstraction/IQueryHandler.cs new file mode 100644 index 0000000..0ee5bbd --- /dev/null +++ b/src/KSFramework/KSMessaging/Abstraction/IQueryHandler.cs @@ -0,0 +1,13 @@ +using KSFramework.KSMessaging.Abstraction; + +namespace KSFramework.KSMessaging.Abstraction; + +public interface IQueryHandler : IRequestHandler + where TQuery : IQuery +{ +} + +public interface IQueryHandler : IRequestHandler + where TQuery : IQuery +{ +} \ No newline at end of file diff --git a/src/KSFramework/Messaging/Abstraction/IRequest.cs b/src/KSFramework/KSMessaging/Abstraction/IRequest.cs similarity index 65% rename from src/KSFramework/Messaging/Abstraction/IRequest.cs rename to src/KSFramework/KSMessaging/Abstraction/IRequest.cs index bff54c1..dbe760f 100644 --- a/src/KSFramework/Messaging/Abstraction/IRequest.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IRequest.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; public interface IRequest { diff --git a/src/KSFramework/Messaging/Abstraction/IRequestHandler.cs b/src/KSFramework/KSMessaging/Abstraction/IRequestHandler.cs similarity index 82% rename from src/KSFramework/Messaging/Abstraction/IRequestHandler.cs rename to src/KSFramework/KSMessaging/Abstraction/IRequestHandler.cs index bf13151..8c40e2f 100644 --- a/src/KSFramework/Messaging/Abstraction/IRequestHandler.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IRequestHandler.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; public interface IRequestHandler where TRequest : IRequest @@ -6,7 +6,7 @@ public interface IRequestHandler Task Handle(TRequest request, CancellationToken cancellationToken); } -public interface IRequestHandler : IRequestHandler +public interface IRequestHandler : IRequestHandler where TRequest : IRequest { } diff --git a/src/KSFramework/Messaging/Abstraction/IRequestPreProcessor.cs b/src/KSFramework/KSMessaging/Abstraction/IRequestPreProcessor.cs similarity index 82% rename from src/KSFramework/Messaging/Abstraction/IRequestPreProcessor.cs rename to src/KSFramework/KSMessaging/Abstraction/IRequestPreProcessor.cs index f11aa7f..8580ac6 100644 --- a/src/KSFramework/Messaging/Abstraction/IRequestPreProcessor.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IRequestPreProcessor.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Defines a pre-processor that runs before the request handler. diff --git a/src/KSFramework/Messaging/Abstraction/ISender.cs b/src/KSFramework/KSMessaging/Abstraction/ISender.cs similarity index 92% rename from src/KSFramework/Messaging/Abstraction/ISender.cs rename to src/KSFramework/KSMessaging/Abstraction/ISender.cs index 35b2c7a..8817735 100644 --- a/src/KSFramework/Messaging/Abstraction/ISender.cs +++ b/src/KSFramework/KSMessaging/Abstraction/ISender.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Represents a sender that can send requests and return responses. diff --git a/src/KSFramework/Messaging/Abstraction/IStreamPipelineBehavior.cs b/src/KSFramework/KSMessaging/Abstraction/IStreamPipelineBehavior.cs similarity index 96% rename from src/KSFramework/Messaging/Abstraction/IStreamPipelineBehavior.cs rename to src/KSFramework/KSMessaging/Abstraction/IStreamPipelineBehavior.cs index 33f50e3..11e4e0d 100644 --- a/src/KSFramework/Messaging/Abstraction/IStreamPipelineBehavior.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IStreamPipelineBehavior.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Defines a pipeline behavior for stream requests that can run code before and after the handler is invoked. diff --git a/src/KSFramework/Messaging/Abstraction/IStreamRequest.cs b/src/KSFramework/KSMessaging/Abstraction/IStreamRequest.cs similarity index 84% rename from src/KSFramework/Messaging/Abstraction/IStreamRequest.cs rename to src/KSFramework/KSMessaging/Abstraction/IStreamRequest.cs index fb71bae..68e98a2 100644 --- a/src/KSFramework/Messaging/Abstraction/IStreamRequest.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IStreamRequest.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Marker interface for a request that returns a stream of responses. diff --git a/src/KSFramework/Messaging/Abstraction/IStreamRequestHandler.cs b/src/KSFramework/KSMessaging/Abstraction/IStreamRequestHandler.cs similarity index 89% rename from src/KSFramework/Messaging/Abstraction/IStreamRequestHandler.cs rename to src/KSFramework/KSMessaging/Abstraction/IStreamRequestHandler.cs index 9f354ad..b46cc7e 100644 --- a/src/KSFramework/Messaging/Abstraction/IStreamRequestHandler.cs +++ b/src/KSFramework/KSMessaging/Abstraction/IStreamRequestHandler.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; /// /// Handles stream requests. diff --git a/src/KSFramework/Messaging/Abstraction/Unit.cs b/src/KSFramework/KSMessaging/Abstraction/Unit.cs similarity index 62% rename from src/KSFramework/Messaging/Abstraction/Unit.cs rename to src/KSFramework/KSMessaging/Abstraction/Unit.cs index f8df51d..1cfda23 100644 --- a/src/KSFramework/Messaging/Abstraction/Unit.cs +++ b/src/KSFramework/KSMessaging/Abstraction/Unit.cs @@ -1,4 +1,4 @@ -namespace KSFramework.Messaging.Abstraction; +namespace KSFramework.KSMessaging.Abstraction; public readonly struct Unit { diff --git a/src/KSFramework/Messaging/Behaviors/ExceptionHandlingBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/ExceptionHandlingBehavior.cs similarity index 92% rename from src/KSFramework/Messaging/Behaviors/ExceptionHandlingBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/ExceptionHandlingBehavior.cs index ebe2639..11547eb 100644 --- a/src/KSFramework/Messaging/Behaviors/ExceptionHandlingBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/ExceptionHandlingBehavior.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Behaviors +namespace KSFramework.KSMessaging.Behaviors { /// /// Behavior that catches exceptions during request handling, logs them, and rethrows. diff --git a/src/KSFramework/Messaging/Behaviors/LoggingBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/LoggingBehavior.cs similarity index 91% rename from src/KSFramework/Messaging/Behaviors/LoggingBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/LoggingBehavior.cs index 6ebce8f..b435767 100644 --- a/src/KSFramework/Messaging/Behaviors/LoggingBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/LoggingBehavior.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; /// /// Logs the request before and after it is handled. diff --git a/src/KSFramework/Messaging/Behaviors/LoggingPostProcessor.cs b/src/KSFramework/KSMessaging/Behaviors/LoggingPostProcessor.cs similarity index 80% rename from src/KSFramework/Messaging/Behaviors/LoggingPostProcessor.cs rename to src/KSFramework/KSMessaging/Behaviors/LoggingPostProcessor.cs index 452e6a4..6491b89 100644 --- a/src/KSFramework/Messaging/Behaviors/LoggingPostProcessor.cs +++ b/src/KSFramework/KSMessaging/Behaviors/LoggingPostProcessor.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; public class LoggingPostProcessor : IRequestPostProcessor { diff --git a/src/KSFramework/Messaging/Behaviors/LoggingPreProcessor.cs b/src/KSFramework/KSMessaging/Behaviors/LoggingPreProcessor.cs similarity index 77% rename from src/KSFramework/Messaging/Behaviors/LoggingPreProcessor.cs rename to src/KSFramework/KSMessaging/Behaviors/LoggingPreProcessor.cs index aa5b028..c4b79db 100644 --- a/src/KSFramework/Messaging/Behaviors/LoggingPreProcessor.cs +++ b/src/KSFramework/KSMessaging/Behaviors/LoggingPreProcessor.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; public class LoggingPreProcessor : IRequestPreProcessor { diff --git a/src/KSFramework/Messaging/Behaviors/NotificationLoggingBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/NotificationLoggingBehavior.cs similarity index 86% rename from src/KSFramework/Messaging/Behaviors/NotificationLoggingBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/NotificationLoggingBehavior.cs index ab5d181..1dbd1fd 100644 --- a/src/KSFramework/Messaging/Behaviors/NotificationLoggingBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/NotificationLoggingBehavior.cs @@ -1,9 +1,9 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; -public class NotificationLoggingBehavior : INotificationBehavior +public class NotificationLoggingBehavior : INotificationBehavior where TNotification : INotification { private readonly ILogger> _logger; diff --git a/src/KSFramework/Messaging/Behaviors/RequestProcessorBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/RequestProcessorBehavior.cs similarity index 95% rename from src/KSFramework/Messaging/Behaviors/RequestProcessorBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/RequestProcessorBehavior.cs index fa06a7b..a91b00a 100644 --- a/src/KSFramework/Messaging/Behaviors/RequestProcessorBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/RequestProcessorBehavior.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; /// /// Executes pre-processors and post-processors for the request. diff --git a/src/KSFramework/Messaging/Behaviors/RequestValidationBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/RequestValidationBehavior.cs similarity index 80% rename from src/KSFramework/Messaging/Behaviors/RequestValidationBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/RequestValidationBehavior.cs index e066f1c..136e113 100644 --- a/src/KSFramework/Messaging/Behaviors/RequestValidationBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/RequestValidationBehavior.cs @@ -1,13 +1,9 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using FluentValidation; using FluentValidation.Results; using Microsoft.Extensions.Logging; -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Behaviors +namespace KSFramework.KSMessaging.Behaviors { /// /// Pipeline behavior for validating requests using FluentValidation validators. @@ -16,7 +12,7 @@ namespace KSFramework.Messaging.Behaviors /// The type of the request. /// The type of the response. public class RequestValidationBehavior : IPipelineBehavior - where TRequest : IRequest // مطمئن شو این شرط در درخواستت وجود دارد + where TRequest : IRequest { private readonly IEnumerable> Validators; private readonly ILogger> Logger; @@ -49,7 +45,7 @@ public async Task Handle(TRequest request, CancellationToken cancella } } - // اگر ولیدیتور نبود یا اعتبارسنجی رد نشد، اجرای هندر ادامه پیدا کند + // If there is no validator or validation passes, continue handler execution return await next(); } } diff --git a/src/KSFramework/Messaging/Behaviors/StreamLoggingBehavior.cs b/src/KSFramework/KSMessaging/Behaviors/StreamLoggingBehavior.cs similarity index 87% rename from src/KSFramework/Messaging/Behaviors/StreamLoggingBehavior.cs rename to src/KSFramework/KSMessaging/Behaviors/StreamLoggingBehavior.cs index 93212e5..a8084a6 100644 --- a/src/KSFramework/Messaging/Behaviors/StreamLoggingBehavior.cs +++ b/src/KSFramework/KSMessaging/Behaviors/StreamLoggingBehavior.cs @@ -1,7 +1,8 @@ -using KSFramework.Messaging.Abstraction; +using System.Runtime.CompilerServices; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Behaviors; +namespace KSFramework.KSMessaging.Behaviors; /// /// A stream pipeline behavior that logs before and after streaming a request. @@ -25,7 +26,7 @@ public StreamLoggingBehavior(ILogger> /// public async IAsyncEnumerable Handle( TRequest request, - CancellationToken cancellationToken, + [EnumeratorCancellation] CancellationToken cancellationToken, StreamHandlerDelegate next) { _logger.LogInformation("Start streaming: {RequestType}", typeof(TRequest).Name); diff --git a/src/KSFramework/Messaging/Extensions/RegisterMediatorServices.cs b/src/KSFramework/KSMessaging/Extensions/RegisterMediatorServices.cs similarity index 94% rename from src/KSFramework/Messaging/Extensions/RegisterMediatorServices.cs rename to src/KSFramework/KSMessaging/Extensions/RegisterMediatorServices.cs index 40e1450..e6a187e 100644 --- a/src/KSFramework/Messaging/Extensions/RegisterMediatorServices.cs +++ b/src/KSFramework/KSMessaging/Extensions/RegisterMediatorServices.cs @@ -1,9 +1,9 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.DependencyInjection; using System.Reflection; -using KSFramework.Messaging.Behaviors; +using KSFramework.KSMessaging.Behaviors; -namespace KSFramework.Messaging.Extensions; +namespace KSFramework.KSMessaging.Extensions; /// /// Extension methods for registering mediator handlers and behaviors. diff --git a/src/KSFramework/Messaging/Mediator.cs b/src/KSFramework/KSMessaging/Mediator.cs similarity index 88% rename from src/KSFramework/Messaging/Mediator.cs rename to src/KSFramework/KSMessaging/Mediator.cs index cfc10b9..b3de658 100644 --- a/src/KSFramework/Messaging/Mediator.cs +++ b/src/KSFramework/KSMessaging/Mediator.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.DependencyInjection; -namespace KSFramework.Messaging; +namespace KSFramework.KSMessaging; /// /// The default implementation of the mediator pattern. @@ -52,15 +52,16 @@ public async Task Send(IRequest request, Cancel var next = handlerDelegate; handlerDelegate = () => { - var method = behaviorType.GetMethod("Handle"); - return (Task)method.Invoke(currentBehavior, new object[] { request, cancellationToken, next }); + var method = behaviorType.GetMethod("Handle") ?? throw new InvalidOperationException("Handle method not found on behavior type."); + var result = method.Invoke(currentBehavior, new object[] { request, cancellationToken, next }) ?? throw new InvalidOperationException("Handle method returned null."); + return (Task)result; }; } // Execute the composed pipeline return await handlerDelegate(); } - + public async Task Send(object request, CancellationToken cancellationToken = default) { if (request == null) @@ -100,7 +101,7 @@ public async Task Send(IRequest request, Cancel var resultProperty = task.GetType().GetProperty("Result"); return resultProperty?.GetValue(task); } - + /// public async Task Publish(object notification, CancellationToken cancellationToken = default) { @@ -126,7 +127,7 @@ public async Task Publish(object notification, CancellationToken cancellationTok var genericMethod = method.MakeGenericMethod(notificationType); await (Task)genericMethod.Invoke(this, new object[] { notification, cancellationToken })!; } - + // /// // public async Task Send(object request, CancellationToken cancellationToken = default) // { @@ -135,7 +136,6 @@ public async Task Publish(object notification, CancellationToken cancellationTok // // var requestType = request.GetType(); // - // // پیدا کردن اینترفیس IRequest که روی request اعمال شده // var interfaceType = requestType // .GetInterfaces() // .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>)); @@ -145,7 +145,6 @@ public async Task Publish(object notification, CancellationToken cancellationTok // // var responseType = interfaceType.GetGenericArguments()[0]; // - // // ساختن متد جنریک Send(IRequest) // var method = typeof(IMediator) // .GetMethods() // .FirstOrDefault(m => @@ -195,14 +194,15 @@ public async Task Publish(TNotification notification, Cancellatio var next = handlerDelegate; handlerDelegate = () => { - var method = behavior.GetType().GetMethod("Handle"); - return (Task)method.Invoke(currentBehavior, new object[] { notification, cancellationToken, next }); + var method = behavior.GetType().GetMethod("Handle") ?? throw new InvalidOperationException("Handle method not found on behavior type."); + var result = method.Invoke(currentBehavior, new object[] { notification, cancellationToken, next }) ?? throw new InvalidOperationException("Handle method returned null."); + return (Task)result; }; } await handlerDelegate(); } - + public IAsyncEnumerable CreateStream( IStreamRequest request, CancellationToken cancellationToken = default) @@ -238,12 +238,14 @@ public IAsyncEnumerable CreateStream( // Compose behaviors foreach (var behavior in behaviors) { + if (behavior == null) throw new ArgumentNullException(nameof(behavior)); var current = behavior; var next = handlerDelegate; handlerDelegate = () => { - var method = behavior.GetType().GetMethod("Handle"); - return (IAsyncEnumerable)method.Invoke(current, new object[] { request, cancellationToken, next })!; + var method = current.GetType().GetMethod("Handle") ?? throw new InvalidOperationException("Handle method not found on behavior type."); + var result = method.Invoke(current, new object[] { request, cancellationToken, next }) ?? throw new InvalidOperationException("Handle method returned null."); + return (IAsyncEnumerable)result; }; } diff --git a/src/KSFramework/Messaging/NotificationLoggingBehavior.cs b/src/KSFramework/KSMessaging/NotificationLoggingBehavior.cs similarity index 90% rename from src/KSFramework/Messaging/NotificationLoggingBehavior.cs rename to src/KSFramework/KSMessaging/NotificationLoggingBehavior.cs index 54c0838..b2f8a76 100644 --- a/src/KSFramework/Messaging/NotificationLoggingBehavior.cs +++ b/src/KSFramework/KSMessaging/NotificationLoggingBehavior.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging; +namespace KSFramework.KSMessaging; public class NotificationLoggingBehavior : INotificationBehavior where TNotification : INotification diff --git a/src/KSFramework/Messaging/Samples/CounterStreamTest.cs b/src/KSFramework/KSMessaging/Samples/CounterStreamTest.cs similarity index 87% rename from src/KSFramework/Messaging/Samples/CounterStreamTest.cs rename to src/KSFramework/KSMessaging/Samples/CounterStreamTest.cs index dfde75b..7889388 100644 --- a/src/KSFramework/Messaging/Samples/CounterStreamTest.cs +++ b/src/KSFramework/KSMessaging/Samples/CounterStreamTest.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; public class CounterStreamRequest : IStreamRequest { diff --git a/src/KSFramework/Messaging/Samples/LoggingNotificationBehavior.cs b/src/KSFramework/KSMessaging/Samples/LoggingNotificationBehavior.cs similarity index 85% rename from src/KSFramework/Messaging/Samples/LoggingNotificationBehavior.cs rename to src/KSFramework/KSMessaging/Samples/LoggingNotificationBehavior.cs index 70c4ec0..dbb0659 100644 --- a/src/KSFramework/Messaging/Samples/LoggingNotificationBehavior.cs +++ b/src/KSFramework/KSMessaging/Samples/LoggingNotificationBehavior.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; public class LoggingNotificationBehavior : INotificationBehavior where TNotification : INotification { diff --git a/src/KSFramework/Messaging/Samples/MultiplyByTwoRequest.cs b/src/KSFramework/KSMessaging/Samples/MultiplyByTwoRequest.cs similarity index 81% rename from src/KSFramework/Messaging/Samples/MultiplyByTwoRequest.cs rename to src/KSFramework/KSMessaging/Samples/MultiplyByTwoRequest.cs index 9116a5a..69ecc26 100644 --- a/src/KSFramework/Messaging/Samples/MultiplyByTwoRequest.cs +++ b/src/KSFramework/KSMessaging/Samples/MultiplyByTwoRequest.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; public class MultiplyByTwoRequest : IRequest { diff --git a/src/KSFramework/Messaging/Samples/NotificationTest.cs b/src/KSFramework/KSMessaging/Samples/NotificationTest.cs similarity index 82% rename from src/KSFramework/Messaging/Samples/NotificationTest.cs rename to src/KSFramework/KSMessaging/Samples/NotificationTest.cs index c84f2a3..ae6659a 100644 --- a/src/KSFramework/Messaging/Samples/NotificationTest.cs +++ b/src/KSFramework/KSMessaging/Samples/NotificationTest.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; public class TestNotification : INotification { diff --git a/src/KSFramework/Messaging/Samples/UserRegisteredHandler.cs b/src/KSFramework/KSMessaging/Samples/UserRegisteredHandler.cs similarity index 88% rename from src/KSFramework/Messaging/Samples/UserRegisteredHandler.cs rename to src/KSFramework/KSMessaging/Samples/UserRegisteredHandler.cs index 3bbd816..ae9c9d8 100644 --- a/src/KSFramework/Messaging/Samples/UserRegisteredHandler.cs +++ b/src/KSFramework/KSMessaging/Samples/UserRegisteredHandler.cs @@ -1,7 +1,7 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.Logging; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; public class SendWelcomeEmailHandler : INotificationHandler { diff --git a/src/KSFramework/Messaging/Samples/UserRegisteredNotification.cs b/src/KSFramework/KSMessaging/Samples/UserRegisteredNotification.cs similarity index 76% rename from src/KSFramework/Messaging/Samples/UserRegisteredNotification.cs rename to src/KSFramework/KSMessaging/Samples/UserRegisteredNotification.cs index fcfba17..63f60cd 100644 --- a/src/KSFramework/Messaging/Samples/UserRegisteredNotification.cs +++ b/src/KSFramework/KSMessaging/Samples/UserRegisteredNotification.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; -namespace KSFramework.Messaging.Samples; +namespace KSFramework.KSMessaging.Samples; /// /// Notification that is triggered when a user registers. diff --git a/src/KSFramework/Messaging/Configuration/ServiceCollectionExtensions.cs b/src/KSFramework/Messaging/Configuration/ServiceCollectionExtensions.cs deleted file mode 100644 index 81889ec..0000000 --- a/src/KSFramework/Messaging/Configuration/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -// using KSFramework.Messaging.Abstraction; -// using Microsoft.Extensions.DependencyInjection; -// using System.Reflection; -// -// namespace KSFramework.Messaging.Configuration -// { -// /// -// /// Extension methods to register messaging components into the service collection. -// /// -// public static class ServiceCollectionExtensions -// { -// /// -// /// Registers mediator handlers and behaviors from specified assemblies with automatic discovery. -// /// -// public static IServiceCollection AddKSMediator(this IServiceCollection services, params Assembly[] assemblies) -// { -// services.AddScoped(); -// services.AddScoped(sp => sp.GetRequiredService()); -// -// services.Scan(scan => scan -// .FromAssemblies(assemblies) -// -// // Request Handlers -// .AddClasses(c => c.AssignableTo(typeof(IRequestHandler<,>))) -// .AsImplementedInterfaces() -// .WithScopedLifetime() -// -// // Notification Handlers -// .AddClasses(c => c.AssignableTo(typeof(INotificationHandler<>))) -// .AsImplementedInterfaces() -// .WithScopedLifetime() -// -// // Pipeline Behaviors -// .AddClasses(c => c.AssignableTo(typeof(IPipelineBehavior<,>))) -// .AsImplementedInterfaces() -// .WithScopedLifetime() -// -// // Notification Behaviors -// .AddClasses(c => c.AssignableTo(typeof(INotificationBehavior<>))) -// .AsImplementedInterfaces() -// .WithScopedLifetime() -// ); -// -// return services; -// } -// } -// } \ No newline at end of file diff --git a/src/KSFramework/Pagination/PaginatedList.cs b/src/KSFramework/Pagination/PaginatedList.cs index c61a392..7a053a8 100644 --- a/src/KSFramework/Pagination/PaginatedList.cs +++ b/src/KSFramework/Pagination/PaginatedList.cs @@ -37,8 +37,8 @@ public bool HasNextPage } } - public static async Task> CreateAsync(IQueryable source, int pageIndex, int pageSize,Expression> where = null, - string orderBy = "", bool desc = false) + public static async Task> CreateAsync(IQueryable source, int pageIndex, int pageSize, Expression>? where = null, + string? orderBy = "", bool desc = false) { if(where is null) where = x => true; @@ -59,8 +59,8 @@ public static async Task> CreateAsync(IQueryable source, int return new PaginatedList(items, count, pageIndex, pageSize); } - public static PaginatedList Create(IQueryable source, int pageIndex, int pageSize,Expression> where = null, - string orderBy = "", bool desc = false) + public static PaginatedList Create(IQueryable source, int pageIndex, int pageSize, Expression>? where = null, + string? orderBy = "", bool desc = false) { if(where is null) where = x => true; diff --git a/src/KSFramework/TagHelpers/ActiveRouteTagHelper.cs b/src/KSFramework/TagHelpers/ActiveRouteTagHelper.cs index a080a69..95268cd 100644 --- a/src/KSFramework/TagHelpers/ActiveRouteTagHelper.cs +++ b/src/KSFramework/TagHelpers/ActiveRouteTagHelper.cs @@ -11,18 +11,18 @@ public class ActiveRouteTagHelper : TagHelper private const string ActiveRouteClassValue = "active-route-class"; [HtmlAttributeName(ActiveRouteClassValue)] - public string ActiveRouteCssClass { get; set; } - private IDictionary _routeValues; + public string ActiveRouteCssClass { get; set; } = string.Empty; + private IDictionary? _routeValues; /// The name of the action method. /// Must be null if is non-null. [HtmlAttributeName("asp-action")] - public string Action { get; set; } + public string? Action { get; set; } /// The name of the controller. /// Must be null if is non-null. [HtmlAttributeName("asp-controller")] - public string Controller { get; set; } + public string? Controller { get; set; } /// Additional parameters for the route. [HtmlAttributeName("asp-all-route-data", DictionaryAttributePrefix = "asp-route-")] @@ -30,14 +30,10 @@ public IDictionary RouteValues { get { - if (this._routeValues == null) - this._routeValues = (IDictionary)new Dictionary((IEqualityComparer)StringComparer.OrdinalIgnoreCase); - return this._routeValues; - } - set - { - this._routeValues = value; + _routeValues ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + return _routeValues; } + set => _routeValues = value; } /// @@ -45,7 +41,7 @@ public IDictionary RouteValues /// [HtmlAttributeNotBound] [ViewContext] - public ViewContext ViewContext { get; set; } + public required ViewContext ViewContext { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { @@ -61,15 +57,15 @@ public override void Process(TagHelperContext context, TagHelperOutput output) private bool ShouldBeActive() { - string currentController = ViewContext.RouteData.Values["Controller"].ToString(); - string currentAction = ViewContext.RouteData.Values["Action"].ToString(); + var currentController = ViewContext.RouteData.Values["Controller"]?.ToString() ?? string.Empty; + var currentAction = ViewContext.RouteData.Values["Action"]?.ToString() ?? string.Empty; - if (!string.IsNullOrWhiteSpace(Controller) && Controller.ToLower() != currentController.ToLower()) + if (!string.IsNullOrWhiteSpace(Controller) && !string.Equals(Controller, currentController, StringComparison.OrdinalIgnoreCase)) { return false; } - if (!string.IsNullOrWhiteSpace(Action) && Action.ToLower() != currentAction.ToLower()) + if (!string.IsNullOrWhiteSpace(Action) && !string.Equals(Action, currentAction, StringComparison.OrdinalIgnoreCase)) { return false; } @@ -94,11 +90,16 @@ private void MakeActive(TagHelperOutput output) classAttr = new TagHelperAttribute("class", ActiveRouteCssClass); output.Attributes.Add(classAttr); } - else if (classAttr.Value == null || classAttr.Value.ToString().IndexOf(ActiveRouteCssClass) < 0) + else { - output.Attributes.SetAttribute("class", classAttr.Value == null - ? ActiveRouteCssClass - : classAttr.Value.ToString() + " " + ActiveRouteCssClass); + var currentClasses = classAttr.Value?.ToString() ?? string.Empty; + if (!currentClasses.Contains(ActiveRouteCssClass)) + { + var newClasses = string.IsNullOrEmpty(currentClasses) + ? ActiveRouteCssClass + : $"{currentClasses} {ActiveRouteCssClass}"; + output.Attributes.SetAttribute("class", newClasses); + } } } } diff --git a/src/KSFramework/Utilities/IdentityExtensions.cs b/src/KSFramework/Utilities/IdentityExtensions.cs index 22da535..3a106f6 100644 --- a/src/KSFramework/Utilities/IdentityExtensions.cs +++ b/src/KSFramework/Utilities/IdentityExtensions.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Security.Claims; using System.Security.Principal; @@ -16,17 +16,17 @@ public static class IdentityExtensions return claimsIdentity?.FindFirstValue(claimType); } - public static string? GetUserId(this IIdentity identity) + public static string? GetUserId(this IIdentity? identity) { return identity?.FindFirstValue(ClaimTypes.NameIdentifier); } - public static T GetUserId(this IIdentity identity) where T : IConvertible + public static T? GetUserId(this IIdentity? identity) where T : IConvertible { var userId = identity?.GetUserId(); return userId.HasValue() ? (T)Convert.ChangeType(userId, typeof(T), CultureInfo.InvariantCulture) - : default(T); + : default; } public static string? GetUserName(this IIdentity identity) diff --git a/src/KSFramework/Utilities/LinqExtentions.cs b/src/KSFramework/Utilities/LinqExtentions.cs index 713dca4..53b0146 100644 --- a/src/KSFramework/Utilities/LinqExtentions.cs +++ b/src/KSFramework/Utilities/LinqExtentions.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; namespace KSFramework.Utilities; public static class ExtendLinq @@ -39,5 +39,5 @@ public int Skip public class ResponseCollection { public int Count { get; set; } - public List Result { get; set; } + public List Result { get; set; } = new List(); } \ No newline at end of file diff --git a/src/KSFramework/Utilities/ModelBuilderExtensions.cs b/src/KSFramework/Utilities/ModelBuilderExtensions.cs index 5efe99a..11bab21 100644 --- a/src/KSFramework/Utilities/ModelBuilderExtensions.cs +++ b/src/KSFramework/Utilities/ModelBuilderExtensions.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata; using Pluralize.NET; using System.Reflection; @@ -15,8 +15,11 @@ public static void AddSingularizingTableNameConvention(this ModelBuilder modelBu Pluralizer pluralizer = new Pluralizer(); foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) { - string tableName = entityType.GetTableName(); - entityType.SetTableName(pluralizer.Singularize(tableName)); + string? tableName = entityType.GetTableName(); + if (tableName != null) + { + entityType.SetTableName(pluralizer.Singularize(tableName)); + } } } @@ -29,8 +32,11 @@ public static void AddPluralizingTableNameConvention(this ModelBuilder modelBuil Pluralizer pluralizer = new Pluralizer(); foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) { - string tableName = entityType.GetTableName(); - entityType.SetTableName(pluralizer.Pluralize(tableName)); + string? tableName = entityType.GetTableName(); + if (tableName != null) + { + entityType.SetTableName(pluralizer.Pluralize(tableName)); + } } } @@ -55,7 +61,7 @@ public static void AddDefaultValueSqlConvention(this ModelBuilder modelBuilder, { foreach (IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) { - IMutableProperty property = entityType.GetProperties().SingleOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); + var property = entityType.GetProperties().FirstOrDefault(p => p.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); if (property != null && property.ClrType == propertyType) property.SetDefaultValueSql(defaultValueSql); } @@ -93,7 +99,8 @@ public static void RegisterEntityTypeConfiguration(this ModelBuilder modelBuilde if (iface.IsConstructedGenericType && iface.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)) { MethodInfo applyConcreteMethod = applyGenericMethod.MakeGenericMethod(iface.GenericTypeArguments[0]); - applyConcreteMethod.Invoke(modelBuilder, new object[] { Activator.CreateInstance(type) }); + var instance = Activator.CreateInstance(type) ?? throw new InvalidOperationException($"Failed to create instance of type {type.FullName}"); + applyConcreteMethod.Invoke(modelBuilder, new object[] { instance }); } } } diff --git a/src/KSFramework/Utilities/StringExtensions.cs b/src/KSFramework/Utilities/StringExtensions.cs index 8c31a67..d3a0075 100644 --- a/src/KSFramework/Utilities/StringExtensions.cs +++ b/src/KSFramework/Utilities/StringExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Net; using System.Text; @@ -74,7 +74,7 @@ public static string ToSnakeCase(this string str) return string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower(); } - public static bool HasValue(this string value, bool ignoreWhiteSpace = true) + public static bool HasValue(this string? value, bool ignoreWhiteSpace = true) { return ignoreWhiteSpace ? !string.IsNullOrWhiteSpace(value) : !string.IsNullOrEmpty(value); } @@ -101,8 +101,8 @@ public static string ToNumeric(this decimal value) public static string ToCurrency(this int value) { - //fa-IR => current culture currency symbol => ریال - //123456 => "123,123ریال" + //fa-IR => current culture currency symbol +//123456 => "123,123" return value.ToString("C0"); } @@ -180,11 +180,13 @@ public static string FixPersianChars(this string str) .Replace("ي", "ی") .Replace(" ", " ") .Replace("‌", " ") - .Replace("ھ", "ه");//.Replace("ئ", "ی"); + .Replace("ھ", "ه"); } - public static string CleanString(this string str) + public static string? CleanString(this string? str) { + if (string.IsNullOrEmpty(str)) + return null; return str.Trim().FixPersianChars().Fa2En().NullIfEmpty(); } @@ -222,9 +224,11 @@ public static string TruncateString(this string str, int start, int maxLength) } #endregion - public static string NullIfEmpty(this string str) + public static string? NullIfEmpty(this string? str) { - return str?.Length == 0 ? null : str; + if (string.IsNullOrEmpty(str)) + return null; + return str; } public static string HtmlToPlainText(this string str) diff --git a/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/GenericRepositoryTests.cs b/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/GenericRepositoryTests.cs new file mode 100644 index 0000000..0d2431c --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/GenericRepositoryTests.cs @@ -0,0 +1,84 @@ +using KSFramework.GenericRepository; +using KSFramework.KSDomain; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace KSFramework.IntegrationTests.GenericRepository; + +public class TestEntity : Entity, KSDomain.AggregatesHelper.IAggregateRoot +{ + public string Name { get; set; } = string.Empty; +} + +public class TestDbContext : DbContext +{ + public TestDbContext(DbContextOptions options) : base(options) + { + } + + public DbSet TestEntities { get; set; } = null!; +} + +public class GenericRepositoryTests : IntegrationTestBase +{ + public GenericRepositoryTests() + { + } + + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + services.AddScoped, TestGenericRepository>(); + } + + [Fact] + public async Task AddAsync_ShouldAddEntityToDatabase() + { + using var scope = CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var repository = scope.ServiceProvider.GetRequiredService>(); + var unitOfWork = scope.ServiceProvider.GetRequiredService(); + + // Arrange + var entity = new TestEntity + { + Id = Guid.NewGuid(), + Name = "Test Entity" + }; + + // Act + await repository.AddAsync(entity); + await unitOfWork.SaveChangesAsync(); + + // Assert + var savedEntity = await dbContext.TestEntities.FirstOrDefaultAsync(e => e.Id == entity.Id); + Assert.NotNull(savedEntity); + Assert.Equal("Test Entity", savedEntity.Name); + } + + [Fact] + public async Task GetByIdAsync_ShouldReturnEntity() + { + using var scope = CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + var repository = scope.ServiceProvider.GetRequiredService>(); + var unitOfWork = scope.ServiceProvider.GetRequiredService(); + + // Arrange + var entity = new TestEntity + { + Id = Guid.NewGuid(), + Name = "Test Entity" + }; + await dbContext.TestEntities.AddAsync(entity); + await dbContext.SaveChangesAsync(); + + // Act + var result = await repository.GetByIdAsync(entity.Id); + + // Assert + Assert.NotNull(result); + Assert.Equal(entity.Id, result.Id); + Assert.Equal("Test Entity", result.Name); + } +} \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/TestGenericRepository.cs b/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/TestGenericRepository.cs new file mode 100644 index 0000000..f5b86e8 --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/GenericRepository/TestGenericRepository.cs @@ -0,0 +1,5 @@ +using KSFramework.GenericRepository; + +namespace KSFramework.IntegrationTests.GenericRepository; + +public class TestGenericRepository(TestDbContext context) : GenericRepository(context); \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.IntegrationTests/IntegrationTestBase.cs b/tests/KSFramework/KSFramework.IntegrationTests/IntegrationTestBase.cs new file mode 100644 index 0000000..8045cde --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/IntegrationTestBase.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.EntityFrameworkCore; +using KSFramework.GenericRepository; +using KSFramework.IntegrationTests.GenericRepository; + +namespace KSFramework.IntegrationTests; + +public abstract class IntegrationTestBase : IDisposable +{ + protected readonly Lazy _serviceProviderLazy; + protected readonly TestServer TestServer; + + protected IntegrationTestBase() + { + var webHostBuilder = new WebHostBuilder() + .Configure(app => + { + // Configure the application here if needed + }) + .ConfigureServices(services => + { + this.ConfigureServices(services); + }); + + TestServer = new TestServer(webHostBuilder); + _serviceProviderLazy = new Lazy(() => TestServer.Services); + } + + protected virtual void ConfigureServices(IServiceCollection services) + { + // Base configuration for all tests + services.AddLogging(); + + // Configure in-memory database for testing + services.AddDbContext(options => + { + options.UseInMemoryDatabase("TestDatabase"); + }); + + services.AddScoped(provider => (DbContext)provider.GetRequiredService()); + + // Register UnitOfWork and IUnitOfWork + services.AddScoped(); + } + + protected IServiceScope CreateScope() + { + return _serviceProviderLazy.Value.CreateScope(); + } + + protected T GetService() where T : class + { + using var scope = CreateScope(); + return scope.ServiceProvider.GetRequiredService(); + } + + public void Dispose() + { + TestServer?.Dispose(); + } +} \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.IntegrationTests/KSFramework.IntegrationTests.csproj b/tests/KSFramework/KSFramework.IntegrationTests/KSFramework.IntegrationTests.csproj new file mode 100644 index 0000000..648a852 --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/KSFramework.IntegrationTests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + false + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/KSFramework/KSFramework.IntegrationTests/Messaging/MediatorTests.cs b/tests/KSFramework/KSFramework.IntegrationTests/Messaging/MediatorTests.cs new file mode 100644 index 0000000..2f2e321 --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/Messaging/MediatorTests.cs @@ -0,0 +1,76 @@ +using KSFramework.KSMessaging; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Extensions; +using Microsoft.Extensions.DependencyInjection; + +namespace KSFramework.IntegrationTests.Messaging; + +public record TestCommand : ICommand +{ + public string Data { get; init; } = string.Empty; +} + +public class TestCommandHandler : ICommandHandler +{ + public Task Handle(TestCommand command, CancellationToken cancellationToken = default) + { + return Task.FromResult($"Handled: {command.Data}"); + } +} + +public record TestQuery : IQuery +{ + public string Data { get; init; } = string.Empty; +} + +public class TestQueryHandler : IQueryHandler +{ + public Task Handle(TestQuery query, CancellationToken cancellationToken = default) + { + return Task.FromResult($"Queried: {query.Data}"); + } +} + +public class MediatorTests : IntegrationTestBase +{ + public MediatorTests() + { + } + + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + + services.AddKSMediator(typeof(MediatorTests).Assembly); + } + + [Fact] + public async Task SendCommand_ShouldExecuteCommandHandler() + { + // Arrange + using var scope = CreateScope(); + var mediator = scope.ServiceProvider.GetRequiredService(); + var command = new TestCommand { Data = "Test Command" }; + + // Act + var result = await mediator.Send(command); + + // Assert + Assert.Equal("Handled: Test Command", result); + } + + [Fact] + public async Task SendQuery_ShouldExecuteQueryHandler() + { + // Arrange + using var scope = CreateScope(); + var mediator = scope.ServiceProvider.GetRequiredService(); + var query = new TestQuery { Data = "Test Query" }; + + // Act + var result = await mediator.Send(query); + + // Assert + Assert.Equal("Queried: Test Query", result); + } +} \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.IntegrationTests/Performance/PerformanceTests.cs b/tests/KSFramework/KSFramework.IntegrationTests/Performance/PerformanceTests.cs new file mode 100644 index 0000000..bc4b108 --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/Performance/PerformanceTests.cs @@ -0,0 +1,112 @@ +using System.Diagnostics; +using KSFramework.GenericRepository; +using KSFramework.IntegrationTests; +using KSFramework.IntegrationTests.GenericRepository; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +public class PerformanceTests : IntegrationTestBase +{ + protected override void ConfigureServices(IServiceCollection services) + { + base.ConfigureServices(services); + + services.AddDbContext(options => + options.UseInMemoryDatabase("PerformanceTestDb")); + + services.AddScoped, GenericRepository>(); + } + + [Fact] + public async Task BulkInsert_ShouldCompleteWithinTimeLimit() + { + using var scope = CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService>(); + var unitOfWork = scope.ServiceProvider.GetRequiredService(); + var stopwatch = new Stopwatch(); + + const int numberOfEntities = 1000; + const int acceptableMilliseconds = 2000; + var entities = Enumerable.Range(1, numberOfEntities) + .Select(i => new TestEntity + { + Id = Guid.NewGuid(), + Name = $"Performance Test Entity {i}" + }) + .ToList(); + + stopwatch.Start(); + foreach (var entity in entities) + { + await repository.AddAsync(entity); + } + await unitOfWork.SaveChangesAsync(); + stopwatch.Stop(); + + Assert.True(stopwatch.ElapsedMilliseconds <= acceptableMilliseconds, + $"Bulk insert took {stopwatch.ElapsedMilliseconds}ms, which is more than acceptable {acceptableMilliseconds}ms"); + } + + [Fact] + public async Task QueryPerformance_ShouldCompleteWithinTimeLimit() + { + using var scope = CreateScope(); + var repository = scope.ServiceProvider.GetRequiredService>(); + var unitOfWork = scope.ServiceProvider.GetRequiredService(); + var stopwatch = new Stopwatch(); + + const int numberOfQueries = 100; + const int acceptableMilliseconds = 1000; + var entity = new TestEntity + { + Id = Guid.NewGuid(), + Name = "Performance Query Test Entity" + }; + await repository.AddAsync(entity); + await unitOfWork.SaveChangesAsync(); + + stopwatch.Start(); + for (int i = 0; i < numberOfQueries; i++) + { + await repository.GetByIdAsync(entity.Id); + } + stopwatch.Stop(); + + Assert.True(stopwatch.ElapsedMilliseconds <= acceptableMilliseconds, + $"Multiple queries took {stopwatch.ElapsedMilliseconds}ms, which is more than acceptable {acceptableMilliseconds}ms"); + } + + [Fact] + public async Task ConcurrentOperations_ShouldHandleEfficiently() + { + const int numberOfTasks = 50; + const int acceptableMilliseconds = 3000; + var stopwatch = new Stopwatch(); + var tasks = new List(); + + stopwatch.Start(); + for (int i = 0; i < numberOfTasks; i++) + { + tasks.Add(Task.Run(async () => + { + using var scope = CreateScope(); + var scopedRepository = scope.ServiceProvider.GetRequiredService>(); + var scopedUnitOfWork = scope.ServiceProvider.GetRequiredService(); + + var entity = new TestEntity + { + Id = Guid.NewGuid(), + Name = $"Concurrent Test Entity {Guid.NewGuid()}" + }; + await scopedRepository.AddAsync(entity); + await scopedUnitOfWork.SaveChangesAsync(); + await scopedRepository.GetByIdAsync(entity.Id); + })); + } + await Task.WhenAll(tasks); + stopwatch.Stop(); + + Assert.True(stopwatch.ElapsedMilliseconds <= acceptableMilliseconds, + $"Concurrent operations took {stopwatch.ElapsedMilliseconds}ms, which is more than acceptable {acceptableMilliseconds}ms"); + } +} \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.IntegrationTests/UnitTest1.cs b/tests/KSFramework/KSFramework.IntegrationTests/UnitTest1.cs new file mode 100644 index 0000000..a493178 --- /dev/null +++ b/tests/KSFramework/KSFramework.IntegrationTests/UnitTest1.cs @@ -0,0 +1,10 @@ +namespace KSFramework.IntegrationTests; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } +} diff --git a/tests/KSFramework/KSFramework.UnitTests/Behaviors/ExceptionHandling/ExceptionHandlingBehaviorTests.cs b/tests/KSFramework/KSFramework.UnitTests/Behaviors/ExceptionHandling/ExceptionHandlingBehaviorTests.cs index 8985cf5..e627133 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Behaviors/ExceptionHandling/ExceptionHandlingBehaviorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Behaviors/ExceptionHandling/ExceptionHandlingBehaviorTests.cs @@ -1,5 +1,5 @@ -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Behaviors; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Behaviors; using Microsoft.Extensions.Logging; using Moq; diff --git a/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestProcessing/RequestProcessorBehaviorTests.cs b/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestProcessing/RequestProcessorBehaviorTests.cs index 7b38757..698aef0 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestProcessing/RequestProcessorBehaviorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestProcessing/RequestProcessorBehaviorTests.cs @@ -1,5 +1,5 @@ -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Behaviors; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Behaviors; using Microsoft.Extensions.Logging; using Moq; diff --git a/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestValidation/RequestValidationBehaviorTests.cs b/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestValidation/RequestValidationBehaviorTests.cs index 747785d..5e014d1 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestValidation/RequestValidationBehaviorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Behaviors/RequestValidation/RequestValidationBehaviorTests.cs @@ -1,7 +1,7 @@ using FluentValidation; using FluentValidation.Results; -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Behaviors; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Behaviors; using Microsoft.Extensions.Logging; using Moq; diff --git a/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorNotificationTests.cs b/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorNotificationTests.cs index 0ea5736..045e36d 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorNotificationTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorNotificationTests.cs @@ -1,5 +1,5 @@ -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Extensions; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Extensions; using Microsoft.Extensions.DependencyInjection; namespace KSFramework.UnitTests.Configuration; diff --git a/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorTests.cs b/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorTests.cs index df048c5..32be322 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Configuration/AddKSMediatorTests.cs @@ -1,6 +1,6 @@ -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Extensions; -using KSFramework.Messaging.Samples; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Extensions; +using KSFramework.KSMessaging.Samples; using Microsoft.Extensions.DependencyInjection; namespace KSFramework.UnitTests.Configuration; @@ -8,7 +8,7 @@ namespace KSFramework.UnitTests.Configuration; public class AddKSMediatorTests { [Fact] - public void AddKSMediator_RegistersHandlersAndBehaviorsFromAssembly() + public async Task AddKSMediator_RegistersHandlersAndBehaviorsFromAssembly() { // Arrange var services = new ServiceCollection(); @@ -23,7 +23,7 @@ public void AddKSMediator_RegistersHandlersAndBehaviorsFromAssembly() // Assert Assert.NotNull(handler); - var result = handler.Handle(new MultiplyByTwoRequest(5), CancellationToken.None).Result; + var result = await handler.Handle(new MultiplyByTwoRequest(5), CancellationToken.None); Assert.Equal(10, result); } } \ No newline at end of file diff --git a/tests/KSFramework/KSFramework.UnitTests/ExceptionHandlingBehaviorTests.cs b/tests/KSFramework/KSFramework.UnitTests/ExceptionHandlingBehaviorTests.cs index 7475f5d..3e77c7d 100644 --- a/tests/KSFramework/KSFramework.UnitTests/ExceptionHandlingBehaviorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/ExceptionHandlingBehaviorTests.cs @@ -1,8 +1,8 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; namespace KSFramework.UnitTests; -using KSFramework.Messaging.Behaviors; +using KSFramework.KSMessaging.Behaviors; using Microsoft.Extensions.Logging; using Moq; using System; diff --git a/tests/KSFramework/KSFramework.UnitTests/KSFramework.UnitTests.csproj b/tests/KSFramework/KSFramework.UnitTests/KSFramework.UnitTests.csproj index 0d84793..202bda2 100644 --- a/tests/KSFramework/KSFramework.UnitTests/KSFramework.UnitTests.csproj +++ b/tests/KSFramework/KSFramework.UnitTests/KSFramework.UnitTests.csproj @@ -1,7 +1,7 @@ - + - net10.0 + net8.0 enable enable false @@ -20,8 +20,8 @@ - - + + diff --git a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/MediatorPublishTests.cs b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/MediatorPublishTests.cs index cf20ce2..2809482 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/MediatorPublishTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/MediatorPublishTests.cs @@ -1,4 +1,4 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.DependencyInjection; namespace KSFramework.UnitTests.Mediator.Publish; @@ -56,7 +56,7 @@ public async Task Publish_InvokesNotificationHandlers() services.AddSingleton>(handler); services.AddLogging(); - services.AddSingleton(); + services.AddSingleton(); var provider = services.BuildServiceProvider(); var mediator = provider.GetRequiredService(); @@ -79,7 +79,7 @@ public async Task Publish_ExecutesNotificationBehavior() services.AddSingleton>(handler); services.AddSingleton>(behavior); services.AddLogging(); - services.AddSingleton(); + services.AddSingleton(); var provider = services.BuildServiceProvider(); var mediator = provider.GetRequiredService(); diff --git a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/SapmleNotification.cs b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/SapmleNotification.cs index b5569b3..62f3255 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/SapmleNotification.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/SapmleNotification.cs @@ -1,4 +1,4 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; namespace KSFramework.UnitTests.Mediator.Publish; diff --git a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationBehavior.cs b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationBehavior.cs index 18857fe..5bdb107 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationBehavior.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationBehavior.cs @@ -1,4 +1,4 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; namespace KSFramework.UnitTests.Mediator.Publish; diff --git a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationHandler.cs b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationHandler.cs index 06b3d9d..e9a92ea 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationHandler.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Mediator/Publish/TestNotificationHandler.cs @@ -1,4 +1,4 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; namespace KSFramework.UnitTests.Mediator.Publish; diff --git a/tests/KSFramework/KSFramework.UnitTests/Mediator/Registration/AddKSMediatorRegistrationTests.cs b/tests/KSFramework/KSFramework.UnitTests/Mediator/Registration/AddKSMediatorRegistrationTests.cs index dd98ef5..901dedb 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Mediator/Registration/AddKSMediatorRegistrationTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Mediator/Registration/AddKSMediatorRegistrationTests.cs @@ -1,5 +1,5 @@ -using KSFramework.Messaging.Abstraction; -using KSFramework.Messaging.Extensions; +using KSFramework.KSMessaging.Abstraction; +using KSFramework.KSMessaging.Extensions; using Microsoft.Extensions.DependencyInjection; namespace KSFramework.UnitTests.Mediator.Registration; diff --git a/tests/KSFramework/KSFramework.UnitTests/RequestValidationBehaviorTests.cs b/tests/KSFramework/KSFramework.UnitTests/RequestValidationBehaviorTests.cs index cdf7810..1592269 100644 --- a/tests/KSFramework/KSFramework.UnitTests/RequestValidationBehaviorTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/RequestValidationBehaviorTests.cs @@ -2,8 +2,8 @@ using FluentValidation.Results; using Microsoft.Extensions.Logging; using Moq; -using KSFramework.Messaging.Behaviors; -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Behaviors; +using KSFramework.KSMessaging.Abstraction; namespace KSFramework.UnitTests { diff --git a/tests/KSFramework/KSFramework.UnitTests/Stream/CreateStream/MediatorCreateStreamTests.cs b/tests/KSFramework/KSFramework.UnitTests/Stream/CreateStream/MediatorCreateStreamTests.cs index c4fc82a..f0e89ff 100644 --- a/tests/KSFramework/KSFramework.UnitTests/Stream/CreateStream/MediatorCreateStreamTests.cs +++ b/tests/KSFramework/KSFramework.UnitTests/Stream/CreateStream/MediatorCreateStreamTests.cs @@ -1,4 +1,4 @@ -using KSFramework.Messaging.Abstraction; +using KSFramework.KSMessaging.Abstraction; using Microsoft.Extensions.DependencyInjection; namespace KSFramework.UnitTests.Stream.CreateStream; @@ -17,7 +17,7 @@ public class StreamRequest : IStreamRequest /// /// A handler for that streams integers from 1 to Count. -/// + /// public class StreamHandler : IStreamRequestHandler { public async IAsyncEnumerable Handle(StreamRequest request, CancellationToken cancellationToken) @@ -32,7 +32,7 @@ public async IAsyncEnumerable Handle(StreamRequest request, CancellationTok /// /// A simple logging behavior for streams that logs before and after streaming. -/// + /// public class LoggingBehavior : IStreamPipelineBehavior { public List Logs { get; } = new(); @@ -60,7 +60,7 @@ public async Task CreateStream_Should_InvokeHandler_AndBehavior() var behavior = new LoggingBehavior(); - services.AddSingleton(); + services.AddSingleton(); services.AddScoped, StreamHandler>(); services.AddScoped>(_ => behavior);