From a1debfd0728c822c20fa739c71274736d03350bf Mon Sep 17 00:00:00 2001 From: "jozsef.b@aycode.com" <9Rj@D}fVwBaN> Date: Thu, 23 Nov 2023 22:38:13 +0100 Subject: [PATCH] database improvements... --- AyCode.Core.Tests/AyCode.Core.Tests.csproj | 19 +++++++++ AyCode.Core.Tests/GlobalUsings.cs | 1 + AyCode.Core.Tests/TestModelBase.cs | 9 +++++ AyCode.Core.sln | 30 +++++++++----- .../AyCode.Database.Tests.csproj | 30 ++++++++++++++ .../DatabaseTestModelBase.cs | 12 ++++++ AyCode.Database.Tests/DatabaseTests.cs | 11 +++++ AyCode.Database.Tests/GlobalUsings.cs | 1 + AyCode.Database/DalBase.cs | 34 ++++++++++++++++ AyCode.Database/DbContexts/DbContextBase.cs | 7 ++-- .../AyCode.Entities.Server.csproj | 4 ++ AyCode.Entities/AyCode.Entities.csproj | 4 ++ AyCode.Entities/Profiles/IProfileBase.cs | 10 +++++ AyCode.Entities/Profiles/ProfileBase.cs | 28 +++++++++++++ AyCode.Entities/Users/IUserBase.cs | 10 +++++ AyCode.Entities/Users/UserBase.cs | 31 ++++++++++++++ AyCode.Interfaces/IOwnerId.cs | 6 +++ AyCode.Utils/AyCode.Utils.csproj | 4 ++ AyCode.Utils/Extensions/DateTimeExtension.cs | 20 ++++++++++ AyCode.Utils/Extensions/GuidExtensions.cs | 15 +++++++ AyCode.Utils/Extensions/LockExtensions.cs | 28 +++++++++++++ AyCode.Utils/Extensions/StringExtensions.cs | 40 +++++++++++++++++++ AyCode.Utils/Wrappers/ReleaseWrapperMutex.cs | 19 +++++++++ .../Wrappers/ReleaseWrapperSemaphore.cs | 19 +++++++++ 24 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 AyCode.Core.Tests/AyCode.Core.Tests.csproj create mode 100644 AyCode.Core.Tests/GlobalUsings.cs create mode 100644 AyCode.Core.Tests/TestModelBase.cs create mode 100644 AyCode.Database.Tests/AyCode.Database.Tests.csproj create mode 100644 AyCode.Database.Tests/DatabaseTestModelBase.cs create mode 100644 AyCode.Database.Tests/DatabaseTests.cs create mode 100644 AyCode.Database.Tests/GlobalUsings.cs create mode 100644 AyCode.Database/DalBase.cs create mode 100644 AyCode.Entities/Profiles/IProfileBase.cs create mode 100644 AyCode.Entities/Profiles/ProfileBase.cs create mode 100644 AyCode.Entities/Users/IUserBase.cs create mode 100644 AyCode.Entities/Users/UserBase.cs create mode 100644 AyCode.Interfaces/IOwnerId.cs create mode 100644 AyCode.Utils/Extensions/DateTimeExtension.cs create mode 100644 AyCode.Utils/Extensions/GuidExtensions.cs create mode 100644 AyCode.Utils/Extensions/LockExtensions.cs create mode 100644 AyCode.Utils/Extensions/StringExtensions.cs create mode 100644 AyCode.Utils/Wrappers/ReleaseWrapperMutex.cs create mode 100644 AyCode.Utils/Wrappers/ReleaseWrapperSemaphore.cs diff --git a/AyCode.Core.Tests/AyCode.Core.Tests.csproj b/AyCode.Core.Tests/AyCode.Core.Tests.csproj new file mode 100644 index 0000000..b8cb93d --- /dev/null +++ b/AyCode.Core.Tests/AyCode.Core.Tests.csproj @@ -0,0 +1,19 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + diff --git a/AyCode.Core.Tests/GlobalUsings.cs b/AyCode.Core.Tests/GlobalUsings.cs new file mode 100644 index 0000000..ab67c7e --- /dev/null +++ b/AyCode.Core.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/AyCode.Core.Tests/TestModelBase.cs b/AyCode.Core.Tests/TestModelBase.cs new file mode 100644 index 0000000..f863edb --- /dev/null +++ b/AyCode.Core.Tests/TestModelBase.cs @@ -0,0 +1,9 @@ +namespace AyCode.Core.Tests; + +public abstract class TestModelBase +{ + protected TestModelBase() + { + //if (IsProductVersion) throw new Exception("IsProductVersion!!!!!"); + } +} \ No newline at end of file diff --git a/AyCode.Core.sln b/AyCode.Core.sln index 16fe191..fb25098 100644 --- a/AyCode.Core.sln +++ b/AyCode.Core.sln @@ -3,23 +3,27 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34221.43 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Core", "AyCode.Core\AyCode.Core.csproj", "{8CCC4969-7306-4747-8A58-80AC5A062EE1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Core", "AyCode.Core\AyCode.Core.csproj", "{8CCC4969-7306-4747-8A58-80AC5A062EE1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Entities", "AyCode.Entities\AyCode.Entities.csproj", "{A2105535-1397-4307-B61B-E49C983353B9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Entities", "AyCode.Entities\AyCode.Entities.csproj", "{A2105535-1397-4307-B61B-E49C983353B9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Entities.Server", "AyCode.Entities.Server\AyCode.Entities.Server.csproj", "{FB027D80-8949-403B-9A86-8E99F305016E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Entities.Server", "AyCode.Entities.Server\AyCode.Entities.Server.csproj", "{FB027D80-8949-403B-9A86-8E99F305016E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Database", "AyCode.Database\AyCode.Database.csproj", "{CAB60420-9F66-42D9-B67E-8E837DBA1F30}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Database", "AyCode.Database\AyCode.Database.csproj", "{CAB60420-9F66-42D9-B67E-8E837DBA1F30}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Interfaces", "AyCode.Interfaces\AyCode.Interfaces.csproj", "{DC42F79D-EEF0-4F32-8608-230F24C6F22A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Interfaces", "AyCode.Interfaces\AyCode.Interfaces.csproj", "{DC42F79D-EEF0-4F32-8608-230F24C6F22A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Interfaces.Server", "AyCode.Interfaces.Server\AyCode.Interfaces.Server.csproj", "{0B5AC35E-3E71-42DC-B503-80D6D3089F91}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Interfaces.Server", "AyCode.Interfaces.Server\AyCode.Interfaces.Server.csproj", "{0B5AC35E-3E71-42DC-B503-80D6D3089F91}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Core.Server", "AyCode.Core.Server\AyCode.Core.Server.csproj", "{F8ECCA33-B5EA-490D-B1A1-D33B5E4238A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Core.Server", "AyCode.Core.Server\AyCode.Core.Server.csproj", "{F8ECCA33-B5EA-490D-B1A1-D33B5E4238A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Utils", "AyCode.Utils\AyCode.Utils.csproj", "{35D47907-CE4F-435B-823E-A02BE59C16D7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Utils", "AyCode.Utils\AyCode.Utils.csproj", "{35D47907-CE4F-435B-823E-A02BE59C16D7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Utils.Server", "AyCode.Utils.Server\AyCode.Utils.Server.csproj", "{EBC6371C-9454-473D-9547-DF9DECEB2D2A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AyCode.Utils.Server", "AyCode.Utils.Server\AyCode.Utils.Server.csproj", "{EBC6371C-9454-473D-9547-DF9DECEB2D2A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Database.Tests", "AyCode.Database.Tests\AyCode.Database.Tests.csproj", "{15272F57-771E-47BE-A960-AD75935254D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AyCode.Core.Tests", "AyCode.Core.Tests\AyCode.Core.Tests.csproj", "{320A245F-6731-476D-A9D8-77888E6B5D9C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +67,14 @@ Global {EBC6371C-9454-473D-9547-DF9DECEB2D2A}.Debug|Any CPU.Build.0 = Debug|Any CPU {EBC6371C-9454-473D-9547-DF9DECEB2D2A}.Release|Any CPU.ActiveCfg = Release|Any CPU {EBC6371C-9454-473D-9547-DF9DECEB2D2A}.Release|Any CPU.Build.0 = Release|Any CPU + {15272F57-771E-47BE-A960-AD75935254D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15272F57-771E-47BE-A960-AD75935254D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15272F57-771E-47BE-A960-AD75935254D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15272F57-771E-47BE-A960-AD75935254D0}.Release|Any CPU.Build.0 = Release|Any CPU + {320A245F-6731-476D-A9D8-77888E6B5D9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {320A245F-6731-476D-A9D8-77888E6B5D9C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {320A245F-6731-476D-A9D8-77888E6B5D9C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {320A245F-6731-476D-A9D8-77888E6B5D9C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AyCode.Database.Tests/AyCode.Database.Tests.csproj b/AyCode.Database.Tests/AyCode.Database.Tests.csproj new file mode 100644 index 0000000..f0fb1d1 --- /dev/null +++ b/AyCode.Database.Tests/AyCode.Database.Tests.csproj @@ -0,0 +1,30 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/AyCode.Database.Tests/DatabaseTestModelBase.cs b/AyCode.Database.Tests/DatabaseTestModelBase.cs new file mode 100644 index 0000000..9ad61b3 --- /dev/null +++ b/AyCode.Database.Tests/DatabaseTestModelBase.cs @@ -0,0 +1,12 @@ +using AyCode.Core.Tests; +using AyCode.Database.DbContexts; + +namespace AyCode.Database.Tests; + +public abstract class DatabaseTestModelBase : TestModelBase where : DbContextBase +{ + protected DatabaseTestModelBase() + { + //var dal = new dal + } +} \ No newline at end of file diff --git a/AyCode.Database.Tests/DatabaseTests.cs b/AyCode.Database.Tests/DatabaseTests.cs new file mode 100644 index 0000000..f4ace3d --- /dev/null +++ b/AyCode.Database.Tests/DatabaseTests.cs @@ -0,0 +1,11 @@ +namespace AyCode.Database.Tests +{ + [TestClass] + public abstract class DatabaseTests : DatabaseTestModelBase + { + [TestMethod] + public void TestMethod1() + { + } + } +} \ No newline at end of file diff --git a/AyCode.Database.Tests/GlobalUsings.cs b/AyCode.Database.Tests/GlobalUsings.cs new file mode 100644 index 0000000..ab67c7e --- /dev/null +++ b/AyCode.Database.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/AyCode.Database/DalBase.cs b/AyCode.Database/DalBase.cs new file mode 100644 index 0000000..1f1d22a --- /dev/null +++ b/AyCode.Database/DalBase.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using AyCode.Database.DbContexts; +using AyCode.Utils.Extensions; + +namespace AyCode.Database; + +public class DalBase where TDbContext : DbContextBase +{ + public string Name { get; private set; } + private TDbContext _ctx; + public TDbContext Ctx => _ctx; + + public DalBase() : this(Activator.CreateInstance()) + { } + + public DalBase(TDbContext ctx) + { + _ctx = ctx; + + if (_ctx.Name.IsNullOrWhiteSpace()) + _ctx.Name = _ctx.GetType().Name; + + Name = $"{this.GetType().Name}, {_ctx.Name}"; + } + + public override string ToString() + { + return Name; + } +} \ No newline at end of file diff --git a/AyCode.Database/DbContexts/DbContextBase.cs b/AyCode.Database/DbContexts/DbContextBase.cs index 4a81766..d144087 100644 --- a/AyCode.Database/DbContexts/DbContextBase.cs +++ b/AyCode.Database/DbContexts/DbContextBase.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using System.Runtime.CompilerServices; +using Microsoft.EntityFrameworkCore; using AyCode.Database.Extensions; using AyCode.Interfaces.TimeStampInfo; @@ -10,12 +11,12 @@ public class DbContextBase : DbContext public Guid SessionId { get; protected set; } = Guid.NewGuid(); - public DbContextBase() : this(string.Empty) + public DbContextBase() { //DbInterception.Add(new UtcDateTimeDbCommandInterceptor()); } - public DbContextBase(string name) : base() + public DbContextBase(string name) : this() { Name = name; } diff --git a/AyCode.Entities.Server/AyCode.Entities.Server.csproj b/AyCode.Entities.Server/AyCode.Entities.Server.csproj index cfadb03..012d8be 100644 --- a/AyCode.Entities.Server/AyCode.Entities.Server.csproj +++ b/AyCode.Entities.Server/AyCode.Entities.Server.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/AyCode.Entities/AyCode.Entities.csproj b/AyCode.Entities/AyCode.Entities.csproj index cfadb03..0117eb9 100644 --- a/AyCode.Entities/AyCode.Entities.csproj +++ b/AyCode.Entities/AyCode.Entities.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/AyCode.Entities/Profiles/IProfileBase.cs b/AyCode.Entities/Profiles/IProfileBase.cs new file mode 100644 index 0000000..0525668 --- /dev/null +++ b/AyCode.Entities/Profiles/IProfileBase.cs @@ -0,0 +1,10 @@ +using AyCode.Entities.Interfaces; +using AyCode.Interfaces; +using AyCode.Interfaces.TimeStampInfo; + +namespace AyCode.Entities.Profiles; + +public interface IProfileBase : IEntityGuid, IOwnerId, ITimeStampInfo +{ + Guid UserMediaId { get; set; } +} \ No newline at end of file diff --git a/AyCode.Entities/Profiles/ProfileBase.cs b/AyCode.Entities/Profiles/ProfileBase.cs new file mode 100644 index 0000000..34d9e2a --- /dev/null +++ b/AyCode.Entities/Profiles/ProfileBase.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AyCode.Entities.Profiles +{ + [Table("ProfileBase")] + public class ProfileBase : IProfileBase + { + public ProfileBase() { } + public ProfileBase(Guid ownerId) : this() + { + OwnerId = ownerId; + } + + [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + public Guid OwnerId { get; } + public Guid UserMediaId { get; set; } + + public DateTime Created { get; set; } + public DateTime Modified { get; set; } + } +} diff --git a/AyCode.Entities/Users/IUserBase.cs b/AyCode.Entities/Users/IUserBase.cs new file mode 100644 index 0000000..9940007 --- /dev/null +++ b/AyCode.Entities/Users/IUserBase.cs @@ -0,0 +1,10 @@ +using AyCode.Entities.Interfaces; +using AyCode.Interfaces.TimeStampInfo; + +namespace AyCode.Entities.Users; + +public interface IUserBase : IEntityGuid, ITimeStampInfo +{ + string Email { get; } + string Password { get; } +} \ No newline at end of file diff --git a/AyCode.Entities/Users/UserBase.cs b/AyCode.Entities/Users/UserBase.cs new file mode 100644 index 0000000..f413f3b --- /dev/null +++ b/AyCode.Entities/Users/UserBase.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace AyCode.Entities.Users +{ + [Table("ProfileBase")] + public class UserBase : IUserBase + { + public UserBase() { } + public UserBase(string email, string password) : this(Guid.NewGuid(), email, password) { } + public UserBase(Guid id, string email, string password) : this() + { + Id = id; + Email = email; + Password = password; + } + + [Key, DatabaseGenerated(DatabaseGeneratedOption.None)] + public Guid Id { get; set; } + public string Email { get; } + public string Password { get; } + + public DateTime Created { get; set; } + public DateTime Modified { get; set; } + } +} diff --git a/AyCode.Interfaces/IOwnerId.cs b/AyCode.Interfaces/IOwnerId.cs new file mode 100644 index 0000000..12b7540 --- /dev/null +++ b/AyCode.Interfaces/IOwnerId.cs @@ -0,0 +1,6 @@ +namespace AyCode.Interfaces; + +public interface IOwnerId +{ + Guid OwnerId { get; } +} diff --git a/AyCode.Utils/AyCode.Utils.csproj b/AyCode.Utils/AyCode.Utils.csproj index cfadb03..9413cb8 100644 --- a/AyCode.Utils/AyCode.Utils.csproj +++ b/AyCode.Utils/AyCode.Utils.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/AyCode.Utils/Extensions/DateTimeExtension.cs b/AyCode.Utils/Extensions/DateTimeExtension.cs new file mode 100644 index 0000000..fa6b5c9 --- /dev/null +++ b/AyCode.Utils/Extensions/DateTimeExtension.cs @@ -0,0 +1,20 @@ +using JetBrains.Annotations; + +namespace AyCode.Utils.Extensions +{ + public static class DateTimeExtension + { + public static bool IsNullOrEmpty(this DateTime dateTime) => dateTime == DateTime.MinValue; + + [ContractAnnotation("dateTime:null => true; dateTime:notnull <= false")] + public static bool IsNullOrEmpty(this DateTime? dateTime) => dateTime == null || dateTime == DateTime.MinValue; + + public static bool IsEqualSqlDateTime2(this DateTime dtSource, DateTime dateTime) + { + return dtSource == dateTime || + (dtSource.Kind == dateTime.Kind && + dtSource.Millisecond == dateTime.Millisecond && dtSource.Hour == dateTime.Hour && dtSource.Second == dateTime.Second && dtSource.Minute == dateTime.Minute && + dtSource.Day == dateTime.Day && dtSource.Month == dateTime.Month && dtSource.Year == dateTime.Year); + } + } +} diff --git a/AyCode.Utils/Extensions/GuidExtensions.cs b/AyCode.Utils/Extensions/GuidExtensions.cs new file mode 100644 index 0000000..94fb475 --- /dev/null +++ b/AyCode.Utils/Extensions/GuidExtensions.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; + +namespace AyCode.Utils.Extensions +{ + public static class GuidExtensions + { + //[ContractAnnotation("=> true, result: notnull; => false, result: null")] + //[ContractAnnotation("true => null; false => notnull")] + public static bool IsNullOrEmpty(this Guid guid) => guid == Guid.Empty; + + //[ContractAnnotation("=> true, result: notnull; => false, result: null")] + [ContractAnnotation("guid:null => true; guid:notnull <= false")] + public static bool IsNullOrEmpty(this Guid? guid) => guid == null || guid == Guid.Empty; + } +} diff --git a/AyCode.Utils/Extensions/LockExtensions.cs b/AyCode.Utils/Extensions/LockExtensions.cs new file mode 100644 index 0000000..5a2859a --- /dev/null +++ b/AyCode.Utils/Extensions/LockExtensions.cs @@ -0,0 +1,28 @@ +using AyCode.Utils.Wrappers; + +namespace AyCode.Utils.Extensions +{ + public static class LockExtensions + { + public static IDisposable UseWaitOne(this Mutex mutex) + { + mutex.WaitOne(); + + return new ReleaseWrapperMutex(mutex); + } + + public static IDisposable UseWait(this SemaphoreSlim semaphore, CancellationToken cancelToken = default) + { + semaphore.Wait(cancelToken); + + return new ReleaseWrapperSemaphore(semaphore); + } + + public static async Task UseWaitAsync(this SemaphoreSlim semaphore, CancellationToken cancelToken = default) + { + await semaphore.WaitAsync(cancelToken).ConfigureAwait(false); + + return new ReleaseWrapperSemaphore(semaphore); + } + } +} diff --git a/AyCode.Utils/Extensions/StringExtensions.cs b/AyCode.Utils/Extensions/StringExtensions.cs new file mode 100644 index 0000000..48c0174 --- /dev/null +++ b/AyCode.Utils/Extensions/StringExtensions.cs @@ -0,0 +1,40 @@ +using System.Diagnostics.CodeAnalysis; +using JetBrains.Annotations; + +namespace AyCode.Utils.Extensions +{ + public static class StringExtensions + { + //[ContractAnnotation("str:null => true; str:notnull <= false")] + [ContractAnnotation("str:null => true; str:notnull <= false")] + public static bool IsNullOrEmpty([NotNullWhen(returnValue: false)] this string str) => str == null || str.Length == 0; + + [ContractAnnotation("str:null => true;")] + public static bool IsNullOrWhiteSpace([NotNullWhen(returnValue: false)] this string str) + { + if (str == null || str.Length == 0) return true; + if (!char.IsWhiteSpace(str[0])) return false; + + for (var i = 1; i < str.Length; i++) + { + if (!char.IsWhiteSpace(str[i])) + return false; + } + + return true; + } + + public static string FirstLetterToUpper(string str) + { + if (str.IsNullOrWhiteSpace()) + return str; + + if (str.Length == 1) + return str.ToUpper(); + + str = str.ToLower(); + + return char.ToUpper(str[0]) + str[1..]; + } + } +} diff --git a/AyCode.Utils/Wrappers/ReleaseWrapperMutex.cs b/AyCode.Utils/Wrappers/ReleaseWrapperMutex.cs new file mode 100644 index 0000000..008134a --- /dev/null +++ b/AyCode.Utils/Wrappers/ReleaseWrapperMutex.cs @@ -0,0 +1,19 @@ +namespace AyCode.Utils.Wrappers +{ + public class ReleaseWrapperMutex : IDisposable + { + private readonly Mutex _mutex; + private bool _isDisposed; + + public ReleaseWrapperMutex(Mutex mutex) => _mutex = mutex; + + public void Dispose() + { + if (_isDisposed) + return; + + _mutex.ReleaseMutex(); + _isDisposed = true; + } + } +} \ No newline at end of file diff --git a/AyCode.Utils/Wrappers/ReleaseWrapperSemaphore.cs b/AyCode.Utils/Wrappers/ReleaseWrapperSemaphore.cs new file mode 100644 index 0000000..229f368 --- /dev/null +++ b/AyCode.Utils/Wrappers/ReleaseWrapperSemaphore.cs @@ -0,0 +1,19 @@ +namespace AyCode.Utils.Wrappers +{ + public class ReleaseWrapperSemaphore : IDisposable + { + private readonly SemaphoreSlim _semaphore; + private bool _isDisposed; + + public ReleaseWrapperSemaphore(SemaphoreSlim semaphore) => _semaphore = semaphore; + + public void Dispose() + { + if (_isDisposed) + return; + + _semaphore.Release(); + _isDisposed = true; + } + } +} \ No newline at end of file