Добавление базовых библиотек

This commit is contained in:
Витковских Евгений 2025-07-15 12:58:24 +03:00
parent 37e709e30d
commit 3c4f7b0622
1155 changed files with 195427 additions and 61 deletions

View File

@ -1,7 +0,0 @@
namespace Kit.Core.Helpers
{
public class Class1
{
}
}

View File

@ -1,25 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36301.6 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "Kit.Core.Helpers.csproj", "{54CF91D7-8299-4DB9-ABF6-4F141A52523A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{54CF91D7-8299-4DB9-ABF6-4F141A52523A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9D8ECF0E-0537-42C4-8DDC-5934B57A874F}
EndGlobalSection
EndGlobal

View File

@ -1 +0,0 @@
1de42344d47904be68c52d310f760c9a33b8396e0e8e253be53c0fe180725da2

View File

@ -1,8 +0,0 @@
{
"version": 2,
"dgSpecHash": "oGlU1raL4Zc=",
"success": true,
"projectFilePath": "C:\\KIT\\Kit.Core\\Kit.Core.Helpers\\Kit.Core.Helpers.csproj",
"expectedPackageFiles": [],
"logs": []
}

View File

@ -3,7 +3,23 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36301.6
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "Kit.Core.Helpers\Kit.Core.Helpers.csproj", "{AF9E1670-5E30-EEEC-3261-A64E6069FE41}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LibCommon", "LibCommon", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Core.Helpers", "LibCommon\Kit.Core.Helpers\Kit.Core.Helpers.csproj", "{45C84BE0-7434-60C9-C314-2ABBEC2244EC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Minio", "LibExternal\Minio\Minio.csproj", "{2EB985EC-7D25-FA14-84F6-8158236A99CF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LibExternal", "LibExternal", "{46D7C78C-2A15-4F28-8498-58FE53B592F0}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql", "LibExternal\Npgsql\Npgsql.csproj", "{AF14796C-37B9-E123-FD4C-4C212045BE8F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.IO.Hashing", "LibExternal\System.IO.Hashing\System.IO.Hashing.csproj", "{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Reactive", "LibExternal\System.Reactive\System.Reactive.csproj", "{837A418C-8CCE-5A21-E61F-EA456EEBECBE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Helpers.OpenXml", "LibCommon\Kit.Helpers.OpenXml\Kit.Helpers.OpenXml.csproj", "{2067E7B0-78BB-95B5-68E2-B38C1D158621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kit.Sign", "LibCommon\Kit.Sign\Kit.Sign.csproj", "{4F7F1524-5EC0-455A-3046-36E2252BC850}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -11,14 +27,47 @@ Global
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF9E1670-5E30-EEEC-3261-A64E6069FE41}.Release|Any CPU.Build.0 = Release|Any CPU
{45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{45C84BE0-7434-60C9-C314-2ABBEC2244EC}.Release|Any CPU.Build.0 = Release|Any CPU
{2EB985EC-7D25-FA14-84F6-8158236A99CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EB985EC-7D25-FA14-84F6-8158236A99CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EB985EC-7D25-FA14-84F6-8158236A99CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2EB985EC-7D25-FA14-84F6-8158236A99CF}.Release|Any CPU.Build.0 = Release|Any CPU
{AF14796C-37B9-E123-FD4C-4C212045BE8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF14796C-37B9-E123-FD4C-4C212045BE8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF14796C-37B9-E123-FD4C-4C212045BE8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF14796C-37B9-E123-FD4C-4C212045BE8F}.Release|Any CPU.Build.0 = Release|Any CPU
{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B3FA8F79-9DF2-476D-AA0B-F02A201D5997}.Release|Any CPU.Build.0 = Release|Any CPU
{837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{837A418C-8CCE-5A21-E61F-EA456EEBECBE}.Release|Any CPU.Build.0 = Release|Any CPU
{2067E7B0-78BB-95B5-68E2-B38C1D158621}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2067E7B0-78BB-95B5-68E2-B38C1D158621}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2067E7B0-78BB-95B5-68E2-B38C1D158621}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2067E7B0-78BB-95B5-68E2-B38C1D158621}.Release|Any CPU.Build.0 = Release|Any CPU
{4F7F1524-5EC0-455A-3046-36E2252BC850}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F7F1524-5EC0-455A-3046-36E2252BC850}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F7F1524-5EC0-455A-3046-36E2252BC850}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F7F1524-5EC0-455A-3046-36E2252BC850}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{45C84BE0-7434-60C9-C314-2ABBEC2244EC} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{2EB985EC-7D25-FA14-84F6-8158236A99CF} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
{AF14796C-37B9-E123-FD4C-4C212045BE8F} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
{B3FA8F79-9DF2-476D-AA0B-F02A201D5997} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
{837A418C-8CCE-5A21-E61F-EA456EEBECBE} = {46D7C78C-2A15-4F28-8498-58FE53B592F0}
{2067E7B0-78BB-95B5-68E2-B38C1D158621} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{4F7F1524-5EC0-455A-3046-36E2252BC850} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9D8ECF0E-0537-42C4-8DDC-5934B57A874F}
EndGlobalSection

View File

@ -0,0 +1,55 @@
using System.Reflection;
namespace Kit.Helpers
{
public class AssemblyHelper
{
public static void CheckSignatures()
{
// Получаем эталонный открытый ключ из текущей сборки
Assembly currentAssembly = Assembly.GetExecutingAssembly();
var referencePublicKey = currentAssembly.GetName().GetPublicKey();
// Проверяем, что текущая сборка подписана
if (referencePublicKey == null || referencePublicKey.Length == 0)
{
throw new ApplicationException("The current build is not signed.");
}
// Получаем список всех загруженных сборок
var assemblies = AppDomain.CurrentDomain.GetAssemblies().OrderBy(x => x.FullName).ToList();
bool anyInvalid = false;
// Проверяем каждую сборку
foreach (var assembly in assemblies)
{
AssemblyName assemblyName = assembly.GetName();
var publicKey = assemblyName.GetPublicKey();
if (assemblyName.Name.ToLowerStartsWith("RiskProf") == false)
{
continue;
}
// Если сборка не подписана
if (publicKey == null || publicKey.Length == 0)
{
anyInvalid = true;
Console.WriteLine($"The assembly {assembly.FullName} is not signed.");
}
// Сравниваем открытый ключ с эталонным
else if (referencePublicKey.SequenceEqual(publicKey) == false)
{
anyInvalid = true;
Console.WriteLine($"The assembly {assembly.FullName} is signed with a different key.");
}
}
if (anyInvalid)
{
throw new ApplicationException("Not all signatures match the signature of the current build.");
}
}
}
}

View File

@ -0,0 +1,11 @@
namespace Kit.Helpers
{
/// <summary> Атрибут, указывающий на то, что для данного статического класса / enum нужно сформировать js-объект </summary>
/// <remarks> Описания класса и полей, которые нужно включить в генерацию, нужно указывать с помощью <see cref="System.ComponentModel.DescriptionAttribute"/> </remarks>
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Enum, Inherited = false, AllowMultiple = false)]
public sealed class NeedToGenerateJsConstAttribute : Attribute
{
/// <summary> Создавать ли константный объект "{имя класса/enum}Titles" с теми же ключами, но значениями из <see cref="System.ComponentModel.DescriptionAttribute"/> </summary>
public bool NeedTitlesObject { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System;
namespace Kit.Helpers
{
/// <summary> Атрибут, указывающий на то, что для данного класса нужно сформировать JsDoc-<see langword="@typedef"/> </summary>
/// <remarks> Описания класса и полей, которые нужно включить в генерацию, нужно указывать с помощью <see cref="System.ComponentModel.DescriptionAttribute"/> </remarks>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class NeedToGenerateJsDocAttribute : Attribute { }
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Kit.Helpers.Auth
{
public interface ICurrentUser
{
string GetUserToken();
}
}

View File

@ -0,0 +1,54 @@
namespace Kit.Helpers.Auth
{
using Kit.Helpers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
public class SecurityKeyFilter : IAuthorizationFilter
{
private string _authServiceUrl;
private ISecurityKeyService _securityKeyService;
public SecurityKeyFilter(IConfiguration configuration, ISecurityKeyService securityKeyService)
{
_authServiceUrl = configuration["Auth:ServiceUrl"];
_securityKeyService = securityKeyService;
}
public bool AllowMultiple { get { return false; } }
public void OnAuthorization(AuthorizationFilterContext context)
{
var routeData = context.HttpContext.GetRouteData();
//// securityKeyName
string securityKeyName = routeData.DataTokens["APP_SecurityKeyName"] as string;
if (string.IsNullOrWhiteSpace(securityKeyName)) return;
//// requestSecurityKeyValue
string requestSecurityKeyValue = context.HttpContext.Request.Headers.ReadAuthHeader();
if (!_securityKeyService.CheckKey(securityKeyName, requestSecurityKeyValue))
{
string method = context.HttpContext.Request.Method.ToLower();
string href = $"{_authServiceUrl}/noaccess/{method}";
string hrefWithQuery = href + context.HttpContext.Request.QueryString;
context.Result = new RedirectResult(hrefWithQuery);
if (method.Equals("get"))
{
context.Result = new RedirectResult(hrefWithQuery);
}
if (method.Equals("post"))
{
context.Result = new JsonResult(new { State = 307, Url = hrefWithQuery });
}
}
}
}
}

View File

@ -0,0 +1,50 @@
namespace Kit.Helpers.Auth
{
using System.Collections.Generic;
public interface ISecurityKeyRegisterService
{
ISecurityKeyRegisterService RegisterKey(string name, string value);
}
public interface ISecurityKeyService
{
bool CheckKey(string name, string value);
string GetKey(string name);
}
public class SecurityKeyService : ISecurityKeyService, ISecurityKeyRegisterService
{
private readonly IDictionary<string, string> _securityKeys;
public SecurityKeyService()
{
_securityKeys = new Dictionary<string, string>();
}
public bool CheckKey(string name, string value)
{
string targetValue;
if(_securityKeys.TryGetValue(name, out targetValue))
{
if (string.IsNullOrWhiteSpace(targetValue)) return true;
return targetValue == value;
}
return false;
}
public string GetKey(string name)
{
string targetValue;
return _securityKeys.TryGetValue(name, out targetValue)
? targetValue
: string.Empty;
}
public ISecurityKeyRegisterService RegisterKey(string name, string value)
{
_securityKeys[name] = value;
return this;
}
}
}

View File

@ -0,0 +1,96 @@
using Microsoft.Extensions.Caching.Memory;
using Kit.Helpers;
namespace Kit.Helpers.Cache
{
public abstract class BaseCacheProvider : ICacheProvider
{
private readonly Dictionary<string, object> _lockObjects = new Dictionary<string, object>(1000);
private readonly object _keysLock = new object();
public abstract bool Contains(string cacheName);
public abstract void PurgeCacheItems(string cacheName, bool isPrefix = false);
public abstract void SetCacheData<TObject>(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
public abstract TResult GetCacheData<TResult>(string cacheName);
protected string CacheNameNormalize(string casheName)
{
if (casheName == null) return string.Empty;
return casheName.ToLower();
}
protected object GetLockObject(string cacheName)
{
lock (_keysLock)
{
if (!this._lockObjects.ContainsKey(cacheName)) this._lockObjects.Add(cacheName, new object());
return this._lockObjects[cacheName];
}
}
public virtual string CreateCacheName(object prefix, params object[] args)
{
prefix = prefix != null ? prefix.ToString()!.AddSufixIsNotEmpty("_") : string.Empty;
string end = string.Empty;
if (!args.IsNullOrEmpty())
{
end = args.Where(x => x != null).Select(x => x.ToString()).Join("_");
}
return $"{prefix}{end}";
}
public virtual TResult GetCacheData<TResult>(string cacheName, Func<TResult> getMethod, MemoryCacheEntryOptions setProperties = null)
{
cacheName = CacheNameNormalize(cacheName);
Check.ArgumentIsNotNull(getMethod, "getMethod");
if (cacheName.IsNullOrEmpty()) return (TResult)getMethod();
// если есть данные в кеше но возвращаем
TResult data = GetCacheData<TResult>(cacheName);
if (data != null) return data;
lock (this.GetLockObject(cacheName))
{
// повторная проверка после блокировки
data = this.GetCacheData<TResult>(cacheName);
if (data != null) return data;
// если в кеше данных нет то выполняем метод
data = getMethod();
this.SetCacheData(cacheName, data, setProperties);
}
return data;
}
public TResult GetCacheData<TResult>(string cacheName, Func<TResult> getMethod, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null)
{
cacheName = CacheNameNormalize(cacheName);
Check.ArgumentIsNotNull(getMethod, "getMethod");
if (cacheName.IsNullOrEmpty()) return (TResult)getMethod();
// если есть данные в кеше но возвращаем
TResult data = GetCacheData<TResult>(cacheName);
if (data != null) return data;
lock (this.GetLockObject(cacheName))
{
// повторная проверка после блокировки
data = this.GetCacheData<TResult>(cacheName);
if (data != null) return data;
// если в кеше данных нет то выполняем метод
data = getMethod();
this.SetCacheData(cacheName, data, setProperties, callback);
}
return data;
}
}
}

View File

@ -0,0 +1,20 @@
namespace Kit.Helpers.Cache
{
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public interface ICacheProvider
{
string CreateCacheName(object prefix, params object[] args);
bool Contains(string cacheName);
void PurgeCacheItems(string cacheName, bool isPrefix = false);
void SetCacheData<TObject>(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
TResult GetCacheData<TResult>(string cacheName);
TResult GetCacheData<TResult>(string cacheName, Func<TResult> getMethod, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null);
}
}

View File

@ -0,0 +1,23 @@
namespace Kit.Helpers.Cache
{
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
public static class MemoryCacheExtensions
{
private static readonly Func<MemoryCache, object> GetEntriesCollection = Delegate.CreateDelegate(
typeof(Func<MemoryCache, object>),
typeof(MemoryCache).GetProperty("EntriesCollection", BindingFlags.NonPublic | BindingFlags.Instance).GetGetMethod(true),
throwOnBindFailure: true) as Func<MemoryCache, object>;
public static IEnumerable GetKeys(this IMemoryCache memoryCache) =>
((IDictionary)GetEntriesCollection((MemoryCache)memoryCache)).Keys;
public static IEnumerable<T> GetKeys<T>(this IMemoryCache memoryCache) =>
GetKeys(memoryCache).OfType<T>();
}
}

View File

@ -0,0 +1,84 @@
namespace Kit.Helpers.Cache
{
using Microsoft.Extensions.Caching.Memory;
using Kit.Helpers;
using System;
using System.Collections;
using System.Collections.Generic;
public interface IHttpCacheProvider : ICacheProvider { }
public class MemoryCacheProvider : BaseCacheProvider, IHttpCacheProvider
{
private IMemoryCache _memoryCache;
public MemoryCacheProvider(IMemoryCache memoryCache)
{
_memoryCache = memoryCache;
}
public override bool Contains(string cacheName)
{
cacheName = this.CacheNameNormalize(cacheName);
object obj = null;
return _memoryCache.TryGetValue(cacheName, out obj);
}
public override TResult GetCacheData<TResult>(string cacheName)
{
cacheName = CacheNameNormalize(cacheName);
object? result = this._memoryCache.Get(cacheName);
if (result == null) return default!;
Check.IsTrue(result is TResult, $"Cache value has type:{result.GetType().FullName}. ArgumentType:{typeof(TResult).FullName}");
return (TResult)result;
}
public override void PurgeCacheItems(string cacheName, bool isPrefix = false)
{
cacheName = CacheNameNormalize(cacheName);
if (isPrefix)
{
var itemsToRemove = new List<string>();
foreach (DictionaryEntry item in this._memoryCache.GetKeys())
{
string key = item.Key.ToString()!;
if (key.StartsWith(cacheName))
{
itemsToRemove.Add(key);
}
}
//удаляем элементы кэша
itemsToRemove.ForEach(x => this._memoryCache.Remove(x));
}
else
{
this._memoryCache.Remove(cacheName);
}
}
public override void SetCacheData<TObject>(string cacheName, TObject data, MemoryCacheEntryOptions? setProperties = null, PostEvictionDelegate? callback = null)
{
if (data == null) return;
cacheName = CacheNameNormalize(cacheName);
if (setProperties == null)
{
setProperties = new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromDays(1) };
setProperties.Priority = CacheItemPriority.Normal;
if (callback != null)
{
setProperties.RegisterPostEvictionCallback(callback);
}
}
this._memoryCache.Set(cacheName, data, setProperties);
}
}
}

View File

@ -0,0 +1,257 @@
namespace Kit.Helpers
{
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
/// <summary>
/// a static class that checks if parameter is invalid and raises an exception in this case
/// </summary>
public static class Check
{
[DebuggerStepThrough]
public static void ArgumentIsNotNull([AllowNull][NotNull] object argument, string name)
{
if (argument == null)
{
throw new ArgumentNullException(name);
}
}
[DebuggerStepThrough]
public static void ArgumentIsNotNull([AllowNull][NotNull] object argument, string name, string message)
{
if (argument == null)
{
throw new ArgumentException(message, name);
}
}
[DebuggerStepThrough]
public static void ArgumentIsNotNullOrEmpty([AllowNull][NotNull] string argument, string name)
{
if (string.IsNullOrWhiteSpace(argument))
{
throw new ArgumentNullException(name);
}
}
[DebuggerStepThrough]
public static void ArgumentIsNotNullOrEmpty([AllowNull][NotNull] string argument, string name, string message)
{
if (string.IsNullOrWhiteSpace(argument))
{
throw new ArgumentException(message, name);
}
}
[DebuggerStepThrough]
public static void IntegerArgumentPositive(Int32 value, string name)
{
if (value < 1) throw new ArgumentOutOfRangeException(name, value, string.Empty);
}
[DebuggerStepThrough]
public static void IntegerArgumentPositive(Int32? value, string name)
{
if (!value.HasValue) throw new ArgumentNullException(name);
if (value.Value < 1) throw new ArgumentOutOfRangeException(name, value.Value, string.Empty);
}
[DebuggerStepThrough]
public static void IntegerPositive(Int16 value, string message)
{
if (value < 1) throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IntegerPositive(Int32 value, string message)
{
if (value < 1) throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IntegerPositive(Int64 value, string message)
{
if (value < 1) throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IntegerPositive(Int32? value, string message)
{
if (!value.HasValue) throw new InvalidOperationException(message);
if (value.Value < 1) throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IntegerArgumentPositive(Int64 value, string name)
{
if (value < 1) throw new ArgumentOutOfRangeException(name, value, string.Empty);
}
[DebuggerStepThrough]
public static void IntegerArgumentPositive(Int64? value, string name)
{
if (!value.HasValue) throw new ArgumentNullException(name);
if (value.Value < 1) throw new ArgumentOutOfRangeException(name, value.Value, string.Empty);
}
[DebuggerStepThrough]
public static void StringArgumentIsNotNullOrEmpty(string str, string? paramName, string? message)
{
if (string.IsNullOrEmpty(str))
throw new ArgumentNullException(paramName: paramName, message: message);
str = str.Trim();
if (string.IsNullOrEmpty(str))
throw new ArgumentNullException(paramName: paramName, message: message);
}
[DebuggerStepThrough]
public static void IsModelStateValid(Microsoft.AspNetCore.Mvc.ModelBinding.ModelStateDictionary modelState, string? message = null)
{
if (modelState.IsValid) return;
if (string.IsNullOrWhiteSpace(message) == false)
{
throw new InvalidOperationException(message);
}
var allErrors = modelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
throw new InvalidOperationException(allErrors.Join(Environment.NewLine));
}
[DebuggerStepThrough]
public static void IsTrue(bool argument, string message)
{
if (argument == false)
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNotTrue(bool argument, string message)
{
if (argument == true)
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNull([AllowNull][NotNull] object argument, string message)
{
if (argument != null)
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNotNull([AllowNull][NotNull] object argument, string message)
{
if (argument == null)
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNullOrEmpty(Guid? argument, string message)
{
if (argument.HasValue == false || argument.Value.Equals(Guid.Empty))
{
return;
}
throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IsEmpty(Guid argument, string message)
{
if (argument.Equals(Guid.Empty))
{
return;
}
throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IsNotNullOrEmpty([AllowNull][NotNull]Guid? argument, string message)
{
if (argument.HasValue && argument.Value.Equals(Guid.Empty) == false)
{
return;
}
throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IsNotEmpty(Guid argument, string message)
{
if (argument.Equals(Guid.Empty) == false)
{
return;
}
throw new InvalidOperationException(message);
}
[DebuggerStepThrough]
public static void IsNullOrEmpty<TEntity>([AllowNull][NotNull] IEnumerable<TEntity> argument, string message)
{
if (argument.IsNullOrEmpty() == false)
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNotNullOrEmpty<TEntity>([AllowNull][NotNull] IEnumerable<TEntity> argument, string message)
{
if (argument.IsNullOrEmpty())
{
throw new InvalidOperationException(message);
}
}
/// <summary>
/// Checks whether argument is of specified type
/// Throws InvalidOperationException if argument is of wrong type
/// </summary>
/// <param name="argument">the argument to be checked</param>
/// <param name="type">the Type</param>
/// <param name="message">Implied to contain a {0} placeholder within in order to be replaced with string representation of the <para>type</para> param </param>
[DebuggerStepThrough]
public static void ArgumentIsOfType(object? argument, Type type, string message)
{
Check.ArgumentIsNotNull(argument, "argument");
Check.ArgumentIsNotNull(type, "type");
if (type != argument.GetType())
{
throw new InvalidOperationException(string.Format(message, type.ToString()));
}
}
[DebuggerStepThrough]
public static void IsNotNullOrWhiteSpace([AllowNull][NotNull] string? value, string message)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new InvalidOperationException(message);
}
}
[DebuggerStepThrough]
public static void IsNullOrWhiteSpace([AllowNull][NotNull] string? value, string message)
{
if (string.IsNullOrWhiteSpace(value) == false)
{
throw new InvalidOperationException(message);
}
}
}
}

View File

@ -0,0 +1,36 @@
using Microsoft.Extensions.DependencyInjection;
using Kit.Helpers.Log;
using Kit.Helpers.Repository.Xml;
using Kit.Helpers.Service;
namespace Kit.Helpers.Config;
public static class DependencyInjectionXmlUpdateItems
{
public static IServiceCollection AddModuleXmlVersionService(this IServiceCollection services)
{
services.AddSingleton<IXmlFileVersionRepository, XmlFileVersionRepository>();
services.AddSingleton<IXmlVersionService, XmlVersionService>();
services.RegisterUpdateItemsViaReflection();
return services;
}
private static void RegisterUpdateItemsViaReflection(this IServiceCollection services)
{
var interfaceType = typeof(IXmlVersionUpdateItem);
var callStackLog = new CallStackLog();
callStackLog.BeginCall("Get Assemblies");
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
callStackLog.EndCall();
callStackLog.BeginCall("Get All Implementations");
IEnumerable<Type> implementationTypes = assemblies.SelectMany(x => x.GetTypes()).Where(t => interfaceType.IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract);
callStackLog.EndCall();
var message = callStackLog.LogMessage;
implementationTypes.ForEach(implementationType => services.AddTransient(interfaceType, implementationType));
}
}

View File

@ -0,0 +1,81 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
namespace Kit.Helpers.Config;
public class StaticFileConfigBuilder<TIApplicationBuilder>
where TIApplicationBuilder : IApplicationBuilder
{
private readonly List<string> _contentModules;
private Action<StaticFileResponseContext> _onPrepareResponse;
internal StaticFileConfigBuilder()
{
_contentModules = new List<string>();
_onPrepareResponse = ctx =>
{
ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=3600");
};
}
public StaticFileConfigBuilder<TIApplicationBuilder> WithContentModule(string moduleName)
{
_contentModules.Add(moduleName);
return this;
}
public StaticFileConfigBuilder<TIApplicationBuilder> WithOnPrepareResponse(Action<StaticFileResponseContext> ctx)
{
_onPrepareResponse = ctx;
return this;
}
internal TIApplicationBuilder Apply(TIApplicationBuilder app)
{
IWebHostEnvironment env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
// основной wwwroot
List<IFileProvider> fileProviderItems = new List<IFileProvider> { env.WebRootFileProvider };
if (_contentModules.Count > 0)
{
#if DEBUG
fileProviderItems.Add(new PhysicalFileProvider(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")));
#else
fileProviders.AddRange(_contentModules.Select(moduleName => new PhysicalFileProvider(Path.Combine(_env.ContentRootPath, "wwwroot", "_content", moduleName))));
#endif
}
var fileProvider = new CompositeFileProvider(fileProviderItems);
env.WebRootFileProvider = fileProvider;
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = fileProvider,
RequestPath = "",
OnPrepareResponse = _onPrepareResponse
});
return app;
}
}
public static class StaticFileConfigBuilderExt
{
public static TIApplicationBuilder UseCustomStaticFiles<TIApplicationBuilder>(this TIApplicationBuilder app, Action<StaticFileConfigBuilder<TIApplicationBuilder>> options)
where TIApplicationBuilder : IApplicationBuilder
{
var config = new StaticFileConfigBuilder<TIApplicationBuilder>();
if (options != null)
options(config);
config.Apply(app);
return app;
}
}

View File

@ -0,0 +1,95 @@
using Microsoft.Extensions.Configuration;
namespace Kit.Helpers
{
public interface IConfigurationItem
{
DateTime FillDate { get; set; }
void Init(IConfiguration configuration);
}
public class ConfigurationHelper
{
private readonly string _prefix;
private readonly IConfiguration _configuration;
public ConfigurationHelper(IConfiguration configuration) : this(configuration, string.Empty)
{
_configuration = configuration;
}
public ConfigurationHelper(IConfiguration configuration, string prefix)
{
_configuration = configuration;
_prefix = prefix;
}
private string CreateKey(string itemKey)
{
return string.IsNullOrEmpty(_prefix)
? itemKey.ToString()
: $"{_prefix}:{itemKey}";
}
public string GetConnectionString(string name, bool required = true)
{
string connectionStringName = this.CreateKey(name);
string connectionString = _configuration.GetConnectionString(connectionStringName);
if (required)
{
Check.StringArgumentIsNotNullOrEmpty(connectionString, null, $"connection string not set in config. name={connectionStringName}");
}
return connectionString;
}
public string GetAppSettingValue(object settingsItemKey, bool required = true, string? defaultValue = null)
{
// create key
string appSettingsKey = this.CreateKey(settingsItemKey?.ToString() ?? string.Empty);
// read
string? configurationValue = _configuration[appSettingsKey] ?? defaultValue;
// validate
if (required && string.IsNullOrWhiteSpace(configurationValue))
{
throw new Exception($"{appSettingsKey} not set in appSettings section");
}
return configurationValue ?? string.Empty;
}
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="required"></param>
/// <exception cref="InvalidOperationException"></exception>
/// <returns>The <see cref="IConfigurationSection"/>.</returns>
public IConfigurationSection GetSection(string name, bool required = true)
{
// create key
string appSettingsKey = this.CreateKey(name.ToString());
if (required)
{
return _configuration.GetRequiredSection(appSettingsKey);
}
else
{
return _configuration.GetSection(appSettingsKey);
}
}
public TEntity? Get<TEntity>(string name)
{
// create key
string key = this.CreateKey(name.ToString());
return _configuration.GetSection(key).Get<TEntity>();
}
}
}

View File

@ -0,0 +1,106 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using System.Reflection;
public static class ControllerExtensions
{
public static async Task<IViewComponentResult> InvokeViewComponentWithModelBindingAsync(
this Controller controller,
Type viewComponentType)
{
var viewComponent = GetViewComponentInstance(controller, viewComponentType);
var invokeMethod = GetInvokeMethod(viewComponentType);
var parameters = invokeMethod.GetParameters();
var parameterValues = await BindParametersAsync(controller, parameters);
return await ExecuteViewComponentAsync(viewComponent, invokeMethod, parameterValues);
}
private static ViewComponent GetViewComponentInstance(Controller controller, Type viewComponentType)
{
var instance = controller.HttpContext.RequestServices.GetService(viewComponentType) as ViewComponent;
if (instance == null)
throw new InvalidOperationException($"ViewComponent {viewComponentType.Name} not found.");
return instance;
}
private static MethodInfo GetInvokeMethod(Type viewComponentType)
{
var method = viewComponentType.GetMethod("InvokeAsync") ?? viewComponentType.GetMethod("Invoke");
if (method == null)
throw new InvalidOperationException("ViewComponent does not have Invoke/InvokeAsync method.");
return method;
}
private static async Task<object[]> BindParametersAsync(Controller controller, ParameterInfo[] parameters)
{
var parameterValues = new object[parameters.Length];
var modelBindingContexts = new DefaultModelBindingContext[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
{
var parameter = parameters[i];
var bindingContext = CreateBindingContextAsync(controller, parameter)
.GetAwaiter()
.GetResult();
var binder = GetModelBinder(controller, parameter);
await binder.BindModelAsync(bindingContext);
parameterValues[i] = bindingContext.Result.IsModelSet
? bindingContext.Result.Model
: parameter.DefaultValue;
}
return parameterValues;
}
private static async Task<DefaultModelBindingContext> CreateBindingContextAsync(Controller controller, ParameterInfo parameter)
{
var valueProviders = new List<IValueProvider>();
var factoryContext = new ValueProviderFactoryContext(controller.ControllerContext);
foreach (var factory in controller.ControllerContext.ValueProviderFactories)
{
await factory.CreateValueProviderAsync(factoryContext);
}
return new DefaultModelBindingContext
{
ActionContext = controller.ControllerContext,
ModelState = controller.ModelState,
ModelMetadata = controller.MetadataProvider.GetMetadataForType(parameter.ParameterType),
FieldName = parameter.Name,
ModelName = parameter.Name,
ValueProvider = new CompositeValueProvider(valueProviders)
};
}
private static IModelBinder GetModelBinder(Controller controller, ParameterInfo parameter)
{
var metadata = controller.MetadataProvider.GetMetadataForType(parameter.ParameterType);
return controller.ModelBinderFactory.CreateBinder(new ModelBinderFactoryContext
{
Metadata = metadata,
BindingInfo = new BindingInfo { BindingSource = BindingSource.ModelBinding }
});
}
private static async Task<IViewComponentResult> ExecuteViewComponentAsync(
ViewComponent viewComponent,
MethodInfo method,
object[] parameters)
{
var result = method.Invoke(viewComponent, parameters);
if (result is Task task)
{
await task;
return (IViewComponentResult)((dynamic)task).Result;
}
return (IViewComponentResult)result;
}
}

View File

@ -0,0 +1,77 @@
namespace Kit.Helpers
{
using System;
using Microsoft.AspNetCore.Http;
public class CookieHelper
{
public static string GetValue(string key)
{
HttpContext context = HttpContextCore.Current;
return GetValue(context, key);
}
public static string GetValue(HttpContext context, string key)
{
String result = String.Empty;
if (context != null && context.Request != null && context.Request.Cookies.ContainsKey(key))
{
return context.Request.Cookies[key];
}
return result;
}
public static void SetValue(string key, string value, DateTime expires)
{
HttpContext context = HttpContextCore.Current;
SetValue(context, key, value, expires);
}
public static void SetValue(HttpContext context, string key, string value, DateTime expires)
{
if (context == null)
{
return;
}
if (context.Request.Cookies.ContainsKey(key))
{
context.Response.Cookies.Delete(key);
}
var cookieOptions = new CookieOptions
{
Expires = expires
};
context.Response.Cookies.Append(key, value, cookieOptions);
}
public static void SetValue(string key, string value)
{
SetValue(key, value, DateTime.MaxValue);
}
public static void SetValue(HttpContext context, string key, string value)
{
SetValue(context, key, value, DateTime.MaxValue);
}
public static void RemoveCookie(string key)
{
HttpContext context = HttpContextCore.Current;
RemoveCookie(context, key);
}
public static void RemoveCookie(HttpContext context, string key)
{
if (context == null)
{
return;
}
CookieOptions cookieOptions = new CookieOptions();
cookieOptions.Expires = DateTime.Now.AddDays(-30);
context.Response.Cookies.Delete(key, cookieOptions);
}
}
}

View File

@ -0,0 +1,577 @@
using System.Data;
using System.Text.Json.Serialization;
namespace Kit.Helpers;
public class OutputValue
{
public object Value;
}
public class CustomType
{
public string Name { get; set; }
public Type Type { get; set; }
public CustomType(string name, Type type)
{
Name = name;
Type = type;
}
}
public class RequestParams
{
public IConnectionString ConnectionString { get; set; } = new ConnectionString();
public string CommandText { get; set; } = string.Empty;
public bool IsStoredProcedure { get; set; } = false;
public bool WithStrictSyntax { get; set; } = false;
public IEnumerable<KeyValuePair<string, object>>? Parameters { get; set; }
public IEnumerable<CustomType>? CustomTypes { get; set; }
}
public class RequestParamsSelect<TEntity> : RequestParams
{
public IEnumerable<Action<IDataRecordSpecified, IList<TEntity>>> Converters { get; set; } = Enumerable.Empty<Action<IDataRecordSpecified, IList<TEntity>>>();
}
public interface ISqlHelper
{
void ExecuteNonQuery(RequestParams requestParams);
object ExecuteScalar(RequestParams requestParams);
TResult ExecuteScalar<TResult>(RequestParams requestParams);
IEnumerable<TEntity> ExecuteSelectMany<TEntity>(RequestParamsSelect<TEntity> requestParams);
IEnumerableWithPage<TEntity> ExecuteSelectManyWithPage<TEntity>(RequestParamsSelect<TEntity> requestParams);
IEnumerableWithOffer<TEntity> ExecuteSelectManyWithOffer<TEntity>(RequestParamsSelect<TEntity> requestParams);
}
public interface ISqlHelperFluent
{
ISqlHelperFluent AsStoredProcedure();
ISqlHelperFluent AsSqlText();
ISqlHelperFluent WithStrictSyntax();
ISqlHelperFluent AddCustomType(string name, Type type);
ISqlHelperFluent AddCustomType<TType>(string name);
ISqlHelperFluent AddFilter(string name, object value);
ISqlHelperFluent AddFilter(string name, string value);
ISqlHelperFluent AddParameter(string name, object value);
ISqlHelperFluent AddParameter(string name, string value);
ISqlHelperFluent AddParameterNullable(string name, object? value);
ISqlHelperFluent AddParameterIfNotNull(string name, object? value);
ISqlHelperFluent AddOutputParameter(string name, object value);
ISqlHelperFluent AddParameters(IEnumerable<KeyValuePair<string, object>> parameters);
ISqlHelperFluent AddFilters(IEnumerable<KeyValuePair<string, object>> parameters);
void ExecuteNonQuery();
TResult ExecuteScalar<TResult>();
}
public interface ISqlHelperFluent<TEntity>
{
ISqlHelperFluent<TEntity> AsStoredProcedure();
ISqlHelperFluent<TEntity> AsSqlText();
ISqlHelperFluent<TEntity> WithStrictSyntax();
ISqlHelperFluent<TEntity> AddCustomType(string name, Type type);
ISqlHelperFluent<TEntity> AddCustomType<TType>(string name);
ISqlHelperFluent<TEntity> AddFilter(string name, object value);
ISqlHelperFluent<TEntity> AddFilter(string name, string value);
ISqlHelperFluent<TEntity> AddParameter(string name, object value);
ISqlHelperFluent<TEntity> AddParameter(string name, string value);
ISqlHelperFluent<TEntity> AddParameterNullable(string name, object? value);
ISqlHelperFluent<TEntity> AddParameterIfNotNull(string name, object? value);
ISqlHelperFluent<TEntity> AddOutputParameter(string name, object value);
ISqlHelperFluent<TEntity> AddParameters(IEnumerable<KeyValuePair<string, object>> parameters);
ISqlHelperFluent<TEntity> AddFilters(IEnumerable<KeyValuePair<string, object>> parameters);
ISqlHelperFluent<TEntity> AddConverter(Action<IDataRecordSpecified, IList<TEntity>> converter);
IEnumerable<TEntity> ExecuteSelectMany();
IEnumerableWithPage<TEntity> ExecuteSelectManyWithPage();
IEnumerableWithOffer<TEntity> ExecuteSelectManyWithOffer();
}
public enum ConnectionStringType
{
Undefined = 0,
Postgres = 1,
SQLite = 2,
}
public interface IConnectionString
{
ConnectionStringType Type { get; }
string Value { get; }
[JsonIgnore]
object? DatabaseConnection { get; }
}
public class ConnectionString : IConnectionString
{
public ConnectionStringType Type { get; set; } = ConnectionStringType.Undefined;
private string _value = string.Empty;
public string Value
{
get => _value;
set => _value = value.ServerMapPath();
}
[JsonIgnore]
public object? DatabaseConnection { get; set; }
public void CreateAndOpenConnection()
{
switch (Type)
{
case ConnectionStringType.Postgres:
DatabaseConnection = new Npgsql.NpgsqlConnection(Value.CreateSQLiteConnectionString());
(DatabaseConnection as Npgsql.NpgsqlConnection)!.Open();
break;
case ConnectionStringType.SQLite:
DatabaseConnection = new Microsoft.Data.Sqlite.SqliteConnection(Value.CreateSQLiteConnectionString());
(DatabaseConnection as Microsoft.Data.Sqlite.SqliteConnection)!.Open();
break;
default:
throw new NotImplementedException();
}
}
~ConnectionString()
{
if (DatabaseConnection is IDisposable disposable) disposable.Dispose();
DatabaseConnection = null;
}
}
public class SqlHelperFluent : ISqlHelperFluent
{
protected readonly IConnectionString _connectionString;
protected readonly string _sqlText;
protected bool _isStoredProcedure;
protected bool _isOutputParameter;
protected bool _withStrictSyntax;
protected List<CustomType> _customTypes;
protected List<KeyValuePair<string, object>> _parameters;
public SqlHelperFluent(IConnectionString connectionString, string sqlText)
{
_connectionString = connectionString;
_sqlText = sqlText;
_isStoredProcedure = true;
_isOutputParameter = false;
_withStrictSyntax = false;
_customTypes = new List<CustomType>();
_parameters = new List<KeyValuePair<string, object>>();
}
protected ISqlHelper GetSqlHelper()
{
switch (_connectionString.Type)
{
case ConnectionStringType.Postgres:
return Postgres.SqlHelper.Instance;
case ConnectionStringType.SQLite:
return SQLite.SqlHelper.Instance;
default:
throw new NotImplementedException();
}
}
#region fluent methods
public ISqlHelperFluent AsStoredProcedure()
{
_isStoredProcedure = true;
return this;
}
public ISqlHelperFluent AsSqlText()
{
_isStoredProcedure = false;
return this;
}
public ISqlHelperFluent WithStrictSyntax()
{
_withStrictSyntax = true;
return this;
}
public ISqlHelperFluent AddCustomType(string name, Type type)
{
if (type != null)
{
_customTypes.Add(new CustomType(name, type));
}
return this;
}
public ISqlHelperFluent AddCustomType<TType>(string name)
{
_customTypes.Add(new CustomType(name, typeof(TType)));
return this;
}
public ISqlHelperFluent AddFilter(string name, object value)
{
if (value != null)
{
_parameters.Add(new KeyValuePair<string, object>(name, value));
}
return this;
}
public ISqlHelperFluent AddFilter(string name, string value)
{
if (!value.IsNullOrEmpty())
{
_parameters.Add(new KeyValuePair<string, object>(name, value));
}
return this;
}
public ISqlHelperFluent AddParameter(string name, object value)
{
_parameters.Add(new KeyValuePair<string, object>(name, value));
return this;
}
public ISqlHelperFluent AddParameter(string name, string value)
{
_parameters.Add(new KeyValuePair<string, object>(name, value ?? string.Empty));
return this;
}
public ISqlHelperFluent AddParameterNullable(string name, object? value)
{
_parameters.Add(new KeyValuePair<string, object>(name, value ?? DBNull.Value));
return this;
}
public ISqlHelperFluent AddParameterIfNotNull(string name, object? value)
{
if (value != null)
{
_parameters.Add(new KeyValuePair<string, object>(name, value));
}
return this;
}
public ISqlHelperFluent AddOutputParameter(string name, object value)
{
_parameters.Add(new KeyValuePair<string, object>(name, new OutputValue { Value = value ?? DBNull.Value }));
return this;
}
public ISqlHelperFluent AddParameters(IEnumerable<KeyValuePair<string, object>> parameters)
{
_parameters.AddRange(parameters);
return this;
}
public ISqlHelperFluent AddFilters(IEnumerable<KeyValuePair<string, object>> parameters)
{
_parameters.AddRange(parameters.Where(x => x.Value != null));
return this;
}
#endregion
#region finish methods
private RequestParams BuildParams() => new RequestParams
{
ConnectionString = _connectionString,
CommandText = _sqlText,
IsStoredProcedure = _isStoredProcedure,
WithStrictSyntax = _withStrictSyntax,
Parameters = _parameters,
CustomTypes = _customTypes,
};
public void ExecuteNonQuery()
{
GetSqlHelper().ExecuteNonQuery(BuildParams());
}
public TResult ExecuteScalar<TResult>()
{
return GetSqlHelper().ExecuteScalar<TResult>(BuildParams());
}
#endregion
}
public class SqlHelperFluent<TEntity> : SqlHelperFluent, ISqlHelperFluent<TEntity>
{
protected List<Action<IDataRecordSpecified, IList<TEntity>>> _converters;
public SqlHelperFluent(IConnectionString connectionString, string sqlText) : base(connectionString, sqlText)
{
_converters = new List<Action<IDataRecordSpecified, IList<TEntity>>>();
}
#region base fluent methods
public new ISqlHelperFluent<TEntity> AddCustomType(string name, Type type)
{
base.AddCustomType(name, type);
return this;
}
public new ISqlHelperFluent<TEntity> AddCustomType<TType>(string name)
{
base.AddCustomType<TType>(name);
return this;
}
public new ISqlHelperFluent<TEntity> AddFilter(string name, object value)
{
base.AddFilter(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddFilter(string name, string value)
{
base.AddFilter(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddParameter(string name, object value)
{
base.AddParameter(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddParameter(string name, string value)
{
base.AddParameter(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddParameterNullable(string name, object? value)
{
base.AddParameterNullable(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddParameterIfNotNull(string name, object? value)
{
base.AddParameterIfNotNull(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddOutputParameter(string name, object value)
{
base.AddParameter(name, value);
return this;
}
public new ISqlHelperFluent<TEntity> AddParameters(IEnumerable<KeyValuePair<string, object>> parameters)
{
base.AddParameters(parameters);
return this;
}
public new ISqlHelperFluent<TEntity> AddFilters(IEnumerable<KeyValuePair<string, object>> parameters)
{
base.AddFilters(parameters);
return this;
}
public new ISqlHelperFluent<TEntity> AsStoredProcedure()
{
base.AsStoredProcedure();
return this;
}
public new ISqlHelperFluent<TEntity> AsSqlText()
{
base.AsSqlText();
return this;
}
public new ISqlHelperFluent<TEntity> WithStrictSyntax()
{
base.WithStrictSyntax();
return this;
}
#endregion
private RequestParamsSelect<TEntity> BuildParams() => new RequestParamsSelect<TEntity>
{
ConnectionString = _connectionString,
CommandText = _sqlText,
IsStoredProcedure = _isStoredProcedure,
WithStrictSyntax = _withStrictSyntax,
Parameters = _parameters,
CustomTypes = _customTypes,
Converters = _converters,
};
public ISqlHelperFluent<TEntity> AddConverter(Action<IDataRecordSpecified, IList<TEntity>> converter)
{
_converters.Add(converter);
return this;
}
public IEnumerable<TEntity> ExecuteSelectMany()
{
return GetSqlHelper().ExecuteSelectMany(BuildParams());
}
public IEnumerableWithPage<TEntity> ExecuteSelectManyWithPage()
{
return GetSqlHelper().ExecuteSelectManyWithPage(BuildParams());
}
public IEnumerableWithOffer<TEntity> ExecuteSelectManyWithOffer()
{
return GetSqlHelper().ExecuteSelectManyWithOffer(BuildParams());
}
}
public static class SqLiteHelperFluentExtentions
{
public static ISqlHelperFluent PrepareExecute(this IConnectionString connectionString, string sqlText)
{
return new SqlHelperFluent(connectionString, sqlText);
}
public static ISqlHelperFluent<TEntity> PrepareExecute<TEntity>(this IConnectionString connectionString, string sqlText)
{
return new SqlHelperFluent<TEntity>(connectionString, sqlText);
}
}
public interface IDataRecordSpecified
{
IDataRecord Src { get; }
Npgsql.INpgsqlNameTranslator NameTranslator { get; }
TValue Convert<TValue>(object value);
}
internal class DataRecordSpecified : IDataRecordSpecified
{
public IDataRecord Src { get; }
public Npgsql.INpgsqlNameTranslator NameTranslator { get; }
public virtual TValue Convert<TValue>(object value)
{
return (TValue)value;
}
public DataRecordSpecified(IDataReader dataReader, Npgsql.INpgsqlNameTranslator nameTranslator)
{
Src = dataReader;
NameTranslator = nameTranslator;
}
}
public static class SqlHelperExtensions
{
/// <summary>
/// Проверка на наличие столбца в запросе
/// </summary>
/// <param name="reader"></param>
/// <param name="name"></param>
/// <returns></returns>
public static bool HasColumn(this IDataRecordSpecified reader, string name)
{
string nameTranslated = reader.NameTranslator.TranslateMemberName(name).ToLower();
for (int n = 0; n < reader.Src.FieldCount; n++)
{
string fieldNameTranslated = reader.NameTranslator.TranslateMemberName(reader.Src.GetName(n)).ToLower();
if (fieldNameTranslated == nameTranslated) return true;
}
return false;
}
private static string GetTranslatedName(this IDataRecordSpecified record, string fieldName) => record.NameTranslator.TranslateMemberName(fieldName);
private static TValue? TryGet<TValue>(this IDataRecordSpecified record, string fieldName, bool isStrict)
{
fieldName = GetTranslatedName(record, fieldName);
object value = record.Src[fieldName];
if (value == DBNull.Value)
{
if (isStrict)
throw new InvalidCastException(string.Format("{0} {1} нельзя привести к {2}", fieldName, typeof(DBNull).FullName, typeof(TValue).FullName));
return default;
}
try
{
return record.Convert<TValue>(value);
}
catch (InvalidCastException ex)
{
throw new InvalidCastException(string.Format("{0} {1} нельзя привести к {2}", fieldName, value.GetType().FullName, typeof(TValue).FullName), ex);
}
}
public static TValue Get<TValue>(this IDataRecordSpecified record, string fieldName) => record.TryGet<TValue>(fieldName, isStrict: true)!;
public static TValue GetN<TValue>(this IDataRecordSpecified record, string fieldName) => record.TryGet<TValue>(fieldName, isStrict: false)!;
internal static IList<TEntity> CreateList<TEntity>(this SelectType extraSelectType)
{
switch (extraSelectType)
{
case SelectType.Page:
return new ListWithPage<TEntity>();
case SelectType.Offer:
return new ListWithOffer<TEntity>();
case SelectType.Default:
default:
return new List<TEntity>();
}
}
internal static void ProcessExtraSelect<TEntity>(this IDataReader reader, Npgsql.INpgsqlNameTranslator translator, IList<TEntity> selectResult, SelectType extraSelectType = SelectType.Default)
{
IDataRecordSpecified readerSpecified = new DataRecordSpecified(reader, translator);
switch (extraSelectType)
{
case SelectType.Page:
{
var result = (IEnumerableWithPage<TEntity>)selectResult;
// считывание данных постраничного вывода
while (reader.Read())
{
if (readerSpecified.HasColumn("FilteredRows")) result.FilteredRows = readerSpecified.Get<int>("filteredRows");
if (readerSpecified.HasColumn("TotalRows")) result.TotalRows = readerSpecified.Get<int>("totalRows");
if (readerSpecified.HasColumn("PageNo")) result.PageNo = readerSpecified.Get<int>("pageNo");
if (readerSpecified.HasColumn("PageSize")) result.PageSize = readerSpecified.Get<int>("pageSize");
if (readerSpecified.HasColumn("Sort")) result.Sort = readerSpecified.Get<string>("sort");
if (readerSpecified.HasColumn("Dir")) result.Dir = readerSpecified.Get<string>("dir");
}
}
break;
case SelectType.Offer:
{
var result = (IEnumerableWithOffer<TEntity>)selectResult;
// считывание данных постраничного вывода
while (reader.Read())
{
if (readerSpecified.HasColumn("FilteredRows")) result.FilteredRows = readerSpecified.Get<int>("FilteredRows");
if (readerSpecified.HasColumn("TotalRows")) result.TotalRows = readerSpecified.Get<int>("TotalRows");
if (readerSpecified.HasColumn("Start")) result.Start = readerSpecified.Get<int>("Start");
if (readerSpecified.HasColumn("Length")) result.Lenght = readerSpecified.Get<int>("Length");
if (readerSpecified.HasColumn("Sort")) result.Sort = readerSpecified.Get<string>("Sort");
if (readerSpecified.HasColumn("Dir")) result.Dir = readerSpecified.Get<string>("Dir");
}
}
break;
case SelectType.Default:
default:
break;
}
}
}
internal enum SelectType
{
Default,
Page,
Offer
}

View File

@ -0,0 +1,264 @@
using Npgsql;
using Npgsql.NameTranslation;
using System.Data;
using System.Text;
namespace Kit.Helpers.Postgres;
public class SqlHelper : ISqlHelper
{
private SqlHelper() { }
public INpgsqlNameTranslator Translator { get; } = new NpgsqlSnakeCaseNameTranslator();
public static SqlHelper Instance { get; } = new SqlHelper();
private IEnumerable<KeyValuePair<string, object>>? UpdateParamNames(IEnumerable<KeyValuePair<string, object>>? @params)
{
var result = new List<KeyValuePair<string, object>>();
if (@params.IsNullOrEmpty()) return result;
@params.ForEach(_param =>
{
string key = "_" + Translator.TranslateMemberName(_param.Key.Remove(" ")!.Trim('@', '_'));
result.Add(new KeyValuePair<string, object>(key, _param.Value));
});
return result;
}
private string GenerateParamsForQuery(IEnumerable<KeyValuePair<string, object>>? @params)
{
var sb = new StringBuilder();
if (@params.IsNullOrEmpty() == false)
{
for (int i = 0; i < @params.Count(); i++)
{
string key = @params.ElementAt(i).Key;
sb.Append(key).Append(" => :").Append(key);
if (i < @params.Count() - 1)
{
sb.Append(", ");
}
}
}
return sb.ToString();
}
private string TranslateName(string name, bool withStrictSyntax)
{
if (withStrictSyntax)
{
return name;
}
return name.Remove("[")!.Remove("]")!.Split('.').Select(x => Translator.TranslateMemberName(x)).Join(".");
}
private NpgsqlCommand AppendTypes(NpgsqlCommand command, RequestParams requestParams)
{
requestParams.CustomTypes?.ForEach(x =>
{
command.Connection?.TypeMapper.MapComposite(x.Type, TranslateName(x.Name, requestParams.WithStrictSyntax), Translator);
});
return command;
}
private NpgsqlCommand CreateCommand(RequestParams requestParams)
{
string sql = TranslateName(requestParams.CommandText, requestParams.WithStrictSyntax);
bool hasParameters = requestParams.Parameters != null && requestParams.Parameters.Count() > 0;
var parameters = UpdateParamNames(requestParams.Parameters);
if (requestParams.ConnectionString.Value.IsNullOrEmpty()) throw new ArgumentNullException("connectionString");
if (sql.IsNullOrEmpty()) throw new ArgumentNullException("sql");
var connection = new NpgsqlConnection(requestParams.ConnectionString.Value);
NpgsqlCommand command = connection.CreateCommand();
command.CommandTimeout = connection.ConnectionTimeout;
if (requestParams.IsStoredProcedure)
{
sql = $"select * from {sql}({GenerateParamsForQuery(parameters)});";
}
command.CommandType = System.Data.CommandType.Text;
command.CommandText = sql;
if (parameters != null && parameters.Any())
{
foreach (var keyValuePair in parameters)
{
string key = keyValuePair.Key.Remove("@").Remove(" ").ToLower();
if (keyValuePair.Value is Guid)
{
command.Parameters.AddWithValue(key.ToLower(), keyValuePair.Value ?? Guid.Empty);
}
else
{
command.Parameters.AddWithValue(key.ToLower(), keyValuePair.Value ?? DBNull.Value);
}
}
}
return command;
}
public void ExecuteNonQuery(RequestParams requestParams)
{
NpgsqlCommand command = CreateCommand(requestParams);
using (command.Connection)
{
command.Connection.Open();
AppendTypes(command, requestParams);
command.ExecuteNonQuery();
command.Connection.Close();
}
}
public object ExecuteScalar(RequestParams requestParams)
{
object returnScalar;
NpgsqlCommand command = CreateCommand(requestParams);
using (command.Connection)
{
command.Connection.Open();
AppendTypes(command, requestParams);
returnScalar = command.ExecuteScalar();
command.Connection.Close();
}
return returnScalar;
}
public TResult ExecuteScalar<TResult>(RequestParams requestParams)
{
object returnScalar;
NpgsqlCommand command = CreateCommand(requestParams);
using (command.Connection)
{
command.Connection.Open();
AppendTypes(command, requestParams);
returnScalar = command.ExecuteScalar();
command.Connection.Close();
}
return (TResult)returnScalar;
}
private IEnumerable<TEntity> ExecuteSelectMany<TEntity>(SelectType extraSelectType, RequestParamsSelect<TEntity> requestParams)
{
if (requestParams.Converters == null || requestParams.Converters.Count() < 1) throw new ArgumentNullException("converters");
NpgsqlCommand command = CreateCommand(requestParams);
command.CommandTimeout = command.Connection.ConnectionTimeout;
IList<TEntity> selectResult = extraSelectType.CreateList<TEntity>();
using (command.Connection)
{
command.Connection.Open();
AppendTypes(command, requestParams);
using (NpgsqlTransaction tran = command.Connection.BeginTransaction())
{
using (IDataReader readerFirst = command.ExecuteReader())
{
var dataTypeName = readerFirst.GetDataTypeName(0); // Для первого столбца
if (dataTypeName == "refcursor")
{
ProcessForCursors(readerFirst, command, requestParams.Converters, selectResult, extraSelectType);
}
else
{
ProcessForSimple(readerFirst, requestParams.Converters, selectResult, extraSelectType);
}
}
tran.Commit();
}
}
return selectResult;
}
private void ProcessForSimple<TEntity>(IDataReader reader, IEnumerable<Action<IDataRecordSpecified, IList<TEntity>>> converters, IList<TEntity> selectResult, SelectType extraSelectType = SelectType.Default)
{
IDataRecordSpecified recordSpecified = new DataRecordSpecified(reader, Translator);
foreach (var converter in converters)
{
while (reader.Read())
{
converter(recordSpecified, selectResult);
}
reader.NextResult();
}
reader.ProcessExtraSelect(Translator, selectResult, extraSelectType);
}
private void CheckCursorCount(int cursorCount, int converterCount, SelectType extraSelectType)
{
switch (extraSelectType)
{
case SelectType.Page:
case SelectType.Offer:
Check.IsTrue(cursorCount >= converterCount + 1, "Количество конвертеров больше количества возвращаемых курсоров");
break;
case SelectType.Default:
default:
Check.IsTrue(cursorCount >= converterCount, "Количество конвертеров больше количества возвращаемых курсоров");
break;
}
}
private void ProcessForCursors<TEntity>(IDataReader readerFirst, NpgsqlCommand command, IEnumerable<Action<IDataRecordSpecified, IList<TEntity>>> converters, IList<TEntity> selectResult, SelectType extraSelectType = SelectType.Default)
{
var cursors = new List<string>();
while (readerFirst.Read())
{
cursors.Add(readerFirst[0].ToString()!);
}
readerFirst.Close();
CheckCursorCount(cursors.Count, converters.Count(), extraSelectType);
for (int i = 0; i < converters.Count(); i++)
{
command.CommandText = $"fetch all in \"{cursors[i]}\"";
command.CommandType = CommandType.Text;
using (NpgsqlDataReader reader = command.ExecuteReader())
{
IDataRecordSpecified recordSpecified = new DataRecordSpecified(reader, Translator);
while (reader.Read())
{
converters.ElementAt(i)(recordSpecified, selectResult);
}
}
}
if (extraSelectType != SelectType.Default)
{
// считывание данных постраничного вывода
command.CommandText = $"fetch all in \"{cursors.Last()}\"";
command.CommandType = CommandType.Text;
using (NpgsqlDataReader reader = command.ExecuteReader())
{
reader.ProcessExtraSelect(Translator, selectResult, extraSelectType);
}
}
}
public IEnumerable<TEntity> ExecuteSelectMany<TEntity>(RequestParamsSelect<TEntity> requestParams) => ExecuteSelectMany(SelectType.Default, requestParams);
public IEnumerableWithPage<TEntity> ExecuteSelectManyWithPage<TEntity>(RequestParamsSelect<TEntity> requestParams) => (ListWithPage<TEntity>)ExecuteSelectMany(SelectType.Page, requestParams);
public IEnumerableWithOffer<TEntity> ExecuteSelectManyWithOffer<TEntity>(RequestParamsSelect<TEntity> requestParams) => (ListWithOffer<TEntity>)ExecuteSelectMany(SelectType.Offer, requestParams);
}
public static class SqlHelperExtensions
{
public static TValue Get<TValue>(this IDataRecordSpecified record, string fieldName) => record.Get<TValue>(fieldName);
public static TValue GetN<TValue>(this IDataRecordSpecified record, string fieldName) => record.GetN<TValue>(fieldName);
}

View File

@ -0,0 +1,408 @@
using Microsoft.Data.Sqlite;
using Npgsql.NameTranslation;
using System.Data;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Text.RegularExpressions;
namespace Kit.Helpers.SQLite;
internal class SQLiteDataRecordSpecified : DataRecordSpecified
{
public SQLiteDataRecordSpecified(IDataReader dataReader, Npgsql.INpgsqlNameTranslator nameTranslator) : base(dataReader, nameTranslator) { }
public override TValue Convert<TValue>(object value) => SQliteConvert.Convert<TValue>(value);
}
internal static class SQliteConvert
{
private static readonly IEnumerable<ConverterItem> _typeConverters = new List<ConverterItem>
{
new ConverterItem(typeof(bool), typeof(long), (value) => System.Convert.ToBoolean(value)),
new ConverterItem(typeof(byte), typeof(long), (value) => System.Convert.ToByte(value)),
new ConverterItem(typeof(char), typeof(string), (value) => System.Convert.ToChar(value.ToString()!)),
new ConverterItem(typeof(DateOnly), typeof(string), (value) => DateOnly.ParseExact(value.ToString()!, "yyyy-MM-dd")),
new ConverterItem(typeof(DateTime), typeof(string), (value) => DateTime.ParseExact(value.ToString()!, "yyyy-MM-dd HH:mm:ss.FFFFFFF", CultureInfo.InvariantCulture)),
new ConverterItem(typeof(DateTimeOffset), typeof(string), (value) => DateTimeOffset.ParseExact(value.ToString()!, "yyyy-MM-dd HH:mm:ss.FFFFFFFzzz", CultureInfo.InvariantCulture)),
new ConverterItem(typeof(decimal), typeof(string), (value) => System.Convert.ToDecimal(value.ToString()!)),
new ConverterItem(typeof(double), typeof(double), (value) => value),
new ConverterItem(typeof(Guid), typeof(string), (value) => Guid.Parse(value.ToString()!)),
new ConverterItem(typeof(short), typeof(long), (value) => System.Convert.ToInt16(value)),
new ConverterItem(typeof(int), typeof(long), (value) => System.Convert.ToInt32(value)),
new ConverterItem(typeof(long), typeof(long), (value) => value),
new ConverterItem(typeof(sbyte), typeof(long), (value) => System.Convert.ToSByte(value)),
new ConverterItem(typeof(float), typeof(double), (value) => System.Convert.ToSingle(value)),
new ConverterItem(typeof(string), typeof(string), (value) => value.ToString()!),
new ConverterItem(typeof(TimeOnly), typeof(string), (value) => TimeOnly.ParseExact(value.ToString()!, "HH:mm:ss.fffffff", CultureInfo.InvariantCulture)),
new ConverterItem(typeof(TimeSpan), typeof(string), (value) => TimeSpan.ParseExact(value.ToString()!, "d.hh:mm:ss.fffffff", CultureInfo.InvariantCulture)),
new ConverterItem(typeof(ushort), typeof(long), (value) => System.Convert.ToUInt16(value)),
new ConverterItem(typeof(uint), typeof(long), (value) => System.Convert.ToUInt32(value)),
new ConverterItem(typeof(ulong), typeof(long), (value) => System.Convert.ToUInt64(value)),
};
private class ConverterItem
{
public Type Tgt { get; set; }
public Type Src { get; set; }
public Func<object, object> Func { get; set; }
public ConverterItem(Type tgt, Type src, Func<object, object> func)
{
this.Tgt = tgt;
this.Src = src;
this.Func = func;
}
}
public static TValue Convert<TValue>(object value)
{
Type valueType = value.GetType();
Type targetType = typeof(TValue);
return (TValue)(_typeConverters.FirstOrDefault(x => x.Tgt == targetType && x.Src == valueType)?.Func(value) ?? value);
}
}
public class SqlHelper : ISqlHelper
{
private readonly ILockService<string> _lockService = new LockService<string>();
private SqlHelper() { }
public Npgsql.INpgsqlNameTranslator Translator { get; } = new NpgsqlSnakeCaseNameTranslator();
public static SqlHelper Instance { get; } = new SqlHelper();
private List<KeyValuePair<string, object>> UpdateParamNames(RequestParams requestParams)
{
var result = new List<KeyValuePair<string, object>>();
if (requestParams.Parameters.IsNullOrEmpty()) return result;
requestParams.Parameters.ForEach(_param =>
{
string key = ":" + (requestParams.WithStrictSyntax ? _param.Key : Translator.TranslateMemberName(_param.Key.Remove(" ")!.Trim('@', '_')));
result.Add(new KeyValuePair<string, object>(key, _param.Value));
});
return result;
}
private SqliteConnection CreateConnection(IConnectionString fileName)
{
var conn = new SqliteConnection(fileName.Value.CreateSQLiteConnectionString());
conn.Open();
return conn;
}
private Func<string, string, string> _funcStrToIntJsonArr = (string input, string delimiter) =>
{
if (string.IsNullOrWhiteSpace(input))
{
return new List<int>().ToJSON();
}
if (string.IsNullOrWhiteSpace(delimiter))
{
delimiter = ",";
}
HashSet<int> ints = input.Split(delimiter, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(x => int.TryParse(x.Trim(), out int result) ? result : 0).ToHashSet();
return ints.ToJSON();
};
private class SQLiteProcedureParam
{
public string Name { get; set; } = string.Empty;
public string Type { get; set; } = string.Empty;
public bool IsOptional { get; set; }
[MemberNotNullWhen(true, "DefaultValueParsed")]
public bool HasDefault { get; set; }
public string DefaultValue { get; set; } = string.Empty;
public SqliteType TypeParsed { get; set; } = SqliteType.Blob;
public object? DefaultValueParsed { get; set; }
}
private void ValidateAndCompleteParams(string procedureName, string procedureParams, string procedureText, List<KeyValuePair<string, object>> parametersUpdated)
{
var errors = new List<string>();
IEnumerable<string> textParams = Regex.Matches(procedureText, @"(:\w+)").Select(x => x.Value.ToLower()).Distinct().ToList();
var paramParsedList = new List<SQLiteProcedureParam>();
procedureParams.Split(",", StringSplitOptions.TrimEntries).ForEach(x =>
{
// Обязательный, без значения по умолчанию
var regexReq = new Regex(@"^(:\w+)\s+(\S+)$");
// Необязательный, по умолчанию null
var regexOpt = new Regex(@"^(:\w+)\s+(\S+)(?i:\s*=\s*|\s+default\s+)(?i:null)$");
// Необязательный, указано значение по умолчанию
var regexOptW = new Regex(@"^(:\w+)\s+(\S+)(?i:\s*=\s*|\s+default\s+)(.*)");
Match match = null!;
var param = new SQLiteProcedureParam();
if ((match = regexReq.Match(x)).Success)
{
param.Name = match.Groups[1].Value;
param.Type = match.Groups[2].Value;
param.IsOptional = false;
param.HasDefault = false;
param.DefaultValue = string.Empty;
}
else if ((match = regexOpt.Match(x)).Success)
{
param.Name = match.Groups[1].Value;
param.Type = match.Groups[2].Value;
param.IsOptional = true;
param.HasDefault = false;
param.DefaultValue = string.Empty;
}
else if ((match = regexOptW.Match(x)).Success)
{
param.Name = match.Groups[1].Value;
param.Type = match.Groups[2].Value;
param.IsOptional = true;
param.HasDefault = true;
param.DefaultValue = match.Groups[3].Value;
}
else
{
errors.Add($"Invalid parameter: {x}");
return;
}
param.Name = param.Name.ToLower();
try
{
param.TypeParsed = param.Type.ParseToEnum<SqliteType>(ignoreCase: true);
}
catch
{
errors.Add($"parameter '{param.Name}': invalid type ({param.Type})");
return;
}
try
{
if (param.HasDefault)
{
switch (param.TypeParsed)
{
case SqliteType.Integer:
param.DefaultValueParsed = int.Parse(param.DefaultValue);
break;
case SqliteType.Real:
param.DefaultValueParsed = double.Parse(param.DefaultValue);
break;
case SqliteType.Text:
if (param.DefaultValue.StartsWith('\'') == false || param.DefaultValue.EndsWith('\'') == false)
throw new Exception();
param.DefaultValueParsed = param.DefaultValue.Trim('\'').Replace("''", "'");
if (((string)param.DefaultValueParsed).IndexOf("''") != -1)
throw new Exception();
break;
case SqliteType.Blob:
default:
break;
}
}
}
catch
{
errors.Add($"parameter '{param.Name}': invalid default value ({param.DefaultValue})");
param.DefaultValueParsed = null;
return;
}
paramParsedList.Add(param);
});
IEnumerable<string> delacrationDuplicates = paramParsedList.GroupBy(x => x.Name.ToLower()).Where(x => x.Count() > 1).Select(x => x.Key).ToList();
if (delacrationDuplicates.Count() > 0)
{
errors.AddRange(delacrationDuplicates.Select(x => $"There are duplicates of the parameter declaration: {x}"));
}
IEnumerable<string> notDescribedParams = textParams.Where(x => paramParsedList.Any(y => y.Name == x) == false).ToList();
if (notDescribedParams.Count() > 0)
{
errors.AddRange(notDescribedParams.Select(x => $"parameter not declared: {x}"));
}
if (errors.Count > 0)
{
throw new Exception(errors.Join(Environment.NewLine));
}
// Ловим неуказанные обязательные параметры
IEnumerable<string> notSendedRequired = paramParsedList.Where(x => x.IsOptional == false && parametersUpdated.Any(y => y.Key == x.Name) == false).Select(x => x.Name).ToList();
if (notSendedRequired.Count() > 0)
{
errors.AddRange(notSendedRequired.Select(x => $"required parameter is not sended: {x}"));
}
if (errors.Count > 0)
{
throw new Exception(errors.Join(Environment.NewLine));
}
// Ловим неуказанные необязательные параметры
paramParsedList.Where(x => x.IsOptional && parametersUpdated.Any(y => y.Key == x.Name) == false).ForEach(x =>
{
if (x.HasDefault)
{
parametersUpdated.Add(new KeyValuePair<string, object>(x.Name, x.DefaultValueParsed));
}
else
{
parametersUpdated.Add(new KeyValuePair<string, object>(x.Name, DBNull.Value));
}
});
}
private void GetStoredProcedureContent(SqliteCommand command, string input, out string procedureName, out string procedureParams, out string procedureText)
{
string oldText = command.CommandText;
CommandType oldType = command.CommandType;
command.CommandType = CommandType.Text;
command.CommandText = "select procedure_name, params, text from procedures where procedure_name = :procedure_name";
command.Parameters.AddWithValue(":procedure_name", input);
using (var reader = command.ExecuteReader())
{
if (reader.Read() == false)
{
throw new Exception($"procedure '{input}' not found");
}
var readerD = new SQLiteDataRecordSpecified(reader, Translator);
procedureName = readerD.Get<string>("ProcedureName");
procedureParams = readerD.Get<string>("Params");
procedureText = readerD.Get<string>("Text");
if (procedureName != input)
{
throw new Exception($"invalid procedure name (requested: {input}; received: {procedureName})");
}
}
command.CommandText = oldText;
command.CommandType = oldType;
command.Parameters.Clear();
}
private string GetCommandText(RequestParams requestParams)
{
if (requestParams.WithStrictSyntax)
{
return requestParams.CommandText;
}
return requestParams.CommandText.Remove("[")!.Remove("]")!.Split('.').Select(x => Translator.TranslateMemberName(x)).Join("_");
}
private SqliteCommand CreateCommand(SqliteConnection connection, RequestParams requestParams)
{
if (requestParams.CommandText.IsNullOrEmpty()) throw new ArgumentNullException("sql");
string sql = GetCommandText(requestParams);
var parametersUpdated = UpdateParamNames(requestParams);
SqliteCommand command = connection.CreateCommand();
command.CommandTimeout = connection.ConnectionTimeout;
// Регистрируем пользовательские функции
connection.CreateFunction("str_to_int_json_arr", _funcStrToIntJsonArr, true);
if (requestParams.IsStoredProcedure)
{
GetStoredProcedureContent(command, sql, out string procedureName, out string procedureParams, out string procedureText);
ValidateAndCompleteParams(procedureName, procedureParams, procedureText, parametersUpdated);
sql = procedureText;
}
if (sql.IsNullOrEmpty()) throw new ArgumentNullException("sql");
command.CommandType = CommandType.Text;
command.CommandText = sql;
if (parametersUpdated.Count > 0)
{
foreach (var keyValuePair in parametersUpdated)
{
command.Parameters.AddWithValue(keyValuePair.Key, keyValuePair.Value ?? (keyValuePair.Value is Guid ? Guid.Empty : DBNull.Value));
}
}
return command;
}
private TResult ExecuteOnConnect<TResult>(RequestParams requestParams, Func<SqliteCommand, TResult> func)
{
return _lockService.Lock(requestParams.ConnectionString.Value, () =>
{
bool needNewConnect = requestParams.ConnectionString.DatabaseConnection == null || requestParams.ConnectionString.DatabaseConnection is SqliteConnection == false;
SqliteConnection connection = needNewConnect ? CreateConnection(requestParams.ConnectionString) : (SqliteConnection)requestParams.ConnectionString.DatabaseConnection!;
try
{
using (SqliteCommand command = CreateCommand(connection, requestParams))
return func(command);
}
finally
{
if (needNewConnect)
connection?.Dispose();
connection = null!;
}
});
}
public void ExecuteNonQuery(RequestParams requestParams)
{
ExecuteOnConnect(requestParams, (SqliteCommand command) => command.ExecuteNonQuery());
}
public object ExecuteScalar(RequestParams requestParams)
{
return ExecuteOnConnect(requestParams, (SqliteCommand command) => command.ExecuteScalar()!);
}
public TResult ExecuteScalar<TResult>(RequestParams requestParams)
{
return ExecuteOnConnect(requestParams, (SqliteCommand command) => SQliteConvert.Convert<TResult>(command.ExecuteScalar()!));
}
private IEnumerable<TEntity> ExecuteSelectMany<TEntity>(SelectType extraSelectType, RequestParamsSelect<TEntity> requestParams)
{
if (requestParams.Converters == null || requestParams.Converters.Count() < 1) throw new ArgumentNullException("converters");
return ExecuteOnConnect(requestParams, (SqliteCommand command) =>
{
IList<TEntity> selectResult = extraSelectType.CreateList<TEntity>();
using (IDataReader reader = command.ExecuteReader())
{
IDataRecordSpecified readerSpecified = new SQLiteDataRecordSpecified(reader, Translator);
foreach (var converter in requestParams.Converters)
{
while (reader.Read())
{
converter(readerSpecified, selectResult);
}
reader.NextResult();
}
reader.ProcessExtraSelect(Translator, selectResult, extraSelectType);
}
return selectResult;
});
}
public IEnumerable<TEntity> ExecuteSelectMany<TEntity>(RequestParamsSelect<TEntity> requestParams) => ExecuteSelectMany(SelectType.Default, requestParams);
public IEnumerableWithPage<TEntity> ExecuteSelectManyWithPage<TEntity>(RequestParamsSelect<TEntity> requestParams) => (ListWithPage<TEntity>)ExecuteSelectMany(SelectType.Page, requestParams);
public IEnumerableWithOffer<TEntity> ExecuteSelectManyWithOffer<TEntity>(RequestParamsSelect<TEntity> requestParams) => (ListWithOffer<TEntity>)ExecuteSelectMany(SelectType.Offer, requestParams);
}

View File

@ -0,0 +1,69 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Kit.Helpers;
using Kit.Helpers.Cache;
using Kit.Helpers.Repository;
using Kit.Helpers.Service;
namespace RiskProf.Files.Dependency;
public static class Services
{
public static IServiceCollection AddModuleMemoryCache(this IServiceCollection services)
{
services.AddSingleton<IMemoryCache, MemoryCache>();
services.AddSingleton<ICacheProvider, MemoryCacheProvider>();
return services;
}
public static IServiceCollection AddModuleHardwareId(this IServiceCollection services, bool needDebugProvider)
{
IHardwareInfoProvider infoProvider = null!;
if (needDebugProvider)
{
infoProvider = new HardwareInfoProviderDebug();
}
else if (OperatingSystem.IsWindows())
{
infoProvider = new HardwareInfoProviderWindows();
}
else if (OperatingSystem.IsLinux())
{
infoProvider = new HardwareInfoProviderLinux();
}
if (infoProvider == null)
{
throw new ApplicationException($"{nameof(infoProvider)} is null");
}
services.AddSingleton(infoProvider);
services.AddSingleton<IHardwareInfoService, HardwareInfoService>();
return services;
}
public static IServiceCollection AddModuleSQLite(this IServiceCollection services)
{
services.AddSingleton<ISQLiteGlobalVarRepository, SQLiteGlobalVarRepository>();
services.AddSingleton<ISQLiteVersionRepository, SQLiteVersionRepository>();
services.AddSingleton<ISQLiteVersionService, SQLiteVersionService>();
services.AddSingleton<ISQLiteFileFullPathFactory, SQLiteFileFullPathFactory>();
services.AddSQLiteUpdateItems();
return services;
}
public static void AddSQLiteUpdateItems(this IServiceCollection services)
{
var interfaceType = typeof(ISQLiteVersionUpdateItem);
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var implementationTypes = assemblies.SelectMany(x => x.GetTypes()).Where(t => interfaceType.IsAssignableFrom(t) && t.IsInterface == false && t.IsAbstract == false);
implementationTypes.ForEach(implementationType => services.AddTransient(interfaceType, implementationType));
}
}

View File

@ -0,0 +1,41 @@
using System.ComponentModel;
using System.Reflection;
namespace Kit.Helpers
{
// Класс для сбора словаря
public static class DictionaryCollector
{
public static IDictionary<string, string> GetConstantsWithDescriptions<T>()
{
return GetConstantsWithDescriptions(typeof(T));
}
public static IDictionary<string, string> GetConstantsWithDescriptions(Type type)
{
var result = new Dictionary<string, string>();
// Получаем все поля в переданном статическом классе
var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var field in fields)
{
// Проверяем, является ли поле константой
if (field.IsLiteral && !field.IsInitOnly)
{
// Получаем значение константы
var value = field.GetValue(null)?.ToString();
// Получаем атрибут Description
var attribute = field.GetCustomAttribute<DescriptionAttribute>();
if (attribute != null)
{
// Добавляем пару "значение константы" - "описание" в словарь
result[value] = attribute.Description;
}
}
}
return result;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
namespace Kit.Helpers.EndPoints
{
using Microsoft.AspNetCore.Mvc;
internal class ControllerEndPointFluent<TController> : IEndPointFluent<ControllerEndPointFluent<TController>>
where TController : ControllerBase
{
public ControllerEndPointFluent<TController> NeedHttpGet(bool required = true)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> NeedHttpPost(bool required = true)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> NeedSecurityKey(string securityKeyName)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> WithConstraints(object constraints)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> WithDataTokens(object dataTokens)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> WithDefaults(object defaults)
{
throw new NotImplementedException();
}
public ControllerEndPointFluent<TController> WithNamespaces(string[] namespaces)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,13 @@
namespace Kit.Helpers.EndPoints
{
public interface IEndPointFluent<TEndPointFluent>
{
TEndPointFluent NeedHttpGet(bool required = true);
TEndPointFluent NeedHttpPost(bool required = true);
TEndPointFluent NeedSecurityKey(string securityKeyName);
TEndPointFluent WithDefaults(object defaults);
TEndPointFluent WithConstraints(object constraints);
TEndPointFluent WithNamespaces(string[] namespaces);
TEndPointFluent WithDataTokens(object dataTokens);
}
}

View File

@ -0,0 +1,34 @@
namespace Kit.Helpers
{
public class ItemLogEventManager
{
public ItemLogEventManager() { }
// Пример делегата для события
public delegate void EventHandler(object contentId, IEnumerable<ItemLog> itemLog);
// Событие, на которое могут подписываться другие части приложения
public static event EventHandler OnLog;
//private static ItemLogEventManager _instance;
//public static ItemLogEventManager Instance
//{
// get
// {
// if (_instance == null)
// {
// _instance = new ItemLogEventManager();
// }
// return _instance;
// }
//}
// Метод для вызова события
public static void Invoke(object contentId, IEnumerable<ItemLog> itemLogs)
{
OnLog?.Invoke(contentId, itemLogs);
}
}
}

View File

@ -0,0 +1,12 @@
namespace Kit.Helpers;
using System;
public class AuthenticationException : Exception
{
private const string _messageDefault = "Необходимо выполнить вход в систему";
public string? RedirectUrl { get; set; }
public AuthenticationException() : base(_messageDefault) { }
public AuthenticationException(string? message) : base(message ?? _messageDefault) { }
public AuthenticationException(string? message, Exception? exception) : base(message ?? _messageDefault, exception) { }
}

View File

@ -0,0 +1,12 @@
namespace Kit.Helpers;
using System;
public class AuthorizationException : Exception
{
private const string _messageDefault = "Нет доступа к выполнению операции";
public string? RedirectUrl { get; set; }
public AuthorizationException() : base(_messageDefault) { }
public AuthorizationException(string? message) : base(message ?? _messageDefault) { }
public AuthorizationException(string? message, Exception? exception) : base(message ?? _messageDefault, exception) { }
}

View File

@ -0,0 +1,149 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security;
using System.Text;
namespace Kit.Helpers
{
public static class ExceptionHelper_Extensions
{
public static string GetInfoAsPlainText(this Exception ex)
{
if (ex == null) return string.Empty;
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Source: {0}", ex.Source); sb.AppendLine();
sb.AppendFormat("Message: {0}", ex.Message); sb.AppendLine();
sb.AppendFormat("StackTrace: {0}", ex.StackTrace); sb.AppendLine();
if (ex.InnerException != null)
{
sb.AppendLine("InnerException:");
sb.Append(ex.InnerException.GetInfoAsPlainText());
}
return sb.ToString();
}
public static string GetInfoAsHtml(this Exception ex)
{
return ex.GetInfoAsPlainText().Replace(Environment.NewLine, "<br />");
}
}
public static class ExceptionExtensions
{
public static string ToString(this Exception exception, ExceptionToStringSettings settings)
{
Exception ex = exception;
var stringBuilder = new StringBuilder();
if (ExceptionToStringSettings.ShowMessageAndInnerExceptionMessages == (ExceptionToStringSettings.ShowMessageAndInnerExceptionMessages & settings))
{
if (ExceptionToStringSettings.ShowSeparators == (ExceptionToStringSettings.ShowSeparators & settings))
{
stringBuilder.AppendLine();
stringBuilder.AppendLine("*********************** Exception Text ************************");
}
stringBuilder.Append(ex);
}
if (ExceptionToStringSettings.ShowAssemblyInformation == (ExceptionToStringSettings.ShowAssemblyInformation & settings))
{
if (ExceptionToStringSettings.ShowSeparators == (ExceptionToStringSettings.ShowSeparators & settings))
{
stringBuilder.AppendLine();
stringBuilder.AppendLine();
stringBuilder.AppendLine();
stringBuilder.AppendLine("********************** Loaded Assemblies **********************");
}
stringBuilder.Append(GetLoadedAssemblyInfo());
}
return stringBuilder.ToString();
}
private static string GetLoadedAssemblyInfo()
{
const string separator = "--------------------------------------------------------------";
//new FileIOPermission(PermissionState.Unrestricted).Assert();
try
{
var stringBuilder = new StringBuilder();
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly assembly in assemblies)
{
AssemblyName name2 = assembly.GetName();
string text2 = string.Empty;
try
{
if (!string.IsNullOrEmpty(name2.EscapedCodeBase))
{
var uri = new Uri(name2.EscapedCodeBase);
if (uri.Scheme == "file")
{
text2 = FileVersionInfo.GetVersionInfo(GetLocalPath(name2.EscapedCodeBase)).FileVersion;
}
}
}
catch (FileNotFoundException)
{
}
stringBuilder.AppendLine(name2.Name);
stringBuilder.AppendLine(name2.Version.ToString());
stringBuilder.AppendLine(text2);
stringBuilder.AppendLine(name2.EscapedCodeBase);
stringBuilder.Append(separator);
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
finally
{
// TODO: разобраться, что это. В .NET Core 6 признано небезопасным. подключается как <PackageReference Include="System.Security.Permissions" Version="7.0.0" />
//CodeAccessPermission.RevertAssert();
}
}
private static string GetLocalPath(string fileName)
{
var uri = new Uri(fileName);
return uri.LocalPath + uri.Fragment;
}
}
/// <summary>
/// Settings for <see cref="ExceptionExtensions.ToString(System.Exception, ExceptionToStringSettings)"/>
/// </summary>
[Flags]
public enum ExceptionToStringSettings
{
/// <summary>
/// Show message
/// </summary>
ShowMessageAndInnerExceptionMessages = 1,
/// <summary>
/// Show assembly information
/// </summary>
ShowAssemblyInformation = 2,
/// <summary>
/// Show sepparators
/// </summary>
ShowSeparators = 4,
//All
All = ShowMessageAndInnerExceptionMessages | ShowAssemblyInformation | ShowSeparators
}
}

View File

@ -0,0 +1,96 @@
namespace Kit.Helpers
{
public enum WordGender
{
/// <summary> Мужской род </summary>
Masculine = 1,
/// <summary> Женский род </summary>
Feminine = 2,
/// <summary> Средний род </summary>
Neuter = 3,
}
public static class ExceptionMessages
{
public const string InvalidContext = "Контекст работника не установлен";
public const string InvalidModel = "Передана некорректная модель";
public const string InvalidModelQuery = "Передана некорректная модель (строка)";
public const string InvalidModelForm = "Передана некорректная модель (форма)";
public const string InvalidModelBody = "Передана некорректная модель (тело)";
public const string InvalidTitle = "Указано некорректное наименование";
public const string InvalidUserId = "Указан некорректный id пользователя";
public const string InvalidProjectId = "Указан некорректный id проекта";
public const string InvalidVersionId = "Указан некорректный id версии";
public const string InvalidWorkerGuid = "Указан некорректный id работника";
public const string InvalidElementId = "Указан некорректный id элемента";
public const string InvalidSecObjPermissions = "Указаны некорректные идентификаторы защищаемых объектов и/или функций";
public const string InvalidRoles = "Указаны некорректные идентификаторы ролей";
public const string InvalidWorkers = "Указаны некорректные данные работников";
public const string InvalidWorkerIds = "Указаны некорректные идентификаторы работников";
public const string InvalidUserToken = "Указан некорректный токен пользователя";
public const string InvalidLogin = "Указан некорректный логин";
public const string InvalidPassword = "Указан некорректный пароль";
public const string InvalidLoginOrPassword = "Указан некорректный логин или пароль";
public const string InvalidTempToken = "Указан некорректный временный токен";
public const string InvalidCheckupItemId = "Указан некорректный id экземпляра проверки";
public const string InvalidCheckupItemObjectLink = "Указана некорректная ссылка на объект";
public const string InvalidAttachmentId = "Указан некорректный id приложения";
public const string InvalidGenerciRefGroupId = "Указан некорректный id динамического классификатора";
public const string InvalidGenerciRefGroupKey = "Указан некорректный ключ динамического классификатора";
public const string InvalidGenerciRefId = "Указан некорректный id элемента динамического классификатора";
public const string InvalidAttachmentFile = "Отсутствует прикреплённый файл";
public const string InvalidAttachmentFiles = "Отсутствуют прикреплённые файлы";
public const string InvalidChecklistIdList = "Отсутствуют элементы в списке id проверочных листов";
public const string XmlDoc404Workers = "Документ со списком работников не найден";
public const string XmlDoc404Checklist = "Документ со структурой проверочного листа не найден";
public const string XmlDoc404Classifiers = "Документ со структурой классификаторов не найден";
public const string XmlDoc404CheckupItem = "Документ со структурой проверки не найден";
public const string XmlDoc404CheckupType = "Документ со списком типов проверок не найден";
public const string XmlDoc404CheckupCategory = "Документ со списком категорий проверок не найден";
public const string XmlDoc404CheckupQualityMatrix = "Документ с матрицей оценки проверок не найден";
public const string Context404ProjectId = "В контексте работника не указан id проекта";
public const string Context404VersionId = "В контексте работника не указан id версии";
public const string Context404WorkerGuid = "В контексте работника не указан id работника";
public const string Context403Version = "Работник не имеет доступа к версии проекта";
public const string Context403Profiles = "Пользователю не доступен ни один вариант входа";
public const string User404ByToken = "Пользователь по токену не найден";
public const string User404ById = "Пользователь по id не найден";
public const string User404ProfileById = "Профиль пользователя по id не найден";
public const string Code500 = "На сервере произошла немзвестная ошибка";
/// <returns>$"Указан некорректный id {<paramref name="elementName"/>}"</returns>
public static string InvalidId(string elementName)
{
return $"Указан некорректный id {elementName}";
}
/// <returns>$"{<paramref name="elementName"/>} не найден(-а, -о в зависимости от значения <paramref name="wordGender"/>)"</returns>
public static string Element404(string elementName, WordGender wordGender)
{
string notFoundWord = string.Empty;
switch (wordGender)
{
case WordGender.Masculine:
notFoundWord = "найден";
break;
case WordGender.Feminine:
notFoundWord = "найдена";
break;
case WordGender.Neuter:
notFoundWord = "найдено";
break;
}
return $"{elementName} не {notFoundWord}";
}
public static string Element404CheckupItem = Element404("Экземпляр проверки", WordGender.Masculine);
public static string Element404AttachmentFile = Element404("Файл приложения", WordGender.Masculine);
}
}

View File

@ -0,0 +1,20 @@
namespace Kit.Helpers
{
using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class WebExceptionAttribute : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext exceptionContext)
{
if (!exceptionContext.ExceptionHandled)
{
exceptionContext.ExceptionHandled = true;
exceptionContext.HttpContext.Response.WriteException(exceptionContext.Exception);
exceptionContext.Result = new EmptyResult();
}
}
}
}

View File

@ -0,0 +1,53 @@
using System;
namespace Kit.Helpers
{
public class XmlFormatExceptionItem
{
public string Type { get; set; }
public int Line { get; set; }
public int Position { get; set; }
public string Message { get; set; }
}
public class XmlFormatException : Exception
{
public IEnumerable<XmlFormatExceptionItem> Errors { get; set; }
public XmlFormatException() { }
public XmlFormatException(IEnumerable<XmlFormatExceptionItem> errors)
{
Errors = errors;
}
}
public enum ValidateMessageType { Info = 1, Warning = 2, Error = 3 }
public class XmlValidateMessage
{
private static IDictionary<Enum, string> MessageTypeDecoding = new Dictionary<Enum, string>()
{
{ValidateMessageType.Info, "Информация"},
{ValidateMessageType.Warning, "Предупреждение"},
{ValidateMessageType.Error, "Ошибка"}
};
public string DecodedMessageType
{
get
{
string value;
if (MessageTypeDecoding.TryGetValue(MessageType, out value)) return value;
else throw new ArgumentOutOfRangeException("MessageType");
}
}
public ValidateMessageType MessageType { get; set; }
public string ValidatorName { get; set; }
public string Message { get; set; }
public IEnumerable<ParameterValue> Parameters { get; set; }
}
public class ParameterValue
{
public string Title { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
}

View File

@ -0,0 +1,152 @@
using System;
namespace Kit.Helpers.Extension.Entities
{
public interface IIdTitle<TId, TTitle>
{
TId Id { get; set; }
TTitle Title { get; set; }
}
public interface IIdOnly<TId>
{
TId Id { get; set; }
}
public class IdTitle : IInt32Id, IEquatable<IdTitle>, IIdTitle<int, string>, ICloneable
{
public IdTitle() { }
public IdTitle(int id, string title) : this()
{
this.Id = id;
this.Title = title;
}
public virtual int Id { get; set; }
public virtual string Title { get; set; }
#region Empty
public static readonly int EmptyId = -1;
public static readonly string EmptyTitle = "n/a";
public static TEntity GetEmpty<TEntity>()
where TEntity : IdTitle, new()
{
return new TEntity
{
Id = EmptyId,
Title = EmptyTitle
};
}
public static IdTitle GetEmpty()
{
return GetEmpty<IdTitle>();
}
#endregion
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
/// </summary>
/// <returns>
/// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
/// </returns>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>. </param><filterpriority>2</filterpriority>
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (!(obj is IdTitle)) return false;
return Equals((IdTitle)obj);
}
/// <summary>
/// Indicates whether the current object is equal to another object of the same type.
/// </summary>
/// <returns>
/// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
/// </returns>
/// <param name="other">An object to compare with this object.</param>
public bool Equals(IdTitle other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return other.Id == Id && Equals(other.Title, Title);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
/// <filterpriority>2</filterpriority>
public override int GetHashCode()
{
unchecked
{
return (Id * 397) ^ (Title != null ? Title.GetHashCode() : 0);
}
}
public object Clone()
{
return new IdTitle
{
Id = Id,
Title = Title
};
}
public static bool operator ==(IdTitle left, IdTitle right)
{
return Equals(left, right);
}
public static bool operator !=(IdTitle left, IdTitle right)
{
return !Equals(left, right);
}
}
public class IdTitle<TId, TTitle> : IIdTitle<TId, TTitle>
{
public IdTitle() { }
public IdTitle(TId id, TTitle title) : this()
{
this.Id = id;
this.Title = title;
}
public IdTitle(IdTitle<TId, TTitle> idTitle) : this()
{
this.Id = idTitle.Id;
this.Title = idTitle.Title;
}
public virtual TId Id { get; set; }
public virtual TTitle Title { get; set; }
}
public class IdOnly<TId> : IIdOnly<TId>
where TId : struct
{
public IdOnly() { }
public IdOnly(TId id) : this()
{
this.Id = id;
}
public IdOnly(IdOnly<TId> idTitle) : this()
{
this.Id = idTitle.Id;
}
public virtual TId Id { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
namespace Kit.Helpers.Extension.Entities
{
public interface IIdTitleParent<TEntity>
{
string Title { get; set; }
TEntity Parent { get; set; }
int? ParentId { get; set; }
int Level { get; set; }
bool IsFirst { get; set; }
bool IsLast { get; set; }
IEnumerable<TEntity> Childs { get; set; }
}
public class IdTitleParent<TEntity> : IdTitle, IIdTitleParent<TEntity>
{
public int? ParentId { get; set; }
public TEntity Parent { get; set; }
public int Level { get; set; }
public bool IsFirst { get; set; }
public bool IsLast { get; set; }
public IEnumerable<TEntity> Childs { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace Kit.Helpers.Extension.Entities
{
public interface IInt32Id
{
int Id { get; }
}
public static class IInt32IdExtentions
{
public static IDictionary<int, TObject> ConvertToDictionary<TObject>(this IEnumerable<TObject> objects)
where TObject : IInt32Id
{
var dictionary = new Dictionary<int, TObject>();
{
foreach (var obj in objects) dictionary.Add(obj.Id, obj);
}
return dictionary;
}
}
}

View File

@ -0,0 +1,69 @@
using System.Collections.Generic;
namespace Kit.Helpers.Extension.Entities
{
public static class IdParentExtension
{
public static string BuildPath<TEntity>(this IIdParent<TEntity> item)
{
if (item != null)
{
return item.Parent.BuildPath().AddSufixIsNotEmpty(".") + item.Id.ToString("x9");
}
return string.Empty;
}
public static int BuildLevel<TEntity>(this IIdParent<TEntity> item)
{
if (item != null)
{
return item.Parent.BuildLevel() + 1;
}
return 0;
}
}
public interface IIdParent<TEntity>
{
int Id { get; set; }
IIdParent<TEntity> Parent { get; set; }
int? ParentId { get; set; }
int Level { get; }
string Path { get; }
IEnumerable<TEntity> Childs { get; set; }
}
public class IdParent<TEntity> : IIdParent<TEntity>
{
private string _path;
private int _lvl;
public int Id { get; set; }
public IIdParent<TEntity> Parent { get; set; }
public int? ParentId { get; set; }
public int Level
{
get
{
if (_lvl == 0)
{
_lvl = this.BuildLevel();
}
return _lvl;
}
}
public IEnumerable<TEntity> Childs { get; set; }
public string Path
{
get
{
if (_path.IsNullOrEmpty())
{
_path = this.BuildPath();
}
return _path;
}
}
}
}

View File

@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
using System.Linq;
namespace Kit.Helpers.Extension.Entities
{
public static class IdTitleExtensions
{
public static IEnumerable<SelectListItem> AddSelectListItem(this IEnumerable<SelectListItem> selectListItems, string title, string value, bool selected = false, bool insertFirst = true)
{
var selectListItem = new SelectListItem { Text = title, Value = value, Selected = selected };
var list = selectListItems as IList<SelectListItem>
?? selectListItems.ToList();
if (insertFirst)
{
list.Insert(0, selectListItem);
}
else
{
list.Add(selectListItem);
}
return list;
}
public static IEnumerable<SelectListItem> AddSelectListItem(this IEnumerable<SelectListItem> selectListItems, string title, object value, bool selected = false, bool insertFirst = true)
{
return selectListItems.AddSelectListItem(title, value.ToString(), selected, insertFirst);
}
}
}

View File

@ -0,0 +1,42 @@
namespace Kit.Helpers
{
public interface IEnumerableWithPage<TEntity> : IEnumerable<TEntity>
{
int TotalRows { get; set; }
int FilteredRows { get; set; }
int PageNo { get; set; }
int PageSize { get; set; }
string Sort { get; set; }
string Dir { get; set; }
}
public interface IEnumerableWithOffer<TEntity> : IEnumerable<TEntity>
{
int TotalRows { get; set; }
int FilteredRows { get; set; }
int Start { get; set; }
int Lenght { get; set; }
string Sort { get; set; }
string Dir { get; set; }
}
public class ListWithPage<TEntity> : List<TEntity>, IEnumerableWithPage<TEntity>
{
public int TotalRows { get; set; }
public int FilteredRows { get; set; }
public int PageNo { get; set; }
public int PageSize { get; set; }
public new string Sort { get; set; }
public string Dir { get; set; }
}
public class ListWithOffer<TEntity> : List<TEntity>, IEnumerableWithOffer<TEntity>
{
public int TotalRows { get; set; }
public int FilteredRows { get; set; }
public int Start { get; set; }
public int Lenght { get; set; }
public new string Sort { get; set; }
public string Dir { get; set; }
}
}

View File

@ -0,0 +1,39 @@
namespace Kit.Helpers
{
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System.Reflection;
public static class MicrosoftAspNetCoreHttpHttpContextExtentions
{
public static bool NeedResponseJsonResult(this HttpContext context, Exception? ex = null)
{
var routeData = context.GetRouteData();
var controllerName = routeData?.Values["controller"]?.ToString();
var actionName = routeData?.Values["action"]?.ToString();
MethodInfo? methodInfo = null;
if (string.IsNullOrEmpty(controllerName) == false && string.IsNullOrEmpty(actionName) == false)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type? controllerType = assembly.GetTypes()?.FirstOrDefault(t => string.Equals(t.Name, $"{controllerName}Controller", StringComparison.OrdinalIgnoreCase));
if (controllerType != null)
{
methodInfo = controllerType.GetMethod(actionName);
break;
}
}
}
if (methodInfo == null && ex?.TargetSite != null && ex.TargetSite is MethodInfo methodInfoEx)
{
methodInfo = methodInfoEx;
}
return methodInfo != null && methodInfo.ReturnType.Equals(typeof(JsonResult));
}
}
}

View File

@ -0,0 +1,12 @@
namespace Kit.Helpers
{
using Microsoft.AspNetCore.Http;
public static class MicrosoftAspNetCoreHttpHttpRequestExtentions
{
public static string? TryGetEventNameIFrame(this HttpRequest request)
{
string? value = request.Query["eventNameIframe"];
return string.IsNullOrWhiteSpace(value) ? null : value;
}
}
}

View File

@ -0,0 +1,32 @@
namespace Kit.Helpers
{
using Microsoft.AspNetCore.Http;
public static class MicrosoftAspNetCoreHttpSessionExtentions
{
public static T GetValue<T>(this ISession session, string key)
{
string json = session.GetString(key);
return json.JsonDeserialize<T>();
}
public static T GetValueOrDefault<T>(this ISession session, string key)
{
string json = session.GetString(key);
T result;
try
{
result = json.JsonDeserialize<T>();
}
catch
{
return default;
}
return result;
}
public static void SetValue(this ISession session, string key, object obj)
{
session.SetString(key, obj.JsonSerialize());
}
}
}

View File

@ -0,0 +1,42 @@
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Kit.Helpers
{
public static class MicrosoftAspNetCoreMvcViewFeaturesExtension
{
public static IHtmlContent ValidationClassFor<TModel, TResult>(this IHtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TResult>> expression, string classNameValid = "is-valid", string classNameInvalid = "is-invalid")
{
ArgumentNullException.ThrowIfNull(expression);
string memberName = GetMemberName(expression);
var state = htmlHelper.ViewData.ModelState[memberName];
var validationState = state?.ValidationState ?? ModelValidationState.Unvalidated;
switch (validationState)
{
case ModelValidationState.Invalid: return htmlHelper.Raw(classNameInvalid);
case ModelValidationState.Valid: return htmlHelper.Raw(classNameValid);
default: return htmlHelper.Raw(string.Empty);
}
}
private static string GetMemberName<TModel, TResult>(Expression<Func<TModel, TResult>> expression)
{
if (expression.Body is MemberExpression memberExpression)
{
// Если выражение представляет доступ к полю или свойству
return memberExpression.Member.Name;
}
else if (expression.Body is UnaryExpression unaryExpression && unaryExpression.Operand is MemberExpression unaryMemberExpression)
{
// Если выражение представляет собой унарную операцию (например, приведение типа)
return unaryMemberExpression.Member.Name;
}
throw new ArgumentException("Выражение должно представлять доступ к полю или свойству.", nameof(expression));
}
}
}

View File

@ -0,0 +1,71 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Routing;
using Kit.Helpers;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Web;
namespace Kit.Helpers
{
public static class UrlHelperExtensions
{
public static Func<string?>? FuncGetUserToken { get; set; }
public static TModel? FillLayoutModel<TModel>(this Controller controller, TModel? model) where TModel : LayoutModel
{
model?.Fill(controller.HttpContext, controller.ControllerContext);
return model;
}
public static string? TryAddExtraData(this string? uriString, bool updateIfExists = true)
{
if (uriString == null)
{
return null;
}
string? userToken = FuncGetUserToken != null ? FuncGetUserToken() : null;
if (userToken != null)
{
uriString = uriString.SetQueryParam("userToken", userToken, updateIfExists);
}
string? eventNameIframe = HttpContextCore.Current.Request.TryGetEventNameIFrame();
if (eventNameIframe != null)
{
uriString = uriString.SetQueryParam("eventNameIframe", eventNameIframe, updateIfExists);
}
return uriString;
}
public static string SetQueryParam(this string url, string paramName, string paramValue, bool updateIfExists)
{
// Разделяем URL на путь и query
var parts = url.Split('?');
var path = parts[0];
var query = parts.Length > 1 ? parts[1] : "";
// Парсим query-параметры
var queryParams = HttpUtility.ParseQueryString(query);
if (queryParams[paramName] == null || updateIfExists)
{
// Обновляем параметр
queryParams[paramName] = paramValue;
}
// Собираем URL обратно
var newQuery = queryParams.ToString();
return path + (string.IsNullOrEmpty(newQuery) ? "" : "?" + newQuery);
}
public static string? ActionWithExtraData(this IUrlHelper urlHelper, UrlActionContext actionContext) => urlHelper.Action(actionContext).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, object values) => urlHelper.Action(action, values).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, string controller) => urlHelper.Action(action, controller).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, string controller, object values) => urlHelper.Action(action, controller, values).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, string controller, object values, string? protocol) => urlHelper.Action(action, controller, values, protocol).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, string controller, object values, string? protocol, string? host) => urlHelper.Action(action, controller, values, protocol, host).TryAddExtraData();
public static string? ActionWithExtraData(this IUrlHelper urlHelper, string action, string controller, object values, string? protocol, string? host, string? fragment) => urlHelper.Action(action, controller, values, protocol, host, fragment).TryAddExtraData();
public static string? ActionWithExtraData<TController>(this IUrlHelper urlHelper, Expression<Func<TController, Delegate>> actionSelector, object? values = null, string? protocol = null) where TController : Controller => urlHelper.Action(actionSelector, values, protocol).TryAddExtraData();
}
}

View File

@ -0,0 +1,12 @@
namespace Kit.Helpers
{
using Microsoft.Extensions.Logging;
public static class MicrosoftExtensionsLoggingExt
{
public static void LogParamTrace(this ILogger logger, string method, KeyValuePair<string, object?> param) => LogParamsTrace(logger, method, [param]);
public static void LogParamsTrace(this ILogger logger, string method, IEnumerable<KeyValuePair<string, object?>> @params)
{
logger.LogTrace(method + Environment.NewLine + @params.Select(x => $"param \"{x.Key}\": {(x.Value?.JsonSerialize(enableCyrillic: true, writeIntended: true) ?? "<NULL>")}").Join(Environment.NewLine));
}
}
}

View File

@ -0,0 +1,10 @@
namespace Kit.Helpers
{
public static partial class SystemBooleanExtensionMethods
{
public static string ToJsString(this bool value)
{
return value ? "true" : "false";
}
}
}

View File

@ -0,0 +1,304 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Kit.Helpers
{
public static class SystemCollectionExtensionMethods
{
public static IList<TItem> NullToEmpty<TItem>(this IList<TItem> list)
{
return list ?? new List<TItem>();
}
public static IEnumerable<TItem> NullToEmpty<TItem>(this IEnumerable<TItem> list)
{
return list ?? new List<TItem>();
}
public static IList<TItem> AddRange<TItem>(this IList<TItem> list, IEnumerable<TItem> items)
{
if (items == null) return list;
foreach (TItem item in items) list.Add(item);
return list;
}
public static IList<TItem> AddRangeEx<TItem>(this IList<TItem> list, IEnumerable<TItem> items)
{
if (items == null) return list;
foreach (TItem item in items) list.Add(item);
return list;
}
public static IList<TItem> AddEx<TItem>(this IList<TItem> list, TItem item)
{
list.Add(item);
return list;
}
public static IEnumerable<TItem> AddToNewList<TItem>(this IEnumerable<TItem> list, TItem item)
{
return list.ToList().AddEx(item);
}
public static IList<TItem> AddToNewList<TItem>(this IList<TItem> list, TItem item)
{
return list.AddEx(item);
}
public static IDictionary<TKey, TValue> AddRange<TKey, TValue>(this IDictionary<TKey, TValue> target, IDictionary<TKey, TValue> source)
{
foreach (var item in source) target.SetByKey(item.Key, item.Value);
return target;
}
public static bool IsNullOrEmpty<TItem>([NotNullWhen(false)] this IEnumerable<TItem>? collection)
{
return collection == null || collection.Any() == false;
}
public static TValue GetByKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue defaultValue)
{
return dictionary != null && dictionary.ContainsKey(key)
? dictionary[key]
: defaultValue;
}
public static TValue GetByKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
{
return dictionary.GetByKey(key, default(TValue));
}
public static TValue SetByKey<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (dictionary == null) throw new ArgumentNullException("dictionary");
lock (dictionary)
{
if (!dictionary.ContainsKey(key)) dictionary.Add(key, value);
else dictionary[key] = value;
}
return value;
}
public static object CastToTypedEnumerable(this IEnumerable<object> collection, Type targetType)
{
var resultListType = typeof(List<>).MakeGenericType(targetType);
var resultList = (IList)Activator.CreateInstance(resultListType);
foreach (var collectionItem in collection) resultList.Add(collectionItem);
return resultList;
}
public static Dictionary<string, string> ParseToDictionaryString(this NameValueCollection collection)
{
Dictionary<string, string> result = new Dictionary<string, string>();
foreach (string item in collection.Keys)
{
result.Add(item, collection[item]);
}
return result;
}
public static string ParseToString(this NameValueCollection collection, char parameterSeparator, char nameValueSeparator)
{
StringBuilder sb = new StringBuilder();
foreach (string item in collection.Keys)
{
sb.AppendFormat("{0}{1}{2}{3}",item,nameValueSeparator,collection[item],parameterSeparator);
}
return sb.ToString();
}
public static void Add<TKey, TValue>(this NameValueCollection collection, IEnumerable<KeyValuePair<TKey, TValue>> items)
{
foreach (KeyValuePair<TKey, TValue> kv in items)
{
if (Equals(kv.Key, default(TKey))) continue;
string value = Equals(kv.Value, default(TValue)) ? string.Empty : kv.Value.ToString();
collection.Add(kv.Key.ToString(), value);
}
}
public static NameValueCollection ToNameValueCollection(this IEnumerable<KeyValuePair<string, string>> kvpCollection)
{
NameValueCollection collection = new NameValueCollection();
collection.Add(kvpCollection);
return collection;
}
public static string Join(this string[] array, string separator)
{
return string.Join(separator, array);
}
public static string Join(this string[] array)
{
return array.Join(",");
}
public static string Join<TObject>(this IEnumerable<TObject> collection, string separator)
{
if (collection == null) return null;
IEnumerable<string> stringCollection = collection.Select(x => x.ToString());
//(from o in collection
// where Equals(o, default(TObject))
// select o.ToString());
return stringCollection.ToArray().Join(separator);
}
public static string Join<TObject>(this IEnumerable<TObject> collection)
{
return collection.Join(",");
}
public static IEnumerable<KeyValuePair<string, string>> ToKeyValuePairs(this NameValueCollection nvc)
{
List<KeyValuePair<string, string>> kvc = new List<KeyValuePair<string,string>>();
if (nvc == null) return kvc;
foreach(string key in nvc.Keys) kvc.Add(new KeyValuePair<string,string>(key, nvc[key]));
return kvc;
}
public static IEnumerable<TObject> Random<TObject>(this IEnumerable<TObject> collection, int limit = -1)
{
if (collection == null) return null;
var random = new Random();
collection = collection.OrderBy(o => random.Next());
if (limit > -1) collection = collection.Take(limit);
return collection;
}
public static IList<T> ForEach<T>(this IList<T> collection, Action<T> action)
{
if (collection == null) return null;
if (action == null) return collection;
foreach (T element in collection) action(element);
return collection;
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T> action)
{
if (collection == null) return null;
if (action == null) return collection;
IEnumerable<T> forEach = collection as IList<T> ?? collection.ToList();
foreach (T element in forEach) action(element);
return forEach;
}
public static T[] ForEach<T>(this T[] collection, Action<T> action)
{
return ((IEnumerable<T>)collection).ForEach(action).ToArray();
}
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> collection, Action<T, int> action)
{
if (collection == null) return null;
if (action == null) return collection;
IEnumerable<T> forEach = collection as IList<T> ?? collection.ToList();
int indexEnd = forEach.Count();
for (int index = 0; index < indexEnd; index++)
{
T element = forEach.ElementAt(index);
action(element, index);
}
return forEach;
}
public static IEnumerable<TItem> InsertFirstToNewList<TItem>(this IEnumerable<TItem> list, TItem item)
{
var lists = list.ToList();
lists.Insert(0, item);
return lists;
}
public static IEnumerable<TItem> InsertEx<TItem>(this IList<TItem> list, int index, TItem item)
{
list.Insert(index, item);
return list;
}
public static List<int> SearchBytePattern(this byte[] Source, byte[] Pattern, int Start)
{
int SourceLen = Source.Length - Pattern.Length + 1;//Получаем длину исходного массива.
int j;
var positions = new List<int>();//Переменная для хранения списка результатов поиска.
for (int i = Start; i < SourceLen; i++)
{
if (Source[i] != Pattern[0]) continue;//Сравниваем первый искомый байт и если он совпадает, то проверяем остальные байты за ним идущие, иначе идем дальше по массиву.
for (j = Pattern.Length - 1; j >= 1 && Source[i + j] == Pattern[j]; j--) ;//Сравниваем остальные байты с нашим значением.
if (j == 0)//Переменная будет равна нулю, если все байты совпали
{
positions.Add(i);//Добавляем адрес конца совпадения первого байта в список
i += Pattern.Length - 1;//Увеличиваем значение переменной на величину искомого, так как мы его уже проверили и это ускоряет поиск.
}
}
return positions;//Отдаем список адресов, которые совпадают с искомым значением.
}
public static IEnumerable<IEnumerable<TItem>> GetPages<TItem>(this IEnumerable<TItem> list, int pageSize)
{
int pageCount = (int)Math.Ceiling((list.Count() * 1.0) / (pageSize * 1.0));
var lists = new List<List<TItem>>();
for (int pageNum = 0; pageNum < pageCount; pageNum++)
{
lists.Add(list.Skip(pageNum * pageSize).Take(pageSize).ToList());
}
return lists;
}
public static IEnumerable<TItem> GetPage<TItem>(this IEnumerable<TItem> list, int page, int pageSize)
{
return list.Skip(page * pageSize).Take(pageSize);
}
public static void AddDistinctValue<K, TValue>(this IDictionary<K, IEnumerable<TValue>> dictionary, K key, params TValue[] newValue)
{
AddDistinctValue(dictionary, key, comparer: null, newValue: newValue);
}
public static void AddDistinctValue<K, TValue>(this IDictionary<K, IEnumerable<TValue>> dictionary, K key, IEqualityComparer<TValue> comparer, params TValue[] newValue)
{
if (dictionary.ContainsKey(key))
{
List<TValue> values = dictionary[key].ToList();
if (values.Any(x => x.Equals(newValue)) == false)
{
IEnumerable<TValue> newValues = comparer == null ? values.Union(newValue).ToList() : values.Union(newValue, comparer).ToList();
dictionary[key] = newValues;
}
}
else
{
dictionary.Add(key, newValue.ToList());
}
}
public static void AddListValue<K, TValue>(this IDictionary<K, IEnumerable<TValue>> dictionary, K key, params TValue[] newValue)
{
if (dictionary.ContainsKey(key))
{
List<TValue> values = dictionary[key].ToList();
values.AddRange(newValue);
dictionary[key] = values;
}
else
{
dictionary.Add(key, newValue.ToList());
}
}
public static bool SetEquals<TItem>(this IEnumerable<TItem> firstCollection, IEnumerable<TItem> secondCollection)
{
if (firstCollection == null && secondCollection == null) return true;
if (firstCollection == null || secondCollection == null) return false;
return new HashSet<TItem>(firstCollection).SetEquals(new HashSet<TItem>(secondCollection));
}
}
}

View File

@ -0,0 +1,172 @@
using System.Globalization;
namespace Kit.Helpers
{
public class WeekInfo
{
public int NumberOfYear { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
public static partial class SystemDateTimeExtensionMethods
{
public static string ToStringDateRus(this DateTime dt)
{
return dt.ToString("dd.MM.yyyy");
}
public static string ToStringDateTimeRus(this DateTime dt)
{
return dt.ToString("dd.MM.yyyy hh:mm");
}
public static string ToStringTimeRus(this DateTime dt)
{
return dt.ToString("hh:mm:ss");
}
public static string ToStringDateRus(this DateTime? dt)
{
if (dt == null) return String.Empty;
return dt.ToStringDateRus();
}
public static string ToStringDateTimeRus(this DateTime? dt)
{
if (dt == null) return String.Empty;
return dt.Value.ToStringDateTimeRus();
}
public static string ToStringTimeRus(this DateTime? dt)
{
if (dt == null) return String.Empty;
return dt.Value.ToStringTimeRus();
}
private static CultureInfo cultureRu = new CultureInfo("ru-Ru");
public static string GetStringMonthYear(this DateTime date)
{
return date.ToString("Y");
}
//public static DateTime Normolize(this DateTime currentValue)
//{
// return currentValue.Normolize(DateTime.MinValue, DateTime.Now);
//}
public static DateTime GetClearTime(this DateTime date)
{
return new DateTime(date.Year, date.Month, date.Day);
}
private static readonly long DatetimeMinTimeTicks =
(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).Ticks;
public static long ToJavaScriptMilliseconds(this DateTime dt)
{
return (long)((dt.ToUniversalTime().Ticks - DatetimeMinTimeTicks) / 10000);
}
public static DateTime GetUtcFromDateTime(this DateTime dt, string timezene)
{
TimeZoneInfo tzf;
try
{
tzf = TimeZoneInfo.FindSystemTimeZoneById(timezene);
}
catch
{
tzf = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
}
return TimeZoneInfo.ConvertTimeToUtc(dt, tzf);
}
public static DateTime GetDateTimeFromUtc(this DateTime dt, string timezene)
{
TimeZoneInfo tzf;
try
{
tzf = TimeZoneInfo.FindSystemTimeZoneById(timezene);
}
catch
{
tzf = TimeZoneInfo.FindSystemTimeZoneById("Russian Standard Time");
}
return TimeZoneInfo.ConvertTimeFromUtc(dt, tzf);
}
public static string ToStringOrEmpty(this DateTime? dt, string format)
{
if (dt == null) return String.Empty;
return dt.Value.ToString(format);
}
public static string ToStringOrEmpty(this DateTime? dt)
{
if (dt == null) return String.Empty;
return dt.Value.ToString("dd.MM.yyyy");
}
public static string ToShortDateStringRu(this DateTime value)
{
return value.ToString("dd.MM.yyyy");
}
public static string ToShortDateStringUniversal(this DateTime value)
{
return value.ToString("yyyy.MM.dd");
}
public static string ToStringDMYHMS(this DateTime value)
{
return value.ToString("dd.MM.yyyy hh:mm:ss");
}
public static string ToShortDateStringRu(this DateTime? value)
{
if (value == null) return String.Empty;
return value.Value.ToShortDateStringRu();
}
public static string ToShortDateStringUniversal(this DateTime? value)
{
if (value == null) return String.Empty;
return value.Value.ToShortDateStringUniversal();
}
public static string ToStringDMYHMS(this DateTime? value)
{
if (value == null) return String.Empty;
return value.Value.ToStringDMYHMS();
}
public static WeekInfo GetInfoWeek(this DateTime? value)
{
if (!value.HasValue) return null;
return value.Value.GetInfoWeek();
}
public static WeekInfo GetInfoWeek(this DateTime value)
{
var result = new WeekInfo();
DateTimeFormatInfo dfi = cultureRu.DateTimeFormat;
Calendar cal = dfi.Calendar;
result.NumberOfYear = cal.GetWeekOfYear(value, CalendarWeekRule.FirstFullWeek, DayOfWeek.Monday);
var dtStart = new DateTime(value.Year, 1, 1).AddDays(-(int)(new DateTime(value.Year, 1, 1).DayOfWeek - 1));
result.StartDate = dtStart.AddDays((result.NumberOfYear - 1) * 7);
result.EndDate = dtStart.AddDays(result.NumberOfYear * 7);
return result;
}
public static string GetMonthNameRP(this DateTime dt)
{
return dt.ToString("MMMMMMMMMMM", cultureRu).Replace("т", "та").Replace("тая", "тя").Replace("й", "я").Replace("ь", "я");
}
}
}

View File

@ -0,0 +1,54 @@
using System.IO;
using System.IO.Pipes;
using System.Reflection;
using System.Security.Cryptography;
namespace Kit.Helpers
{
public static class SystemIOStreamExtentions
{
public static byte[] ReadFromStream(this Stream stream)
{
byte[] array = new byte[32768];
using (MemoryStream memoryStream = new MemoryStream())
{
while (true)
{
int num = stream.Read(array, 0, array.Length);
if (num <= 0)
{
break;
}
memoryStream.Write(array, 0, num);
}
return memoryStream.ToArray();
}
}
public static string ComputeHash<THashAlgorithm>(this Stream stream) where THashAlgorithm : HashAlgorithm
{
// Получаем тип THashAlgorithm
Type algorithmType = typeof(THashAlgorithm);
// Получаем метод Create() через Reflection
MethodInfo? createMethod = algorithmType.GetMethods(BindingFlags.Public | BindingFlags.Static).SingleOrDefault(x => x.Name == "Create" && x.GetParameters().Length == 0);
// Проверяем, существует ли метод
if (createMethod == null)
{
throw new InvalidOperationException($"Тип {algorithmType.FullName} не имеет статического метода Create().");
}
// Вызываем метод Create() и приводим результат к HashAlgorithm
using (var algorithm = (THashAlgorithm)createMethod.Invoke(null, null))
{
var hash = BitConverter.ToString(algorithm.ComputeHash(stream)).Replace("-", "").ToLowerInvariant();
if (stream.CanSeek) stream.Position = 0;
return hash;
}
}
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Kit.Helpers
{
public static class SystemIntegerExtensionMethods
{
public static string ToMonthName(this int monthNo)
{
if (monthNo < 1 || monthNo > 12) { throw new ArgumentOutOfRangeException("monthNo", monthNo, "monthNo must be one of 1..12"); }
var dateTimeFormat = new CultureInfo("ru-RU").DateTimeFormat;
return dateTimeFormat.GetMonthName(monthNo);
}
public static string ToMonthNamea(this int monthNo)
{
if (monthNo < 1 || monthNo > 12) { throw new ArgumentOutOfRangeException("monthNo", monthNo, "monthNo must be one of 1..12"); }
var monthNames = new string[] {
"января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"
};
return monthNames[monthNo - 1];
}
public static string Join(this IEnumerable<int> collection, string separator)
{
if (collection == null) return null;
IEnumerable<string> stringCollection = collection.Select(x => x.ToString());
return stringCollection.ToArray().Join(separator);
}
public static bool InRange(this int value, int rangeStart, int rangeEnd)
{
return value >= rangeStart && value <= rangeEnd;
}
public static IEnumerable<TEntity> Select<TEntity>(this int count, Func<int, TEntity> func)
{
var list = new List<TEntity>();
for (int i = 0; i < count; i++)
{
list.Add(func(i));
}
return list;
}
}
}

View File

@ -0,0 +1,185 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
namespace Kit.Helpers
{
public static partial class SystemObjectExtensionMethods
{
public static string ToJSON(this object obj, bool enableCyrillic = false)
{
return obj.JsonSerialize(enableCyrillic);
}
public static TObject ToObjectFromJSON<TObject>(this string json)
{
return json.JsonDeserialize<TObject>();
}
public static void SetProperties(this object obj, NameValueCollection properties)
{
Type objectType = obj.GetType();
foreach (string name in properties.Keys)
{
var property = objectType.GetProperty(name);
object propertyValue = property.PropertyType.IsEnum
? Enum.Parse(property.PropertyType, properties[name])
: System.Convert.ChangeType(properties[name], property.PropertyType);
property.SetValue(obj, propertyValue, null);
}
}
public static void SetProperties(this object obj, Dictionary<string, object> properties)
{
Type objectType = obj.GetType();
foreach (string name in properties.Keys)
{
var property = objectType.GetProperty(name);
object propertyValue = properties[name];
property.SetValue(obj, propertyValue, null);
}
}
public static object GetPropertyValue(this object thisObject, string propertyPath)
{
if (thisObject == null) return null;
string[] propertyNames = propertyPath.Split('.');
foreach (var propertyName in propertyNames)
{
var propertyInfo = thisObject.GetType().GetProperty(propertyName);
if (propertyInfo == null) throw new ArgumentOutOfRangeException("PropertyName", propertyName, "Property not found.");
thisObject = propertyInfo.GetValue(thisObject, null);
}
return thisObject;
}
public static T Normolize<T>(this T currentValue, T minValue, T defaultValue)
where T : IComparable<T>
{
return currentValue.CompareTo(minValue) > 0 ? currentValue : defaultValue;
}
public static T CreateClone<T>(this T thisObject)
where T : ICloneable
{
return (T)thisObject.Clone();
}
public static IEnumerable<T> AscendantsOrSelf<T>(this T obj, Func<T, T> GetParent) where T : class
{
if (obj == null) throw new ArgumentNullException("obj");
if (GetParent == null) throw new ArgumentNullException("GetParent");
yield return obj;
T parent = GetParent(obj);
while (parent != null)
{
yield return parent;
parent = GetParent(parent);
}
}
public static string ToStringOrEmpty(this object obj)
{
if (obj == null) return String.Empty;
return obj.ToString();
}
/// <summary>
/// Шифрует исходное сообщение AES ключом (добавляет соль)
/// </summary>
/// <param name="src"></param>
/// <returns></returns>
public static byte[] ToAes256(this string src, byte[] aeskey)
{
//Объявляем объект класса AES
Aes aes = Aes.Create();
//Генерируем соль
aes.GenerateIV();
//Присваиваем ключ. aeskey - переменная (массив байт), сгенерированная методом GenerateKey() класса AES
aes.Key = aeskey;
byte[] encrypted;
ICryptoTransform crypt = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream())
{
using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Write))
{
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(src);
}
}
//Записываем в переменную encrypted зашиврованный поток байтов
encrypted = ms.ToArray();
}
//Возвращаем поток байт + крепим соль
return encrypted.Concat(aes.IV).ToArray();
}
public static string FromAes256(this byte[] shifr, byte[] aeskey)
{
byte[] bytesIv = new byte[16];
byte[] mess = new byte[shifr.Length - 16];
//Списываем соль
for (int i = shifr.Length - 16, j = 0; i < shifr.Length; i++, j++)
bytesIv[j] = shifr[i];
//Списываем оставшуюся часть сообщения
for (int i = 0; i < shifr.Length - 16; i++)
mess[i] = shifr[i];
//Объект класса Aes
Aes aes = Aes.Create();
//Задаем тот же ключ, что и для шифрования
aes.Key = aeskey;
//Задаем соль
aes.IV = bytesIv;
//Строковая переменная для результата
string text = "";
byte[] data = mess;
ICryptoTransform crypt = aes.CreateDecryptor(aes.Key, aes.IV);
using (MemoryStream ms = new MemoryStream(data))
{
using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Read))
{
using (StreamReader sr = new StreamReader(cs))
{
//Результат записываем в переменную text в вие исходной строки
text = sr.ReadToEnd();
}
}
}
return text;
}
public static byte[] ReadAllBytes(this Stream instream)
{
if (instream is MemoryStream)
return ((MemoryStream)instream).ToArray();
using (var memoryStream = new MemoryStream())
{
instream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
public static string ReadFileAes256(this Stream stream,byte[] aeskey)
{
return ReadAllBytes(stream).FromAes256(aeskey);
}
public static void WriteFileAes256(this Stream stream, string data, byte[] aeskey)
{
byte[] dataBytes = data.ToAes256(aeskey);
stream.Write(dataBytes, 0, dataBytes.Length);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace Kit.Helpers
{
public static class SystemWebMvcExtensions
{
/// <summary>
/// Преобразует перечень объектов в перечень элементов html-списка
/// </summary>
/// <typeparam name="T">Тип преобразуемого объекта</typeparam>
/// <param name="items">Перечень преобразуемых объектов</param>
/// <param name="valueSelector">Функция, возвращающая значение, используемое в качестве Value для элемента списка.
/// Для преобразования возвращаемого значения в строку вызывается стандартная реализация ToString()</param>
/// <param name="textSelector">Функция, возвращающая значение, используемое в качестве Text для элемента списка.
/// Для преобразования возвращаемого значения в строку вызывается стандартная реализация ToString()</param>
public static IEnumerable<SelectListItem> ToSelectList<T>(
this IEnumerable<T> items,
Func<T, object> valueSelector,
Func<T, object> textSelector)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
if (textSelector == null) throw new ArgumentNullException("textSelector");
return items.Select(i => new SelectListItem
{
Value = valueSelector(i).ToString(),
Text = textSelector(i).ToString()
});
}
/// <summary>
/// Преобразует перечень объектов в перечень элементов html-списка
/// </summary>
/// <typeparam name="T">Тип преобразуемого объекта</typeparam>
/// <param name="items">Перечень преобразуемых объектов</param>
/// <param name="valueSelector">Функция, возвращающая значение, используемое в качестве Value для элемента списка.</param>
/// <param name="textSelector">Функция, возвращающая значение, используемое в качестве Text для элемента списка.</param>
public static IEnumerable<SelectListItem> ToSelectList<T>(
this IEnumerable<T> items,
Func<T, string> valueSelector,
Func<T, string> textSelector)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
if (textSelector == null) throw new ArgumentNullException("textSelector");
return items.Select(i => new SelectListItem
{
Value = valueSelector(i),
Text = textSelector(i)
});
}
/// <summary>
/// Преобразует перечень объектов в перечень элементов html-списка
/// </summary>
/// <typeparam name="T">Тип преобразуемого объекта</typeparam>
/// <param name="items">Перечень преобразуемых объектов</param>
/// <param name="valueSelector">Функция, возвращающая значение, используемое в качестве Value для элемента списка.</param>
/// <param name="textSelector">Функция, возвращающая значение, используемое в качестве Text для элемента списка.</param>
/// <param name="selectedValues">Список значений, которые необходимо отметить в результирующем списке как выбранные.</param>
public static IEnumerable<SelectListItem> ToSelectList<T, TValue, TText>(
this IEnumerable<T> items,
Func<T, TValue> valueSelector,
Func<T, TText> textSelector,
Func<T, bool> disabled)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
if (textSelector == null) throw new ArgumentNullException("textSelector");
return items.Select(i => new SelectListItem
{
Value = (valueSelector(i) != null ? valueSelector(i).ToString() : string.Empty),
Text = (textSelector(i) != null ? textSelector(i).ToString() : string.Empty),
Selected = disabled(i)
});
}
/// <summary>
/// Преобразует перечень объектов в перечень элементов html-списка
/// </summary>
/// <typeparam name="T">Тип преобразуемого объекта</typeparam>
/// <param name="items">Перечень преобразуемых объектов</param>
/// <param name="valueSelector">Функция, возвращающая значение, используемое в качестве Value для элемента списка.</param>
/// <param name="textSelector">Функция, возвращающая значение, используемое в качестве Text для элемента списка.</param>
/// <param name="selectedValues">Список значений, которые необходимо отметить в результирующем списке как выбранные.</param>
public static IEnumerable<SelectListItem> ToSelectList<T, TValue, TText>(
this IEnumerable<T> items,
Func<T, TValue> valueSelector,
Func<T, TText> textSelector,
TValue selectedValue)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
if (textSelector == null) throw new ArgumentNullException("textSelector");
return items.Select(i => new SelectListItem
{
Value = (valueSelector(i) != null ? valueSelector(i).ToString() : string.Empty),
Text = (textSelector(i) != null ? textSelector(i).ToString() : string.Empty),
Selected = (selectedValue != null && selectedValue.Equals(valueSelector(i)))
});
}
/// <summary>
/// Преобразует перечень объектов в перечень элементов html-списка
/// </summary>
/// <typeparam name="T">Тип преобразуемого объекта</typeparam>
/// <param name="items">Перечень преобразуемых объектов</param>
/// <param name="valueSelector">Функция, возвращающая значение, используемое в качестве Value для элемента списка.</param>
/// <param name="textSelector">Функция, возвращающая значение, используемое в качестве Text для элемента списка.</param>
/// <param name="selectedValues">Список значений, которые необходимо отметить в результирующем списке как выбранные.</param>
public static IEnumerable<SelectListItem> ToSelectList<T, TValue, TText>(
this IEnumerable<T> items,
Func<T, TValue> valueSelector,
Func<T, TText> textSelector,
IEnumerable<TValue> selectedValues)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
if (textSelector == null) throw new ArgumentNullException("textSelector");
return items.Select(i => new SelectListItem
{
Value = (valueSelector(i) != null ? valueSelector(i).ToString() : string.Empty),
Text = (textSelector(i) != null ? textSelector(i).ToString() : string.Empty),
Selected = selectedValues != null && selectedValues.Contains(valueSelector(i))
});
}
private static string GetLambdaAction(LambdaExpression actionSelector)
{
UnaryExpression? unaryExpression = (UnaryExpression)actionSelector.Body;
MethodCallExpression? methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
ConstantExpression? constantExpression = (ConstantExpression)methodCallExpression.Object;
MethodInfo? methodInfo = (MethodInfo)constantExpression.Value;
string actionName = methodInfo.Name;
return actionName;
}
public static string? Action<TController>(this IUrlHelper urlHelper, Expression<Func<TController, Delegate>> actionSelector, object? values = null, string? protocol = null) where TController : Controller
{
// получаем имя контроллера
string controllerName = typeof(TController).Name;
if (controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && controllerName.Length > "Controller".Length)
{
controllerName = controllerName.Remove(controllerName.Length - "Controller".Length, "Controller".Length);
}
return urlHelper.Action(GetLambdaAction(actionSelector), controllerName, values, protocol);
}
}
}

View File

@ -0,0 +1,744 @@
using System.Globalization;
namespace Kit.Helpers
{
using System;
using System.Xml;
using System.Linq;
using System.Collections.Generic;
public enum ItemLogTypes
{
None = 0,
Create = 1,
Update = 2,
Delete = 3,
}
public class ItemLogGenerator
{
public object ObjectId { get; set; }
public string ObjectType { get; set; }
public ItemLogGenerator(object objectId, string objectType)
{
ObjectId = objectId;
ObjectType = objectType;
}
public List<ItemLog> Items { get; set; } = new List<ItemLog>();
public ItemLogGenerator Add(ItemLog item)
{
if (item != null)
{
Items.Add(new ItemLog(ObjectId, ObjectType, item.Name, item.Title) { Type = item.Type, ValueNew = item.ValueNew, ValueOld = item.ValueOld });
}
return this;
}
public ItemLogGenerator AddRange(IEnumerable<ItemLog> items)
{
if (items.IsNullOrEmpty() == false)
{
items.ForEach(item =>
{
Items.Add(new ItemLog(ObjectId, ObjectType, item.Name, item.Title) { Type = item.Type, ValueNew = item.ValueNew, ValueOld = item.ValueOld });
});
}
return this;
}
}
public class ItemLog
{
public ItemLogTypes Type { get; set; }
public ItemLog(string name, string? title = null)
{
Type = ItemLogTypes.None;
Name = name ?? string.Empty;
Title = title ?? string.Empty;
}
public ItemLog(object objectId, string objectType, string name, string? title = null) : this(name, title)
{
ObjectId = objectId;
ObjectType = objectType ?? string.Empty;
}
public object ObjectId { get; set; }
public string ObjectType { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public string ValueOld { get; set; }
public string ValueNew { get; set; }
public ItemLog FillType()
{
bool hasValueOld = string.IsNullOrEmpty(ValueOld) == false;
bool hasValueNew = string.IsNullOrEmpty(ValueNew) == false;
if (hasValueOld == false && hasValueNew == false)
{
Type = ItemLogTypes.None;
return this;
}
if (hasValueOld == false && hasValueNew)
{
Type = ItemLogTypes.Create;
return this;
}
if (hasValueOld && hasValueNew == false)
{
Type = ItemLogTypes.Delete;
return this;
}
if (ValueOld.Equals(ValueNew) == false)
{
Type = ItemLogTypes.Update;
return this;
}
Type = ItemLogTypes.None;
return this;
}
}
public static class SystemXmlNodeExtensionMethods
{
private static readonly CultureInfo ci = new CultureInfo("en-US");
public static void RemoveAttribute(this XmlDocument input, string name)
{
foreach (XmlNode item in input.SelectNodes("//*[@{0}]".ApplyFormat(name)))
{
item.Attributes.Remove(item.Attributes[name]);
}
}
public static XmlNode SetElementValue(this XmlNode input, string name, string value)
{
var element = input.SelectSingleNode(name);
if (element == null)
{
element = input.OwnerDocument.CreateElement(name);
input.AppendChild(element);
}
element.InnerText = value;
return element;
}
public static XmlNode SetElementValue(this XmlNode input, string name, DateTime value)
{
var element = input.SelectSingleNode(name);
if (element == null)
{
element = input.OwnerDocument.CreateElement(name);
input.AppendChild(element);
}
element.InnerText = XmlConvert.ToString(value, XmlDateTimeSerializationMode.RoundtripKind);
return element;
}
public static XmlNode SetElement(this XmlNode input, string name, Object value)
{
var element = input.SelectSingleNode(name);
if (element == null)
{
element = input.OwnerDocument.CreateElement(name);
input.AppendChild(element);
}
element.InnerText = value.ToString();
return element;
}
public static XmlNode SetElementCDataValue(this XmlNode input, string name, string value)
{
var element = input.SelectSingleNode(name);
if (element == null)
{
var root = input.OwnerDocument.CreateElement(name);
element = input.OwnerDocument.CreateNode(XmlNodeType.CDATA, name, null);
root.AppendChild(element);
input.AppendChild(root);
}
element.InnerText = value;
return element;
}
public static XmlNode SetElementValue(this XmlNode input, string name, decimal value)
{
return input.SetElementValue(name, value.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
public static XmlNode SetElementValue(this XmlNode input, string name, decimal? value, string elementsAfterPostion)
{
if (value.HasValue)
{
return input.SetElementNullableValue(name, elementsAfterPostion, value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
else
{
return input.SetElementNullableValue(name, null);
}
}
public static XmlNode SetElementValue(this XmlNode input, string name, int? value, string elementsAfterPostion)
{
if (value.HasValue && value != 0)
{
return input.SetElementNullableValue(name, elementsAfterPostion, value.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
}
else
{
return input.SetElementNullableValue(name, null);
}
}
private static XmlNode GetElementAfterInsert(XmlNode root, string elementsAfterPostion)
{
if (!elementsAfterPostion.IsNullOrEmpty())
{
var elements = root.SelectNodes(elementsAfterPostion);
return elements.Cast<XmlNode>().LastOrDefault();
}
return null;
}
public static XmlNode SetElementNullableValue(this XmlNode input, string name, string value)
{
return input.SetElementNullableValue(name, "", value);
}
public static XmlNode SetElementNullableValue(this XmlNode input, string name, string elementsAfterPostion, string value)
{
if (!value.IsNullOrEmpty())
{
var element = input.SelectSingleNode(name);
if (element != null)
{
input.RemoveChild(element);
}
element = input.OwnerDocument.CreateElement(name);
var after = GetElementAfterInsert(input, elementsAfterPostion);
if (after == null)
input.InsertBefore(element, input.FirstChild);
else
input.InsertAfter(element, after);
element.InnerText = value;
return element;
}
else
{
var child = input.SelectSingleNode(name);
if (child != null)
input.RemoveChild(child);
return null;
}
}
public static XmlNode CreateElement(this XmlNode input, string name) => input.CreateElement(input.OwnerDocument, name);
public static XmlNode CreateElement(this XmlNode input, XmlDocument xmlDocument, string name)
{
//var element = input.SelectSingleNode(name);
//if (element == null)
//{
var element = xmlDocument.CreateElement(name);
input.AppendChild(element);
//}
return element;
}
public static XmlAttribute CreateAttributeValue(this XmlNode input, string name, string value)
{
XmlAttribute element = null;
//try
{
element = input.Attributes[name];
}
//catch
{
if (element == null)
{
element = input.OwnerDocument.CreateAttribute(name);
input.Attributes.Append(element);
}
element.InnerText = value;
}
return element;
}
public static bool HasAttribute(this XmlNode input, string name)
{
return input.Attributes[name] != null;
}
public static void SetAttribute(this XmlNode input, string name, Object value)
{
XmlAttribute element = null;
//try
{
element = input.Attributes[name];
}
//catch { }
if (element == null)
{
element = input.OwnerDocument.CreateAttribute(name);
input.Attributes.Append(element);
}
element.InnerText = value.ToString();
}
public static void SetAttributeNullable(this XmlNode input, string name, string? value)
{
if (string.IsNullOrWhiteSpace(value))
{
input.RemoveAttribute(name);
}
else
{
input.SetAttribute(name, value);
}
}
public static void RemoveAttribute(this XmlNode input, string name)
{
if (input.Attributes == null || input.Attributes.Count == 0) return;
XmlAttribute? element = input.Attributes[name];
if (element == null) return;
((XmlElement)input).RemoveAttribute(name);
}
public static void SetAttributeValue(this XmlNode input, string name, string value)
{
XmlAttribute element = null;
//try
{
element = input.Attributes[name];
}
//catch { }
if (element == null)
{
element = input.OwnerDocument.CreateAttribute(name);
input.Attributes.Append(element);
}
element.InnerText = value;
}
public static void SetAttributeValue(this XmlNode input, string name, Guid? value)
{
XmlAttribute element = null;
//try
{
element = input.Attributes[name];
}
//catch { }
if (element == null)
{
element = input.OwnerDocument.CreateAttribute(name);
input.Attributes.Append(element);
}
element.InnerText = value.ToStringOrEmpty();
}
public static string GetValueAttribute(this XmlNode input, string name)
{
return input.GetValueAttribute(name, string.Empty);
}
public static string GetValueAttribute(this XmlNode input, string name, string empty)
{
XmlAttribute element = null;
//try
{
if (input == null) return empty;
element = input.Attributes[name];
if (element != null)
return element.InnerText;
else
return empty;
}
//catch
//{
// return empty;
//}
}
public static string GetValueAttribute(this XmlNode input, string name, string empty, XmlNamespaceManager namespaces)
{
XmlNode element = null;
//try
{
if (input == null) return empty;
element = input.SelectSingleNode(name, namespaces);
if (element != null)
return element.InnerText;
else
return empty;
}
//catch
//{
// return empty;
//}
}
public static string GetValue(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty);
}
public static DateTime GetValueDateTime(this XmlNode input, string name)
{
return XmlConvert.ToDateTime(input.GetValue(name, String.Empty), XmlDateTimeSerializationMode.RoundtripKind);
}
public static string GetValue(this XmlNode input, string name, string empty)
{
var element = input.SelectSingleNode(name);
if (element == null) return empty;
return element.InnerText;
}
public static string GetValue(this XmlNode input, string name, XmlNamespaceManager namespaces)
{
return input.GetValue(name, String.Empty, namespaces);
}
public static string GetValue(this XmlNode input, string name, string empty, XmlNamespaceManager namespaces)
{
var element = input.SelectSingleNode(name, namespaces);
if (element == null) return empty;
return element.InnerText;
}
public static string GetValueCData(this XmlNode input, string name)
{
return input.GetValueCData(name, string.Empty);
}
public static string GetValueCData(this XmlNode input, string name, string empty)
{
var element = input.SelectSingleNode(name);
if (element == null) return empty;
if (element.FirstChild == null) return element.InnerText;
return element.FirstChild.InnerText;
}
public static DateTime? GetValueDate(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToDateXMLNullable();
}
public static DateTime GetValueDate(this XmlNode input, string name, DateTime empty)
{
return input.GetValue(name, String.Empty).TryParseToDateXMLNullable(empty);
}
public static short GetValueShort(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToShort();
}
public static bool GetValueBool(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToBool(false);
}
public static bool GetValueBool(this XmlNode input, string name, bool empty)
{
return input.GetValue(name, String.Empty).TryParseToBool(empty);
}
public static Int32? GetValueInt32Nulable(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToInt32Nulable();
}
public static Int32 GetValueInt32(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToInt32();
}
public static Int32 GetValueInt32(this XmlNode input, string name, int empty)
{
return input.GetValue(name, String.Empty).TryParseToInt32(empty);
}
public static byte GetValueByte(this XmlNode input, string name, byte empty)
{
return input.GetValue(name, String.Empty).TryParseToByte(empty);
}
public static decimal? GetValueDecimal(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToDecimalNulable();
}
public static decimal GetValueDecimal(this XmlNode input, string name, decimal empty)
{
return input.GetValue(name, String.Empty).TryParseToDecimal(empty);
}
public static Guid GetValueGuid(this XmlNode input, string name, Guid empty)
{
return input.GetValue(name, String.Empty).TryParseToGuid(empty);
}
public static Guid GetValueGuid(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToGuid(Guid.Empty);
}
public static Guid? GetValueGuidNullable(this XmlNode input, string name)
{
return input.GetValue(name, String.Empty).TryParseToGuidNulable(null);
}
public static Guid GetValueAttributeGuid(this XmlNode input, string name)
{
return input.GetValueAttributeGuid(name, null) ?? Guid.Empty;
}
public static Guid? GetValueAttributeGuid(this XmlNode input, string name, Guid? empty)
{
return input.GetValueAttribute(name, String.Empty).TryParseToGuidNulable(empty);
}
public static Guid? GetValueAttributeGuid(this XmlNode input, string name, XmlNamespaceManager namespaces, Guid? empty)
{
return input.GetValueAttribute(name, String.Empty).TryParseToGuidNulable(empty);
}
public static int GetValueAttributeInt32(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToInt32(0);
}
public static int? GetValueAttributeInt32Nullable(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToInt32Nulable();
}
public static double GetValueAttributeDouble(this XmlNode input, string name)
{
return GetValueAttributeDouble(input, name, 0);
}
public static double GetValueAttributeDouble(this XmlNode input, string name, double @default)
{
return input.GetValueAttribute(name, String.Empty).TryParseToDouble(@default);
}
public static double? GetValueAttributeDoubleNullable(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToDoubleNulable();
}
public static long GetValueAttributeInt64(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToInt64(0);
}
public static long? GetValueAttributeInt64Nullable(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToInt64Nulable();
}
public static ulong GetValueAttributeUInt64(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToUInt64(0);
}
public static ulong? GetValueAttributeUInt64Nullable(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToUInt64Nulable();
}
public static bool GetValueAttributeBool(this XmlNode input, string name)
{
return input.GetValueAttribute(name, String.Empty).TryParseToBool(false);
}
public static bool GetValueAttributeBool(this XmlNode input, string name, bool empty)
{
return input.GetValueAttribute(name, String.Empty).TryParseToBool(empty);
}
public static string ToStringXMLShema(this DateTime dt)
{
if (dt == null) return String.Empty;
return dt.ToString("yyyy-MM-ddTHH:mm:ss");
}
public static string ToStringXMLShema(this DateTime? dt)
{
if (dt == null) return String.Empty;
return (dt ?? DateTime.UtcNow).ToString("yyyy-MM-ddTHH:mm:ss");
}
public static string ToStringSoutXML(this DateTime dt)
{
if (dt == null) return String.Empty;
return dt.ToString("yyyy-MM-dd");
}
public static string ToStringSoutXML(this DateTime? dt)
{
if (dt == null) return String.Empty;
return (dt ?? DateTime.UtcNow).ToString("yyyy-MM-dd");
}
public static double GetValueDouble_(this XmlNode input, string name)
{
double value;
if (double.TryParse(input.GetValue(name), NumberStyles.Any, ci.NumberFormat, out value))
return value;
return double.MinValue;
}
public static double GetValueDouble_(this XmlNode input)
{
double value;
if (double.TryParse(input.InnerText, NumberStyles.Any, ci.NumberFormat, out value))
return value;
return double.MinValue;
}
public static void ForEach(this XmlNodeList nodeList, Action<XmlNode> action)
{
foreach (XmlNode node in nodeList)
{
action(node);
}
}
public static IEnumerable<XmlNode> ToList<TResult>(this XmlNodeList nodeList)
{
var resultList = new List<XmlNode>();
foreach (XmlNode node in nodeList)
{
resultList.Add(node);
}
return resultList;
}
public static IEnumerable<TResult> Select<TResult>(this XmlNodeList nodeList, Func<XmlNode, TResult> action)
{
var resultList = new List<TResult>();
foreach (XmlNode node in nodeList)
{
resultList.Add(action(node));
}
return resultList;
}
/// <summary>
/// Get the node located along the <paramref name="path"/>. <br />
/// If the element could not be found, the element itself and the path to it will be created.
/// </summary>
/// <param name="xmlNodeRoot">Root node</param>
/// <param name="path">XPath to the node. XPath should only include tag names and separators.</param>
/// <returns>Target child node</returns>
public static XmlNode GetOrRestoreByPath(this XmlNode xmlNodeRoot, string path)
{
XmlNode xmlNodeTarget = xmlNodeRoot.SelectSingleNode(path);
if (xmlNodeTarget != null)
{
return xmlNodeTarget;
}
string[] pathItems = path.Trim('/').Split('/');
XmlNode xmlNodeCurrent = xmlNodeRoot;
foreach (string pathItem in pathItems)
{
XmlNode xmlNodeItem = xmlNodeCurrent.SelectSingleNode(pathItem);
if (xmlNodeItem == null)
{
xmlNodeItem = xmlNodeCurrent.AppendChild((xmlNodeRoot.OwnerDocument ?? (XmlDocument)xmlNodeRoot).CreateElement(pathItem));
}
xmlNodeCurrent = xmlNodeItem;
}
return xmlNodeCurrent;
}
public static XmlNode RemoveAllEx(this XmlNode xmlNode)
{
xmlNode.RemoveAll();
return xmlNode;
}
public static ItemLog ModifyAttribute(this XmlNode input, ItemLog itemLog, string name, object? value)
{
itemLog = itemLog ?? new ItemLog(string.Empty, string.Empty, name);
itemLog.ValueNew = value?.ToString() ?? string.Empty;
XmlAttribute element = input.Attributes[name];
if (element == null)
{
element = input.OwnerDocument.CreateAttribute(name);
input.Attributes.Append(element);
}
else
{
itemLog.ValueOld = element.InnerText;
}
element.InnerText = itemLog.ValueNew;
return itemLog.FillType();
}
public static ItemLog ModifyElement(this XmlNode input, ItemLog itemLog, string name, object value)
{
itemLog = itemLog ?? new ItemLog(string.Empty, string.Empty, name);
itemLog.ValueNew = value?.ToString() ?? string.Empty;
var element = input.SelectSingleNode(name);
if (element == null)
{
element = input.OwnerDocument.CreateElement(name);
input.AppendChild(element);
}
else
{
itemLog.ValueOld = element.InnerText;
}
element.InnerText = itemLog.ValueNew;
return itemLog.FillType();
}
public static ItemLog ModifyElementCData(this XmlNode input, ItemLog itemLog, string name, string value)
{
itemLog = itemLog ?? new ItemLog(string.Empty, string.Empty, name);
itemLog.ValueNew = value?.ToString() ?? string.Empty;
var element = input.SelectSingleNode(name);
if (element == null)
{
var root = input.OwnerDocument.CreateElement(name);
element = input.OwnerDocument.CreateNode(XmlNodeType.CDATA, name, null);
root.AppendChild(element);
input.AppendChild(root);
}
else
{
itemLog.ValueOld = GetValueCData(element, name);
}
element.InnerText = itemLog.ValueNew;
return itemLog.FillType();
}
}
}

View File

@ -0,0 +1,158 @@
namespace Kit.Helpers
{
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
public class FluentHttpClient
{
private readonly Uri _requestUrl;
private Func<HttpResponseMessage, string, Task<string>> _successHandler = null;
private Func<HttpResponseMessage, string, Task<string>> _errorHandler = null;
private string _securityKey = null;
private readonly List<KeyValuePair<string, string>> _parameters = new List<KeyValuePair<string, string>>();
private readonly HttpMethod _httpMethod;
private string _postStringContent = string.Empty;
private string _postJsonContent = string.Empty;
public FluentHttpClient(Uri requestUrl, HttpMethod httpMethod)
{
_requestUrl = requestUrl;
if (httpMethod != HttpMethod.Get && httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only POST and GET methods supported");
_httpMethod = httpMethod;
}
#region prepare methods
public FluentHttpClient WithParameterNullable<TObject>(string name, TObject value)
{
if (value != null)
{
_parameters.Add(new KeyValuePair<string, string>(name, value?.ToString()));
}
return this;
}
public FluentHttpClient WithParameter<TObject>(string name, TObject value)
{
_parameters.Add(new KeyValuePair<string, string>(name, value?.ToString()));
return this;
}
public FluentHttpClient WithStringContent(string content)
{
if (_httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only for POST method string content supported");
_postStringContent = content;
return this;
}
public FluentHttpClient WithJsonContent<TObject>(TObject obj)
{
if (_httpMethod != HttpMethod.Post) throw new ArgumentOutOfRangeException("httpMethod", "only for POST method string content supported");
_postJsonContent = obj.JsonSerialize();
return this;
}
public FluentHttpClient WithSecurityKey(string securityKey)
{
_securityKey = securityKey;
return this;
}
public FluentHttpClient WithSuccessHandler(Func<HttpResponseMessage, string, Task<string>> responseHandler)
{
_successHandler = responseHandler;
return this;
}
public FluentHttpClient WithErrorHandler(Func<HttpResponseMessage, string, Task<string>> errorHandler)
{
_errorHandler = errorHandler;
return this;
}
#endregion
#region finish methods
public async Task<string> ExecuteRequest()
{
Func<HttpClient, Task<HttpResponseMessage>> createRequestMethod;
if (_httpMethod == HttpMethod.Get)
{
// классический GET запрос
createRequestMethod = async x => await x.GetAsync(_requestUrl.SetQueryParameters(_parameters)).ConfigureAwait(false);
}
else if (!string.IsNullOrWhiteSpace(_postStringContent))
{
// запрос с собственным содержанием тела
createRequestMethod = async x => await x.PostAsync(_requestUrl.SetQueryParameters(_parameters), new StringContent(_postStringContent)).ConfigureAwait(false);
}
else if (!string.IsNullOrWhiteSpace(_postJsonContent))
{
// JSON запрос
createRequestMethod = async x => await x.PostAsync(_requestUrl.SetQueryParameters(_parameters), new StringContent(_postJsonContent, Encoding.UTF8, "application/json")).ConfigureAwait(false);
}
else
{
// классический POST запрос
createRequestMethod = async y => await y.PostAsync(_requestUrl, new FormUrlEncodedContent(_parameters)).ConfigureAwait(false);
}
return await HttpHelper.ExecuteRequest(createRequestMethod, _successHandler, _errorHandler, _securityKey).ConfigureAwait(false);
}
public async Task<Stream> ExecuteStreamRequest()
{
if (_httpMethod == HttpMethod.Get)
{
return await HttpHelper.ExecuteGetStreamUrl(_requestUrl, _parameters, null!, null!, _securityKey).ConfigureAwait(false);
}
return await HttpHelper.ExecutePostStreamUrl(_requestUrl, _parameters, null!, null!, _securityKey).ConfigureAwait(false);
}
#endregion
}
public static class FluentHttpClient_Extensions
{
public static FluentHttpClient PrepareHttpGet(this Uri requestUri)
{
return new FluentHttpClient(requestUri, HttpMethod.Get);
}
public static FluentHttpClient PrepareHttpPost(this Uri requestUri)
{
return new FluentHttpClient(requestUri, HttpMethod.Post);
}
}
}
//_createTokenUrl.HttpRequest()
// .AddHeader("header1", "header1_value")
// .AddCookie("cookie1", "cookie1_value")
// .AddQueryStringParameter("qsp", "qsp_value")
// .AddParameter("lifeTimeInSeconds", lifeTimeInSeconds)
// .AddParameter("data", data)
// .AddParameters(new List<KeyValuePair<string, object>>
// {
// new KeyValuePair<string, object>("k1", "v1"),
// new KeyValuePair<string, object>("k2", "v2")
// })
// .AddParameters(new { a = 5, b = "abc" })
// .SendPost()
// .OnSuccess(response =>
// {
// // HttpResponseMessage response
// })
// .OnError(ex =>
// {
// // exception handling
// })
// .ConvertToObject<string>()
// .ConvertToCollection<string>();

View File

@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
namespace Kit.Helpers
{
public static class HttpContextCore
{
private static IServiceProvider _serviceProvider;
private static bool _notWebApp;
public static void Configure(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_notWebApp = false;
}
public static void ConfigureNotWebApp()
{
_notWebApp = true;
}
public static HttpContext Current
{
get
{
if (_notWebApp)
{
return null;
}
// var factory2 = ServiceProvider.GetService<Microsoft.AspNetCore.Http.IHttpContextAccessor>();
object factory = _serviceProvider.GetService(typeof(IHttpContextAccessor));
// Microsoft.AspNetCore.Http.HttpContextAccessor fac =(Microsoft.AspNetCore.Http.HttpContextAccessor)factory;
HttpContext context = ((HttpContextAccessor)factory).HttpContext;
// context.Response.WriteAsync("Test");
return context;
}
}
private static readonly string _contextHostUrl = "HostUrl";
public static string GetHostUrl(this HttpContext context)
{
context.Items[_contextHostUrl] = context.Items[_contextHostUrl] as string ?? context.GenerateHostUrl();
return context.Items[_contextHostUrl]!.ToString()!;
}
public static string GetRootUrl(this HttpContext context)
{
return $"{GetHostUrl(context)}{context.Request.PathBase}";
}
private static string GenerateHostUrl(this HttpContext context, bool checkForwardedProto = true)
{
HttpRequest request = context.Request;
// Формируем RootUrl
string scheme = request.Scheme;
if (checkForwardedProto && request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues forwardedProtoValues) && forwardedProtoValues.Count > 0)
{
scheme = forwardedProtoValues.First()!;
}
return $"{scheme}://{request.Host}";
}
}
}

View File

@ -0,0 +1,559 @@
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Http;
namespace Kit.Helpers
{
public class FileNameStream : IDisposable
{
public string FileName { get; set; }
public Stream Stream { get; set; }
public void Dispose()
{
if (Stream != null) Stream.Dispose();
}
}
public class HttpRequestException : Exception
{
public HttpRequestException(HttpResponseMessage response, string responseBody)
: base(response.ReasonPhrase)
{
this.Response = response;
this.ResponseBody = responseBody;
}
public string ResponseBody { get; protected set; }
public HttpResponseMessage Response { get; protected set; }
}
public static class HttpHelper_Extensions
{
public static Uri ToUri(this string url)
{
return new Uri(url);
}
public static string Append(this string url, params string[] paths)
{
if (url == null) url = string.Empty;
string[] urlParts = url.Split('?');
url = paths.Aggregate(urlParts[0], (current, path) => string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/')));
if (urlParts.Length > 1) url += "?" + urlParts[1];
return url;
}
public static Uri Append(this Uri uri, params string[] paths)
{
return new Uri(uri.AbsoluteUri.Append(paths));
}
public static Uri SetQueryParameters(this Uri uri, IEnumerable<KeyValuePair<string, string>> parameters)
{
var ub = new UriBuilder(uri);
NameValueCollection nvc = HttpUtility.ParseQueryString(ub.Query);
parameters.ToList().ForEach(x =>
{
nvc[x.Key] = x.Value;
});
ub.Query = nvc.ToString();
return ub.Uri;
}
public static string AbsoluteUrl(this HttpRequest request, bool checkForwardedProto = false)
{
string scheme = request.Scheme;
string host = request.Host.ToUriComponent();
string pathBase = request.PathBase.ToUriComponent();
string path = request.Path.ToUriComponent();
string queryString = request.QueryString.ToUriComponent();
if (checkForwardedProto && request.Headers.TryGetValue("X-Forwarded-Proto", out StringValues forwardedProtoValues) && forwardedProtoValues.Count > 0)
{
scheme = forwardedProtoValues.First();
}
return string.Concat(scheme, "://", host, pathBase, path, queryString);
}
public static string GetUserToken(this HttpRequest request)
{
string _userTokenNameContent = "userToken";
string _userTokenNameHeader = "RiskProf_UserToken";
string? userTokenCookie = request.Cookies[_userTokenNameHeader];
string userToken = request.Query[_userTokenNameContent];
if (string.IsNullOrWhiteSpace(userToken))
{
userToken = request.Headers[_userTokenNameHeader];
}
if (string.IsNullOrWhiteSpace(userToken) && request.HasFormContentType)
{
userToken = request.Form[_userTokenNameContent];
}
if (string.IsNullOrWhiteSpace(userToken) == false)
{
return userToken;
}
return userTokenCookie;
}
public static int? GetCabinetId(this HttpRequest request)
{
string _cabinetIdNameContent = "cabinetId";
string _cabinetIdNameHeader = "RiskProf_CabinetId";
string? cabinetIdCookie = request.Cookies[_cabinetIdNameHeader];
string cabinetId = request.Query[_cabinetIdNameContent];
if (string.IsNullOrWhiteSpace(cabinetId))
{
cabinetId = request.Headers[_cabinetIdNameHeader];
}
if (string.IsNullOrWhiteSpace(cabinetId) && request.HasFormContentType)
{
cabinetId = request.Form[_cabinetIdNameContent];
}
if (string.IsNullOrWhiteSpace(cabinetId) == false)
{
return cabinetId.TryParseToInt32();
}
return cabinetIdCookie.TryParseToInt32();
}
public static string PathAndQuery(this HttpRequest request)
{
return string.Concat(
request.PathBase.ToUriComponent(),
request.Path.ToUriComponent(),
request.QueryString.ToUriComponent());
}
public static StringValues GetValue(this HttpRequest request, string name)
{
var result = StringValues.Empty;
if (request.HasFormContentType)
{
result = request.Form[name];
}
if (result == StringValues.Empty)
{
result = request.Query[name];
}
return result;
}
public static StringValues GetValueWithoutCheck(this HttpRequest request, string name)
{
StringValues result = request.Form[name];
if (result == StringValues.Empty)
{
result = request.Query[name];
}
return result;
}
/// <summary>
/// Метод чтения json модели из тела запроса. Поток запроса можно считать только один раз
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="request"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public static T TryReadJsonModel<T>(this HttpRequest request)
{
try
{
using var reader = new StreamReader(request.Body);
string body = reader.ReadToEndAsync().Result;
return body.JsonDeserialize<T>();
}
catch (Exception ex)
{
throw new InvalidOperationException("Не удалось прочитать json модель из запроса", ex);
}
}
public static void WriteStream(this HttpResponse response, Stream stream, string fileName, string contentType = null)
{
//response.Clear();
//response.ContentType = string.IsNullOrWhiteSpace(contentType) ? "application/octet-stream" : contentType;
//response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}");
//response.SendFileAsync()
//stream.CopyTo(response.OutputStream);
//response.Flush();
//response.End();
}
public static void WriteStream(this HttpResponse response, string filePath, string fileName, string contentType = null)
{
response.Clear();
response.ContentType = string.IsNullOrWhiteSpace(contentType) ? "application/octet-stream" : contentType;
response.Headers.Add("Content-Disposition", $"attachment; filename={fileName}");
response.SendFileAsync(filePath);
}
public static string ReadAuthHeader(this IHeaderDictionary headers)
{
return headers["X-Requested-Token"].FirstOrDefault() ?? String.Empty;
}
public static void WriteStatus(this HttpResponse response, HttpStatusCode statusCode, string message = null, string responseBody = null)
{
if (response == null) return;
byte[] bytes = Encoding.ASCII.GetBytes(message ?? string.Empty);
response.StatusCode = (int)statusCode;
response.Body.WriteAsync(bytes);
}
// для отправки сообщений об ошибках в браузер при ajax-вызовах
public static void WriteError(this HttpResponse response, string message)
{
response.WriteStatus(HttpStatusCode.InternalServerError, message);
}
public static void WriteException(this HttpResponse response, Exception ex)
{
if (ex == null) return;
response.WriteStatus(HttpStatusCode.InternalServerError, ex.Message, ex.GetInfoAsPlainText());
}
public static void SetAuthHeader(this HttpClient client, string securityKey)
{
client.DefaultRequestHeaders.Add("X-Requested-Token", securityKey); // = new AuthenticationHeaderValue()
}
public static string ReadAuthHeader(this HttpRequestHeaders headers)
{
return headers.GetValues("X-Requested-Token").FirstOrDefault() ?? String.Empty;
}
}
public static class HttpHelper
{
public static async Task<TResult> ExecuteGetUrl<TResult>(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
string responseBody = await HttpHelper.ExecuteGetUrl(uri, parameters, responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<string> ExecuteGetUrl(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteRequest(async x => await x.GetAsync(uri.SetQueryParameters(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<Stream> ExecuteGetStreamUrl(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, Stream, Task<Stream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<Stream>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteStreamRequest(async x => await x.GetAsync(uri.SetQueryParameters(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<Stream> ExecutePostStreamUrl(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, Stream, Task<Stream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<Stream>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteStreamRequest(async x => await x.PostAsync(uri, new FormUrlEncodedContent(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<FileNameStream> ExecutePostFileStreamUrl(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, Stream, Task<FileNameStream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<FileNameStream>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteFileStreamRequest(async x => await x.PostAsync(uri, new FormUrlEncodedContent(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<string> ExecutePostUrl(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, new FormUrlEncodedContent(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<TResult> ExecutePostUrl<TResult>(Uri uri
, IEnumerable<KeyValuePair<string, string>> parameters = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
string responseBody = await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, new FormUrlEncodedContent(parameters)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<string> ExecutePostString(Uri uri
, string content = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
return await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, new StringContent(content)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<TResult> ExecutePostString<TResult>(Uri uri
, string content = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
string responseBody = await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, new StringContent(content)).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<string> ExecutePostObject<TObject>(Uri uri
, TObject obj
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
string requestBody = obj.JsonSerialize();
return await HttpHelper.ExecutePostString(uri, requestBody, responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<TResult> ExecutePostObject<TRequest, TResult>(Uri uri
, TRequest obj
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
string requestBody = obj.JsonSerialize();
string responseBody = await HttpHelper.ExecutePostString(uri, requestBody, responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<TResult> ExecutePostFiles<TResult>(Uri uri
, IEnumerable<KeyValuePair<Stream, string>> fileStreamNames
, IEnumerable<KeyValuePair<string, string>> parameters = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
var formData = new MultipartFormDataContent();
if (fileStreamNames.IsNullOrEmpty() == false)
{
int i = 1;
foreach (KeyValuePair<Stream, string> fileStreamName in fileStreamNames)
{
HttpContent fileStreamContent = new StreamContent(fileStreamName.Key);
formData.Add(fileStreamContent, string.Concat("file_", i++.ToString()), fileStreamName.Value);
}
}
if (parameters != null)
{
foreach (var keyValuePair in parameters)
{
HttpContent stringContent = new StringContent(keyValuePair.Value);
formData.Add(stringContent, keyValuePair.Key);
}
}
string responseBody = await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, formData).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<TResult> ExecutePostFiles<TResult>(Uri uri
, IEnumerable<FileStream> files
, IEnumerable<KeyValuePair<string, string>> parameters = null
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string securityKey = null
)
{
var formData = new MultipartFormDataContent();
if (files != null)
{
int i = 1;
foreach (FileStream fileStream in files)
{
HttpContent fileStreamContent = new StreamContent(fileStream);
formData.Add(fileStreamContent, string.Concat("file_", i++.ToString()), fileStream.Name);
}
}
if (parameters != null)
{
foreach (var keyValuePair in parameters)
{
HttpContent stringContent = new StringContent(keyValuePair.Value);
formData.Add(stringContent, keyValuePair.Key);
}
}
string responseBody = await HttpHelper.ExecuteRequest(async x => await x.PostAsync(uri, formData).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
return responseBody.JsonDeserialize<TResult>();
}
public static async Task<Stream> ExecutePostStreamUrl(Uri uri
, IEnumerable<FileStream> files
, IEnumerable<KeyValuePair<string, string>> parameters
, Func<HttpResponseMessage, Stream, Task<Stream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<Stream>> errorHandler = null
, string securityKey = null
)
{
var formData = new MultipartFormDataContent();
if (files != null)
{
int i = 1;
foreach (FileStream fileStream in files)
{
HttpContent fileStreamContent = new StreamContent(fileStream);
formData.Add(fileStreamContent, string.Concat("file_", i++.ToString()), fileStream.Name);
}
}
if (parameters != null)
{
foreach (var keyValuePair in parameters)
{
HttpContent stringContent = new StringContent(keyValuePair.Value);
formData.Add(stringContent, keyValuePair.Key);
}
}
return await HttpHelper.ExecuteStreamRequest(async x => await x.PostAsync(uri, formData).ConfigureAwait(false), responseHandler, errorHandler, securityKey).ConfigureAwait(false);
}
public static async Task<string> ExecuteRequest(
Func<HttpClient, Task<HttpResponseMessage>> createRequest
, Func<HttpResponseMessage, string, Task<string>> responseHandler = null
, Func<HttpResponseMessage, string, Task<string>> errorHandler = null
, string userToken = null
)
{
using (var client = new HttpClient())
{
//client.BaseAddress = new Uri("https://localhost:44354/api/file/createFolder");
client.Timeout = TimeSpan.FromSeconds(3600);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// add auth header
client.SetAuthHeader(userToken);
try
{
using (HttpResponseMessage response = await createRequest(client).ConfigureAwait(false))
{
// считываем содержимое ответа
string responseBody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (response.Content != null) response.Content.Dispose();
// если что-то произошло на стороне сервера
if (!response.IsSuccessStatusCode)
{
if (errorHandler != null) return await errorHandler(response, responseBody).ConfigureAwait(false);
throw new HttpRequestException(response, responseBody);
}
return responseHandler != null
? await responseHandler(response, responseBody).ConfigureAwait(false)
: responseBody;
}
}
catch (Exception ex)
{
throw;
}
}
}
public static async Task<Stream> ExecuteStreamRequest(
Func<HttpClient, Task<HttpResponseMessage>> createRequest
, Func<HttpResponseMessage, Stream, Task<Stream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<Stream>> errorHandler = null
, string securityKey = null
)
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(3600);
// add auth header
client.SetAuthHeader(securityKey);
HttpResponseMessage response = await createRequest(client).ConfigureAwait(false);
// считываем содержимое ответа
Stream responseBody = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
string responseBodyString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
// если что-то произошло на стороне сервера
if (!response.IsSuccessStatusCode)
{
if (errorHandler != null) return await errorHandler(response, responseBody).ConfigureAwait(false);
throw new HttpRequestException(response, responseBodyString);
}
return responseHandler != null
? await responseHandler(response, responseBody).ConfigureAwait(false)
: responseBody;
}
}
public static async Task<FileNameStream> ExecuteFileStreamRequest(
Func<HttpClient, Task<HttpResponseMessage>> createRequest
, Func<HttpResponseMessage, Stream, Task<FileNameStream>> responseHandler = null
, Func<HttpResponseMessage, Stream, Task<FileNameStream>> errorHandler = null
, string securityKey = null
)
{
using (var client = new HttpClient())
{
client.Timeout = TimeSpan.FromSeconds(3600);
// add auth header
client.SetAuthHeader(securityKey);
HttpResponseMessage response = await createRequest(client).ConfigureAwait(false);
string fileName = response.Content.Headers.ContentDisposition.FileName;
// считываем содержимое ответа
Stream responseBody = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
string responseBodyString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
// если что-то произошло на стороне сервера
if (!response.IsSuccessStatusCode)
{
if (errorHandler != null) return await errorHandler(response, responseBody).ConfigureAwait(false);
throw new HttpRequestException(response, responseBodyString);
}
return responseHandler != null
? await responseHandler(response, responseBody).ConfigureAwait(false)
: new FileNameStream { Stream = responseBody, FileName = fileName };
}
}
}
}

View File

@ -0,0 +1,106 @@
using System.Globalization;
using System.Text.Json.Serialization;
using System.Text.Json;
using System;
using System.Text.RegularExpressions;
namespace Kit.Helpers
{
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks;
public static class JsonHelper
{
public static TObject? JsonDeserialize<TObject>(this string json)
{
if (json == null || string.IsNullOrWhiteSpace(json)) return default(TObject);
var options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeIsoFormatConverter());
options.PropertyNameCaseInsensitive = true;
return JsonSerializer.Deserialize<TObject>(json, options);
}
public static string JsonSerialize<TObject>(this TObject serializeObject, bool enableCyrillic = false, bool includeFields = false, bool writeIntended = false, JsonNamingPolicy? propertyNamingPolicy = null)
{
if (serializeObject == null) return "{}";
var options = new JsonSerializerOptions
{
IncludeFields = includeFields,
Encoder = enableCyrillic ? JavaScriptEncoder.Create(UnicodeRanges.BasicLatin, UnicodeRanges.Cyrillic) : JavaScriptEncoder.Create(UnicodeRanges.BasicLatin),
Converters = { new DateTimeIsoFormatConverter() },
WriteIndented = writeIntended,
};
if (propertyNamingPolicy != null)
{
options.PropertyNamingPolicy = propertyNamingPolicy;
}
string result = JsonSerializer.Serialize(serializeObject, options);
return result;
}
public static async Task<TObject> JsonDeserialize<TObject>(this Task<string> task)
{
string json = await task.ConfigureAwait(false);
return await Task<TObject>.Run(() => json.JsonDeserialize<TObject>()).ConfigureAwait(false);
}
}
}
public class DateTimeIsoFormatConverter : JsonConverter<DateTime>
{
private readonly string format = "yyyy-MM-ddTHH:mm";
public DateTimeIsoFormatConverter() { }
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string item = reader.GetString();
if (DateTime.TryParseExact(item, this.format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime result))
{
return result;
}
for (int i = 1; i < 10; i++)
{
string format_item = "yyyy-MM-ddTHH:mm:ss." + new string('f', i);
if (DateTime.TryParseExact(item, format_item, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out DateTime result_item))
{
return result_item;
}
if (DateTime.TryParseExact(item, format_item + "Z", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result_item))
{
return result_item;
}
if (DateTime.TryParseExact(item, format_item + "zzz", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result_item))
{
return result_item;
}
}
Match match = Regex.Match(item, @"\/Date\((\d+)\)\/");
if (match.Success)
{
long milliseconds = long.Parse(match.Groups[1].Value);
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(milliseconds);
}
throw new FormatException();
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
ArgumentNullException.ThrowIfNull(writer, nameof(writer));
writer.WriteStringValue(value
.ToUniversalTime()
.ToString(
this.format,
CultureInfo.InvariantCulture));
}
}

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.5" />
<PackageReference Include="System.Management" Version="9.0.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\LibExternal\Npgsql\Npgsql.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,222 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace Kit.Helpers.Log
{
public interface ICallStackLog
{
string LogMessage { get; }
ICallStackLog NextLevel();
void FinishLevel();
void BeginCall(string message);
void EndCall();
}
public class EmptyCallStackLog : ICallStackLog
{
public string LogMessage
{
get { return ""; }
}
public ICallStackLog NextLevel()
{
return CallStackLog.Empty;
}
public void FinishLevel()
{
return;
}
public void BeginCall(string message)
{
return;
}
public void EndCall()
{
return;
}
}
public class CallStackLog : ICallStackLog
{
protected ConcurrentDictionary<string, long> Logs { get; }
protected ConcurrentBag<CallStackLog> ChildCallStackLogs { get; set; }
protected readonly int _level;
protected readonly string _path;
protected string _currentCall;
protected int _number;
protected readonly Stopwatch _stopwatch = new Stopwatch();
public static ICallStackLog Empty { get { return new EmptyCallStackLog(); } }
protected string Number
{
get
{
return _number.ToString("D5");
}
}
public CallStackLog()
{
Logs = new ConcurrentDictionary<string, long>();
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
_level = 0;
_path = "";
_number = 1;
}
public CallStackLog(int level, string path)
{
Logs = new ConcurrentDictionary<string, long>();
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
_level = level;
_path = path;
_number = 1;
}
public string LogMessage
{
get
{
StringBuilder stringBuilder = new StringBuilder();
foreach (var logLine in Logs.OrderBy(x => x.Key))
{
string time = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000}", logLine.Value / 1000.0f);
stringBuilder.AppendLine(logLine.Key + $". Time - {time} ms");
}
return stringBuilder.ToString();
}
}
public ConcurrentDictionary<string, long> GetLogs
{
get {
return this.Logs;
}
}
public ICallStackLog NextLevel()
{
var childCallStackLog = new CallStackLog(_level + 1, _path + this.Number + ".");
ChildCallStackLogs.Add(childCallStackLog);
return childCallStackLog;
}
public void FinishLevel()
{
foreach (CallStackLog childCallStackLog in ChildCallStackLogs)
{
lock (Logs)
{
foreach (var logLine in childCallStackLog.Logs)
{
if (Logs.ContainsKey(logLine.Key))
{
Logs[logLine.Key] += logLine.Value;
}
else
{
Logs[logLine.Key] = logLine.Value;
}
}
}
}
ChildCallStackLogs = new ConcurrentBag<CallStackLog>();
}
public void BeginCall(string message)
{
_currentCall = message;
_stopwatch.Restart();
}
public void EndCall()
{
_stopwatch.Stop();
lock (Logs)
{
long ticksByMicrosecond = Stopwatch.Frequency / (1000L * 1000L);
long microSeconds = _stopwatch.ElapsedTicks / ticksByMicrosecond;
Logs.TryAdd($"{_path}{this.Number} {_currentCall.ToStringOrEmpty()}", microSeconds);
_number++;
}
}
}
public class CallStackLogFile : ICallStackLog
{
protected object _lock = new object();
protected ConcurrentBag<CallStackLogFile> ChildCallStackLogs { get; set; }
protected readonly int _level;
protected readonly string _path;
protected string _currentCall;
protected int _number;
protected readonly Stopwatch _stopwatch = new Stopwatch();
protected readonly StreamWriter _writer;
public static ICallStackLog Empty { get { return new EmptyCallStackLog(); } }
protected string Number
{
get
{
return _number.ToString("D5");
}
}
public CallStackLogFile(StreamWriter writer)
{
_writer = writer;
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
_level = 0;
_path = "";
_number = 1;
}
public CallStackLogFile(StreamWriter writer, int level, string path)
{
_writer = writer;
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
_level = level;
_path = path;
_number = 1;
}
public string LogMessage => string.Empty;
public ICallStackLog NextLevel()
{
var childCallStackLog = new CallStackLogFile(_writer, _level + 1, _path + this.Number + ".");
ChildCallStackLogs.Add(childCallStackLog);
return childCallStackLog;
}
public void FinishLevel()
{
ChildCallStackLogs = new ConcurrentBag<CallStackLogFile>();
}
public void BeginCall(string message)
{
_currentCall = message;
_stopwatch.Restart();
}
public void EndCall()
{
_stopwatch.Stop();
lock (_lock)
{
long microSeconds = _stopwatch.ElapsedTicks / (Stopwatch.Frequency / (1000L * 1000L));
string time = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:0.000}", microSeconds / 1000.0f);
_writer?.WriteLine($"{_path}{this.Number} {_currentCall.ToStringOrEmpty()}. Time - {time} ms");
_number++;
}
}
}
}

View File

@ -0,0 +1,44 @@
namespace Kit.Helpers.Log
{
public class CallStackLogDB: CallStackLog, IDisposable
{
protected string _sessionKey;
protected LogMessageType _type;
protected string _target;
protected string _action;
protected DateTime _dateCreated;
private bool disposed = false;
public CallStackLogDB(string sessionKey, LogMessageType type, string target, string action):base()
{
_sessionKey = sessionKey;
_type = type;
_target = target;
_action = action;
_dateCreated = DateTime.UtcNow;
}
public void Dispose()
{
Dispose(true);
// подавляем финализацию
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
this.Logs.TryAdd($"Full Time", Convert.ToInt64((DateTime.UtcNow - _dateCreated).TotalMilliseconds));
Kit.Helpers.Log.LoggerDB.Commit(this._sessionKey, this._type, this._target, this._action, _dateCreated, this.Logs.ToDictionary(x => x.Key, x => x.Value));
}
// освобождаем неуправляемые объекты
disposed = true;
}
}
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Mail;
using System.Text;
using Microsoft.Extensions.Configuration;
namespace Kit.Helpers.Log
{
public enum LogMessageType
{
Debug = 1,
Info,
Warn,
Error,
}
public static class Logger
{
private static string _pathLogFolder;
private static string _emailas;
private static bool _isSendEmail;
public static void Configure(IConfiguration configuration)
{
var config = new ConfigurationHelper(configuration, "Log");
_pathLogFolder = config.GetAppSettingValue("PathFolder");
if (_pathLogFolder.IsNullOrEmpty() || Directory.Exists(_pathLogFolder) == false)
_pathLogFolder = Path.GetTempPath();
_isSendEmail = config.GetAppSettingValue("IsSendMail").TryParseToBool(false);
_emailas = config.GetAppSettingValue("Emails");
}
public static string GetCurrentFullFileName()
{
var dt = DateTime.UtcNow;
return Path.Combine(_pathLogFolder, String.Format("log_{0}_{1}_{2}.log", dt.Day, dt.Month, dt.Year));
}
public static string GetNameMessageType(LogMessageType type)
{
switch (type)
{
case LogMessageType.Debug:
return "Дебаг";
case LogMessageType.Error:
return "Ошибка";
case LogMessageType.Info:
return "Информация";
case LogMessageType.Warn:
return "Предупреждение";
}
return String.Empty;
}
public static void Log(LogMessageType type, string log)
{
string path = GetCurrentFullFileName();
using (var file = File.AppendText(path))
{
var str = String.Format("{0}:({1}) {2}" + Environment.NewLine, DateTime.UtcNow, GetNameMessageType(type), log);
file.WriteLine(str);
if (_isSendEmail && type == LogMessageType.Error)
SendMail(new StringBuilder(str));
}
}
public static void Log(LogMessageType type, string log, params object[] args)
{
Log(type, String.Format(log, args));
}
public static void LogError(string header, string error)
{
using (var file = File.AppendText(GetCurrentFullFileName()))
{
file.WriteLine(String.Format("{0}:({1}) {2}", DateTime.UtcNow, GetNameMessageType(LogMessageType.Error), header));
file.WriteLine(String.Format("Trace: {0}" + Environment.NewLine, error));
}
}
public static void LogInfo(string log, params object[] args)
{
Log(LogMessageType.Info, String.Format(log, args));
}
public static void LogWarn(string log, params object[] args)
{
Log(LogMessageType.Warn, String.Format(log, args));
}
public static void SendMail(StringBuilder errorText)
{
List<MailMessage> gettersMailMessages = new List<MailMessage>();
List<string> Getters = new List<string>();
foreach (var mail in _emailas.Replace(",", ";").Split(';'))
{
Getters.Add(mail);
}
if (!string.IsNullOrEmpty(errorText.ToString()))
{
foreach (var getter in Getters)
{
var mail = new MailMessage
{
From = new MailAddress("noreply@aetalon.ru", "Ошибка Эталон"),
Subject = "Ошибка Эталон",
IsBodyHtml = false,
Body = errorText.ToString()
};
mail.Body = mail.Body.Replace("_EMAIL_RECEIVER_", Base64Encode(errorText.ToString()));
mail.To.Add(getter);
gettersMailMessages.Add(mail);
}
}
using (var smtp = new SmtpClient())
{
foreach (var mail in gettersMailMessages)
{
try
{
smtp.Send(mail);
}
catch (Exception e)
{
}
}
}
}
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
}
}

View File

@ -0,0 +1,137 @@
using Microsoft.Extensions.Configuration;
using System.Collections.Concurrent;
namespace Kit.Helpers.Log
{
public enum TypeInfo { Debug = 1, Info, Warn, Error, }
public class LoggerKey : IDisposable
{
private bool disposed = false;
public Guid Key { get; set; }
public void Dispose()
{
Dispose(true);
// подавляем финализацию
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
LoggerDB.Commit(this);
}
// освобождаем неуправляемые объекты
disposed = true;
}
}
// Деструктор
~LoggerKey()
{
Dispose(false);
}
}
public class LoggerItems
{
public string SessionKey { get; set; }
public LogMessageType LogMessageType { get; set; }
public DateTime DateCreated { get; set; }
public string Target { get; set; }
public string Action { get; set; }
public string Message { get; set; }
public double Duration { get; set; }
}
public static class LoggerDB
{
private static string _connectionString;
private static bool _isRun;
private static ConcurrentDictionary<Guid, LoggerItems> _items = new ConcurrentDictionary<Guid, LoggerItems>();
public static void Configure(IConfiguration configuration)
{
_connectionString = "";// new ConfigurationHelper("Logger") ConfigurationManager.ConnectionStrings["Logger.Db"].ConnectionString;
var config = new ConfigurationHelper(configuration, "LogDb");
_isRun = config.GetAppSettingValue("IsRun").TryParseToBool(false);
}
public static LoggerKey Log(string sessionKey, LogMessageType type, string target, string action, string message)
{
return Log(sessionKey, type, DateTime.UtcNow, target, action, message);
}
public static LoggerKey Log(string sessionKey, LogMessageType type, DateTime dateCreated, string target, string action, string message)
{
if (_isRun)
{
Guid key = Guid.NewGuid();
_items.TryAdd(key, new LoggerItems
{
SessionKey = sessionKey,
Action = action,
DateCreated = dateCreated,
Duration = 0,
LogMessageType = type,
Message = message,
Target = target
});
return new LoggerKey { Key = key };
}
return new LoggerKey { Key = Guid.Empty }; ;
}
public static void Commit(LoggerKey key, DateTime? finishDate = null)
{
if (_items.ContainsKey(key.Key))
{
LoggerItems item = _items.GetByKey(key.Key);
double duration = ((finishDate ?? DateTime.UtcNow) - item.DateCreated).TotalMilliseconds;
//_connectionString.ExecuteScalar<int>("[Logs].[Insert_Logger]", true, new List<KeyValuePair<string, object>>
//{
// new KeyValuePair<string, object>("@LogMessageType", (int)item.LogMessageType),
// new KeyValuePair<string, object>("@SessionKey", item.SessionKey),
// new KeyValuePair<string, object>("@DateCreate", item.DateCreated),
// new KeyValuePair<string, object>("@Target", item.Target),
// new KeyValuePair<string, object>("@Action", item.Action),
// new KeyValuePair<string, object>("@Message", item.Message),
// new KeyValuePair<string, object>("@Duration", duration),
//});
_items.TryRemove(key.Key, out item!);
}
}
public static void Commit(params LoggerKey[] keys)
{
foreach (LoggerKey key in keys)
{
Commit(key);
}
}
public static void Commit(string sessionKey, LogMessageType type, string target, string action, DateTime dateTime, Dictionary<string, long> logs)
{
foreach (var log in logs)
{
//_connectionString.ExecuteScalar<int>("[Logs].[Insert_Logger]", true, new List<KeyValuePair<string, object>>
//{
// new KeyValuePair<string, object>("@LogMessageType", (int)type),
// new KeyValuePair<string, object>("@SessionKey", sessionKey),
// new KeyValuePair<string, object>("@DateCreate", dateTime),
// new KeyValuePair<string, object>("@Target", target),
// new KeyValuePair<string, object>("@Action", action),
// new KeyValuePair<string, object>("@Message", log.Key),
// new KeyValuePair<string, object>("@Duration", log.Value),
//});
}
}
}
}

View File

@ -0,0 +1,92 @@
namespace Kit.Helpers.Log
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
public class OperationResult
{
public string Title { get; set; }
public int Level { get; set; }
public int Count { get; set; }
public TimeSpan Elapsed { get; set; }
public OperationResult()
{
Title = string.Empty;
}
}
public interface IOperationLogger
{
void Log(string operation, int level, Action? action);
IEnumerable<OperationResult> GetOperationResults();
string GetOperationResultsAsText();
}
public class EmptyOperationLogger : IOperationLogger
{
public IEnumerable<OperationResult> GetOperationResults()
{
return new List<OperationResult>();
}
public string GetOperationResultsAsText()
{
return "";
}
public void Log(string operation, int level, Action? action)
{
if (action != null) action();
}
}
public class OperationLogger : IOperationLogger
{
private readonly IDictionary<string, OperationResult> _operationResults;
public OperationLogger()
{
_operationResults = new Dictionary<string, OperationResult>();
}
public static IOperationLogger Empty { get { return new EmptyOperationLogger(); } }
public void Log(string operation, int level, Action? action)
{
var stopwatch = new Stopwatch();
try
{
stopwatch.Start();
if (action != null) action();
}
finally
{
stopwatch.Stop();
OperationResult operationResult = _operationResults.GetByKey(operation);
if (operationResult == null)
{
operationResult = new OperationResult { Title = operation, Count = 0, Level = level, Elapsed = new TimeSpan() };
_operationResults.Add(operation, operationResult);
}
operationResult.Count++;
operationResult.Elapsed += stopwatch.Elapsed;
}
}
public IEnumerable<OperationResult> GetOperationResults()
{
return _operationResults.Values;
}
public string GetOperationResultsAsText()
{
string result = "";
foreach (var operationResult in _operationResults)
{
result += operationResult.Value.Title + Environment.NewLine;
}
return result;
}
}
}

View File

@ -0,0 +1,156 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Kit.Helpers;
public enum StackTraceModesJson
{
None = 0,
Text = 1,
Html = 2
}
public enum StackTraceModesHtml
{
None = 0,
Text = 1,
Html = 2,
CommentText = 3,
CommentHtml = 4,
}
public interface IConfigurationExceptionHandler
{
StackTraceModesJson StackTraceModeJson { get; }
StackTraceModesHtml StackTraceModeHtml { get; }
}
public class ConfigurationExceptionHandler : IConfigurationExceptionHandler
{
public StackTraceModesJson StackTraceModeJson { get; private set; }
public StackTraceModesHtml StackTraceModeHtml { get; private set; }
public ConfigurationExceptionHandler(Microsoft.Extensions.Configuration.IConfiguration configuration)
{
var helper = new ConfigurationHelper(configuration, "ExceptionHandler");
StackTraceModeJson = helper.GetAppSettingValue("StackTraceModeJson", required: false, defaultValue: StackTraceModesJson.None.ToString()).ParseToEnum<StackTraceModesJson>();
StackTraceModeHtml = helper.GetAppSettingValue("StackTraceModeHtml", required: false, defaultValue: StackTraceModesHtml.None.ToString()).ParseToEnum<StackTraceModesHtml>();
}
}
public static class ExceptionHandlerMiddlewareExt
{
public static IServiceCollection InitExceptionHandlerMiddleware(this IServiceCollection builder, IConfigurationExceptionHandler configuration)
{
ExceptionHandlerMiddleware.Init(configuration);
return builder;
}
}
public class ExceptionHandlerMiddleware
{
private static IConfigurationExceptionHandler? _exceptionHandler;
private static StackTraceModesJson _stackTraceModeJson => _exceptionHandler?.StackTraceModeJson ?? StackTraceModesJson.None;
private static StackTraceModesHtml _stackTraceModeHtml => _exceptionHandler?.StackTraceModeHtml ?? StackTraceModesHtml.None;
private readonly RequestDelegate _next;
public static void Init(IConfigurationExceptionHandler exceptionHandler)
{
_exceptionHandler = exceptionHandler;
}
public ExceptionHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
int statusCode = 0;
string errorMessage = string.Empty;
string errorStackTrace = string.Empty;
string? redirectUrl = null;
bool forceJson = false;
switch (ex)
{
case AuthenticationException exception:
statusCode = StatusCodes.Status401Unauthorized;
errorMessage = exception.Message;
redirectUrl = exception.RedirectUrl;
forceJson = true;
break;
case AuthorizationException exception:
statusCode = StatusCodes.Status403Forbidden;
errorMessage = exception.Message;
redirectUrl = exception.RedirectUrl;
forceJson = true;
break;
case InvalidOperationException exception:
statusCode = StatusCodes.Status409Conflict;
errorMessage = exception.Message;
break;
default:
statusCode = StatusCodes.Status500InternalServerError;
errorMessage = "На сервере произошла неизвестная ошибка" + Environment.NewLine + ex.Message;
break;
}
context.Response.StatusCode = statusCode;
if (forceJson || context.NeedResponseJsonResult(ex))
{
switch (_stackTraceModeJson)
{
case StackTraceModesJson.Text:
errorStackTrace = ex?.GetInfoAsPlainText() ?? string.Empty;
break;
case StackTraceModesJson.Html:
errorStackTrace = ex?.GetInfoAsHtml() ?? string.Empty;
break;
}
string json = new JsonResultError
{
ErrorMessage = errorMessage,
ErrorStackTrace = errorStackTrace,
RedirectUrl = redirectUrl
}.JsonSerialize(enableCyrillic: true, propertyNamingPolicy: System.Text.Json.JsonNamingPolicy.CamelCase);
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.WriteAsync(json, System.Text.Encoding.UTF8);
}
else
{
switch (_stackTraceModeHtml)
{
case StackTraceModesHtml.Text:
errorStackTrace = ex.GetInfoAsPlainText() ?? string.Empty;
break;
case StackTraceModesHtml.Html:
errorStackTrace = ex.GetInfoAsHtml() ?? string.Empty;
break;
case StackTraceModesHtml.CommentText:
errorStackTrace = "<!-- " + ex.GetInfoAsPlainText() ?? string.Empty + " -->";
break;
case StackTraceModesHtml.CommentHtml:
errorStackTrace = "<!-- " + ex.GetInfoAsHtml() ?? string.Empty + " -->";
break;
}
errorMessage += Environment.NewLine + errorStackTrace;
context.Response.ContentType = "text/html; charset=utf-8";
await context.Response.WriteAsync(errorMessage, System.Text.Encoding.UTF8);
}
}
}
}

View File

@ -0,0 +1,77 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Microsoft.AspNetCore.Mvc.TagHelpers;
/// <summary>
/// Methods for determining how an <see cref="ITagHelper"/> should run based on the attributes that were specified.
/// </summary>
internal static class AttributeMatcher
{
/// <summary>
/// Determines the most effective mode a <see cref="ITagHelper" /> can run in based on which modes have
/// all their required attributes present.
/// </summary>
/// <typeparam name="TMode">The type representing the <see cref="ITagHelper" />'s modes.</typeparam>
/// <param name="context">The <see cref="TagHelperContext"/>.</param>
/// <param name="modeInfos">The modes and their required attributes.</param>
/// <param name="compare">A comparer delegate.</param>
/// <param name="result">The resulting most effective mode.</param>
/// <returns><c>true</c> if a mode was determined, otherwise <c>false</c>.</returns>
public static bool TryDetermineMode<TMode>(
TagHelperContext context,
ModeAttributes<TMode>[] modeInfos,
Func<TMode, TMode, int> compare,
out TMode result)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(modeInfos);
ArgumentNullException.ThrowIfNull(compare);
var foundResult = false;
result = default;
// Perf: Avoid allocating enumerator
var allAttributes = context.AllAttributes;
// Read interface .Count once rather than per iteration
var allAttributesCount = allAttributes.Count;
foreach (var modeInfo in modeInfos)
{
var requiredAttributes = modeInfo.Attributes;
// If there are fewer attributes present than required, one or more of them must be missing.
if (allAttributesCount >= requiredAttributes.Length &&
!HasMissingAttributes(allAttributes, requiredAttributes) &&
compare(result, modeInfo.Mode) <= 0)
{
foundResult = true;
result = modeInfo.Mode;
}
}
return foundResult;
}
private static bool HasMissingAttributes(ReadOnlyTagHelperAttributeList allAttributes, string[] requiredAttributes)
{
// Check for all attribute values
// Perf: Avoid allocating enumerator
for (var i = 0; i < requiredAttributes.Length; i++)
{
if (!allAttributes.TryGetAttribute(requiredAttributes[i], out var attribute))
{
// Missing attribute.
return true;
}
if (attribute.Value is string valueAsString && string.IsNullOrEmpty(valueAsString))
{
// Treat attributes with empty values as missing.
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,68 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Razor.TagHelpers;
namespace Kit.Helpers.Mvc.TagHelpers
{
[HtmlTargetElement(Attributes = "asp-viewmodel", TagStructure = TagStructure.NormalOrSelfClosing)]
public class BootstrapTagHelper : TagHelper
{
[HtmlAttributeName("asp-viewmodel")]
public BaseViewModel Model { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
base.Process(context, output);
if (Model == null || Model.NeedLoadBootstrap || output.Attributes.IsNullOrEmpty())
{
return;
}
IEnumerable<KeyValuePair<string, string>> keyValuePairs = output.Attributes!.Select(x => new KeyValuePair<string, string>(x.Name, x.Value?.ToString() ?? string.Empty));
var keyValuePairsNew = new List<KeyValuePair<string, string>>();
keyValuePairs.ForEach(x =>
{
string key = x.Key;
string value = x.Value?.ToString() ?? string.Empty;
if (x.Key.ToLowerEquals("class"))
{
value = ProcessClassAttributeValue(value);
}
else if (x.Key.ToLowerStartsWith("data-bs-"))
{
key = ProcessDataBsAttributeName(x.Key);
}
keyValuePairsNew.Add(new KeyValuePair<string, string>(key, value));
});
output.Attributes.Clear();
keyValuePairsNew.ForEach(x => output.Attributes.SetAttribute(x.Key, x.Value));
}
private string ProcessClassAttributeValue(string value)
{
return (value ?? string.Empty).Split(' ').ToList().Select(x =>
{
if (Model.NeedDowngradeBootstrap)
{
if (x.StartsWith("ms-")) return "ml-" + x.Substring(3);
if (x.StartsWith("me-")) return "mr-" + x.Substring(3);
if (x.StartsWith("ps-")) return "pl-" + x.Substring(3);
if (x.StartsWith("pe-")) return "pr-" + x.Substring(3);
}
if (Model.NeedDowngradeFaIcons)
{
if (x.ToLowerEquals("fa-solid")) return "fa";
}
return x;
}).Join(" ");
}
private string ProcessDataBsAttributeName(string value)
{
return "data-" + value.Substring("data-bs-".Length);
}
}
}

View File

@ -0,0 +1,254 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using System.Text.Encodings.Web;
namespace Kit.Helpers.Mvc.TagHelpers
{
public class FileTagHelperContext
{
public enum LoadEntityType
{
Script = 1,
Stylesheet = 2,
}
public enum PriorityChangeMode
{
Same = 1,
After = 2,
}
public class File
{
public File(string url, LoadEntityType type, uint priority)
{
Url = url;
Type = type;
Priority = priority;
Loaded = false;
}
public string Url { get; }
public LoadEntityType Type { get; }
public uint Priority { get; }
public bool Loaded { get; }
}
public FileTagHelperContext(HttpContext? httpContext, bool needDynamicLoad, string allFilesLoadedEventName)
{
HttpContext = httpContext;
IsDynamicLoad = needDynamicLoad;
EventNameAllFilesLoaded = allFilesLoadedEventName;
Files = new List<File>();
_priority = 0;
}
public HttpContext? HttpContext { get; }
public bool IsDynamicLoad { get; }
public string EventNameAllFilesLoaded { get; }
public bool NeedPrependDomain { get; init; }
public bool NeedAppendVersion { get; init; }
public List<File> Files { get; }
private uint _priority;
public void SetPriorityAfter()
{
_priority++;
}
public void AddFile(string url, LoadEntityType entityType, PriorityChangeMode mode)
{
if (Files.Any(x => x.Url == url)) return;
if (mode == PriorityChangeMode.After) SetPriorityAfter();
Files.Add(new File(url, entityType, _priority));
}
}
[HtmlTargetElement(TagName, Attributes = $"[{PathExactAttributeName}^='~/']", TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement(TagName, Attributes = TypeAttributeName, TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement(TagName, Attributes = ContextAttributeName, TagStructure = TagStructure.WithoutEndTag)]
public class FileTagHelper : Microsoft.AspNetCore.Mvc.Razor.TagHelpers.UrlResolutionTagHelper
{
private const string TagName = "file";
private const string TypeAttributeName = "type";
private const string PriorityModeAttributeName = "priority-mode";
private const string ContextAttributeName = "asp-context";
private const string PathExactAttributeName = "asp-path-exact";
private const string PathIncludeAttributeName = "asp-path-include";
private const string PathExcludeAttributeName = "asp-path-exclude";
#pragma warning disable
/// <summary> Creates a new <see cref="FileTagHelper"/>. </summary>
public FileTagHelper(IWebHostEnvironment hostingEnvironment, TagHelperMemoryCacheProvider cacheProvider, IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, JavaScriptEncoder javaScriptEncoder, IUrlHelperFactory urlHelperFactory) : base(urlHelperFactory, htmlEncoder)
{
HostingEnvironment = hostingEnvironment;
Cache = cacheProvider.Cache;
FileVersionProvider = fileVersionProvider;
}
#pragma warning enable
/// <inheritdoc />
public override int Order => -1000;
[HtmlAttributeName(ContextAttributeName)]
public FileTagHelperContext Context { get; set; }
[HtmlAttributeName(PathExactAttributeName)]
public string Path { get; set; }
[HtmlAttributeName(TypeAttributeName)]
public FileTagHelperContext.LoadEntityType Type { get; set; }
[HtmlAttributeName(PriorityModeAttributeName)]
public FileTagHelperContext.PriorityChangeMode PriorityChangeMode { get; set; } = FileTagHelperContext.PriorityChangeMode.Same;
/// <summary> A comma separated list of globbed file patterns to load. The glob patterns are assessed relative to the application's 'webroot' setting. </summary>
[HtmlAttributeName(PathIncludeAttributeName)]
public string PathInclude { get; set; }
/// <summary> A comma separated list of globbed file patterns to exclude from loading. The glob patterns are assessed relative to the application's 'webroot' setting. Must be used in conjunction with <see cref="PathInclude"/>. </summary>
[HtmlAttributeName(PathExcludeAttributeName)]
public string PathExclude { get; set; }
/// <summary> Gets the <see cref="IWebHostEnvironment"/> for the application. </summary>
protected internal IWebHostEnvironment HostingEnvironment { get; }
/// <summary> Gets the <see cref="IMemoryCache"/> used to store globbed urls. </summary>
protected internal IMemoryCache Cache { get; }
/// <summary> Gets the <see cref="GlobbingUrlBuilder"/> used to populate included and excluded urls. </summary>
// Internal for ease of use when testing.
protected internal GlobbingUrlBuilder GlobbingUrlBuilder { get; set; }
internal IFileVersionProvider FileVersionProvider { get; private set; }
/// <inheritdoc />
public override void Process(TagHelperContext context, TagHelperOutput output)
{
if (context == null) throw new ArgumentNullException(nameof(context));
if (output == null) throw new ArgumentNullException(nameof(output));
var builder = output.PostElement;
builder.Clear();
output.TagName = null;
output.Content.SetContent(string.Empty);
IEnumerable<string> urls = SelectUrls();
urls = urls.Where(url =>
{
if (string.IsNullOrWhiteSpace(url)) return false;
string mimeType = url.GetMimeType();
switch (Type)
{
case FileTagHelperContext.LoadEntityType.Stylesheet:
return url.GetMimeType() == ".css".GetMimeType();
case FileTagHelperContext.LoadEntityType.Script:
return url.GetMimeType() == ".js".GetMimeType();
default:
return false;
}
});
urls.ForEach((url, index) =>
{
string urlFull = GetVersionedUrl(url);
FileTagHelperContext.PriorityChangeMode mode = FileTagHelperContext.PriorityChangeMode.Same;
if (index == 0)
{
mode = PriorityChangeMode;
}
Context.AddFile(urlFull, Type, mode);
});
}
private GlobbingUrlBuilder EnsureGlobbingUrlBuilder()
{
if (GlobbingUrlBuilder == null)
{
GlobbingUrlBuilder = new GlobbingUrlBuilder(HostingEnvironment.WebRootFileProvider, Cache, ViewContext.HttpContext.Request.PathBase);
}
return GlobbingUrlBuilder;
}
private IEnumerable<string> SelectUrls()
{
var result = new List<string>();
if (Path != null)
{
result.Add(Path);
}
if (string.IsNullOrWhiteSpace(PathInclude) == false)
{
IReadOnlyList<string>? urls = EnsureGlobbingUrlBuilder().BuildUrlList(null, PathInclude, PathExclude);
if (urls.IsNullOrEmpty() == false)
{
result.AddRange(urls);
}
}
return result;
}
private IFileVersionProvider EnsureFileVersionProvider()
{
if (FileVersionProvider == null)
{
FileVersionProvider = ViewContext.HttpContext.RequestServices.GetRequiredService<IFileVersionProvider>();
}
return FileVersionProvider;
}
private string GetVersionedUrl(string url)
{
if (Context.NeedAppendVersion == true)
{
url = EnsureFileVersionProvider().AddFileVersionToPath(ViewContext.HttpContext.Request.PathBase, url);
}
if (Context.NeedPrependDomain && Context.HttpContext != null)
{
string rootUrl = Context.HttpContext.GetRootUrl();
// Заменяем символ "~" на домен
if (url.StartsWith("~"))
{
url = rootUrl + url.Substring(1);
}
else if (url.StartsWith("/"))
{
url = rootUrl + url;
}
}
return url;
}
}
}

View File

@ -0,0 +1,62 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Text.Encodings.Web;
namespace Kit.Helpers.Mvc.TagHelpers
{
[HtmlTargetElement("link", Attributes = "asp-context", TagStructure = TagStructure.WithoutEndTag)]
[HtmlTargetElement("link", Attributes = "asp-prepend-domain", TagStructure = TagStructure.WithoutEndTag)]
public class LinkTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper
{
public LinkTagHelper(IWebHostEnvironment hostingEnvironment, TagHelperMemoryCacheProvider cacheProvider, IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, JavaScriptEncoder javaScriptEncoder, IUrlHelperFactory urlHelperFactory) : base(hostingEnvironment, cacheProvider, fileVersionProvider, htmlEncoder, javaScriptEncoder, urlHelperFactory)
{
}
[HtmlAttributeName("asp-context")]
public HttpContext? HttpContext { get; set; }
[HtmlAttributeName("asp-prepend-domain")]
public bool NeedPrependDomain { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
base.Process(context, output);
if (NeedPrependDomain && HttpContext != null)
{
string rootUrl = HttpContext.GetRootUrl();
TagHelperAttribute attributeHref = output.Attributes["href"];
if (attributeHref != null)
{
var href = output.Attributes["href"].Value.ToString();
// Заменяем символ "~" на ваш домен
if (href.StartsWith("~"))
{
href = rootUrl + href.Substring(1);
}
else if (href.StartsWith("/"))
{
href = rootUrl + href;
}
output.Attributes.SetAttribute("href", href);
}
else if (output.PostElement != null && output.PostElement.IsEmptyOrWhiteSpace == false)
{
string str = output.PostElement.GetContent();
str = str.Replace("href=\"/", $"href=\"{rootUrl}/").Replace("href=\"~/", $"href=\"{rootUrl}/");
output.PostElement.SetHtmlContent(str);
;
}
}
}
}
}

View File

@ -0,0 +1,29 @@
namespace Microsoft.AspNetCore.Mvc.TagHelpers;
/// <summary>
/// A mapping of a <see cref="AspNetCore.Razor.TagHelpers.ITagHelper"/> mode to its required attributes.
/// </summary>
/// <typeparam name="TMode">The type representing the <see cref="AspNetCore.Razor.TagHelpers.ITagHelper"/>'s mode.</typeparam>
internal sealed class ModeAttributes<TMode>
{
/// <summary>
/// Initializes a new instance of <see cref="ModeAttributes{TMode}"/>.
/// </summary>
/// <param name="mode">The <see cref="AspNetCore.Razor.TagHelpers.ITagHelper"/>'s mode.</param>
/// <param name="attributes">The names of attributes required for this mode.</param>
public ModeAttributes(TMode mode, string[] attributes)
{
Mode = mode;
Attributes = attributes;
}
/// <summary>
/// Gets the <see cref="AspNetCore.Razor.TagHelpers.ITagHelper"/>'s mode.
/// </summary>
public TMode Mode { get; }
/// <summary>
/// Gets the names of attributes required for this mode.
/// </summary>
public string[] Attributes { get; }
}

View File

@ -0,0 +1,120 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Text.Encodings.Web;
namespace Kit.Helpers.Mvc.TagHelpers
{
//public class ScriptIdCollection : List<string>
//{
//}
[HtmlTargetElement("script", Attributes = "asp-context")]
[HtmlTargetElement("script", Attributes = "asp-prepend-domain")]
[HtmlTargetElement("script", Attributes = "asp-await-collection")]
public class ScriptTagHelper : Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper
{
public ScriptTagHelper(IWebHostEnvironment hostingEnvironment, TagHelperMemoryCacheProvider cacheProvider, IFileVersionProvider fileVersionProvider, HtmlEncoder htmlEncoder, JavaScriptEncoder javaScriptEncoder, IUrlHelperFactory urlHelperFactory) : base(hostingEnvironment, cacheProvider, fileVersionProvider, htmlEncoder, javaScriptEncoder, urlHelperFactory)
{
}
[HtmlAttributeName("asp-context")]
public HttpContext? HttpContext { get; set; }
[HtmlAttributeName("asp-prepend-domain")]
public bool NeedPrependDomain { get; set; }
//[HtmlAttributeName("asp-await-collection")]
//public ScriptIdCollection? AwaitCollection { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
base.Process(context, output);
if (NeedPrependDomain && HttpContext != null)
{
string rootUrl = HttpContext.GetRootUrl();
TagHelperAttribute attributeSrc = output.Attributes["src"];
if (attributeSrc != null)
{
var src = output.Attributes["src"].Value.ToString();
// Заменяем символ "~" на ваш домен
if (src.StartsWith("~"))
{
src = rootUrl + src.Substring(1);
}
else if (src.StartsWith("/"))
{
src = rootUrl + src;
}
output.Attributes.SetAttribute("src", src);
//if (AwaitCollection != null)
//{
// TagHelperAttribute attributeId = output.Attributes["id"];
// string id = attributeId?.Value?.ToString() ?? "script".AppendToken();
// output.Attributes.SetAttribute("id", id);
// AwaitCollection.Add(id);
//}
}
else if (output.PostElement != null && output.PostElement.IsEmptyOrWhiteSpace == false)
{
string str = output.PostElement.GetContent();
str = str.Replace("src=\"/", $"src=\"{rootUrl}/").Replace("src=\"~/", $"src=\"{rootUrl}/");
output.PostElement.SetHtmlContent(str);
;
}
}
}
}
//[HtmlTargetElement("scriptawaiter")]
//public class ScriptAwaiterTagHelper : TagHelper
//{
// public const string EventNameAllLoaded = "allScriptsLoaded";
// [HtmlAttributeName("asp-await-collection")]
// public ScriptIdCollection? AwaitCollection { get; set; }
// public override void Init(TagHelperContext context)
// {
// base.Init(context);
// }
// public override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
// {
// output.TagName = "script";
// output.TagMode = TagMode.StartTagAndEndTag;
// output.Attributes.SetAttribute("class", "scriptawaiter");
// var stringBuilder = new StringBuilder();
// stringBuilder.AppendLine("");
// stringBuilder.AppendLine("const awaitingScripts = " + AwaitCollection?.ToDictionary(x => x, x=> false).JsonSerialize(true) ?? "{}");
// stringBuilder.AppendLine("console.log(awaitingScripts);");
// stringBuilder.AppendLine("$('script[src]').on('load', function() {");
// {
// stringBuilder.AppendLine("var itemId = this.id;");
// stringBuilder.AppendLine("console.log('file loaded: ' + itemId);");
// stringBuilder.AppendLine("if (Object.keys(awaitingScripts).indexOf(itemId) != -1) {");
// stringBuilder.AppendLine("awaitingScripts[itemId] = true;");
// stringBuilder.AppendLine("var allLoaded = true;");
// stringBuilder.AppendLine("Object.keys(awaitingScripts).forEach(key => { if (awaitingScripts[key] == false) allLoaded = false; });");
// stringBuilder.AppendLine("if (allLoaded) { window.dispatchEvent(new Event('" + EventNameAllLoaded + "')); };");
// stringBuilder.AppendLine("}");
// }
// stringBuilder.AppendLine("});");
// output.Content.SetHtmlContent(stringBuilder.ToString());
// return Task.CompletedTask;
// }
//}
}

View File

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Kit.Helpers.Repository.Converter
{
public interface IConverter<TInput, TOutput>
{
TOutput Convert(TInput input);
}
public interface IEnumerableConverter<TInput, TOutput> :
IConverter<IEnumerable<TInput>, IEnumerable<TOutput>> { }
}

View File

@ -0,0 +1,63 @@
using System.Xml;
namespace Kit.Helpers.Repository
{
public enum ContentTypes
{
Xml = 0,
Sqlite = 1,
}
public interface IFileContent
{
ContentTypes Type { get; }
object Content { get; }
}
public abstract class FileContent : IFileContent
{
public abstract ContentTypes Type { get; }
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public object Content { get; set; }
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
}
public class FileContentXml : FileContent
{
public override ContentTypes Type => ContentTypes.Xml;
public FileContentXml(XmlDocument xmlDocument)
{
Content = xmlDocument;
}
}
public class FileContentSqlite : FileContent
{
public override ContentTypes Type => ContentTypes.Sqlite;
public IConnectionString ConnectionString => (IConnectionString)Content;
public FileContentSqlite(IConnectionString connectionString)
{
Check.IsTrue(connectionString.Type.Equals(ConnectionStringType.SQLite), $"value ({nameof(connectionString)}.{nameof(connectionString.Type)} = {connectionString.Type.ToString()}) is invalid - {ConnectionStringType.SQLite.ToString()} was expected");
Content = connectionString;
}
}
public interface IContent<TContentKey>
{
public ContentTypes Type { get; }
IFileContent GetContentDataDefault();
IFileContent? GetContentData(TContentKey key);
bool CreateContentData(TContentKey key, IFileContent content, bool skipIfExists = true);
void UpdateContentData(TContentKey key, IFileContent content);
int GetRowVersion(IFileContent document);
}
public abstract class ContentBase<TContentKey>
{
public abstract ContentTypes Type { get; }
public void CheckFileContent(IFileContent content)
{
Check.IsTrue(content.Type.Equals(Type), $"value ({nameof(content)}.{nameof(content.Type)} = {content.Type.ToString()}) is invalid - {Type.ToString()} was expected");
}
}
}

View File

@ -0,0 +1,82 @@
using Microsoft.Extensions.Caching.Memory;
using Kit.Helpers.Cache;
using Kit.Helpers.Repository.Xml;
using System;
using System.Collections.Generic;
namespace Kit.Helpers.Repository
{
public interface IDictionaryRepository<TKey, TEntity>
{
IDictionary<TKey, TEntity> Select();
TEntity Get(TKey key);
void ClearCache();
}
public abstract class DictionaryRepository<TKey, TEntity> : IDictionaryRepository<TKey, TEntity>
where TEntity : IPersistentKey<TKey>
{
private string _cacheKey;
private ICacheProvider _cacheProvider;
public DictionaryRepository(ICacheProvider cacheProvider, string cacheKey)
{
_cacheProvider = cacheProvider;
_cacheKey = cacheKey;
}
// лучше сделать абстрактным
protected abstract IEnumerable<TEntity> GetData();
public IDictionary<TKey, TEntity> Select()
{
IDictionary<TKey, TEntity> values;
if (_cacheKey.IsNullOrEmpty())
{
throw new ArgumentNullException("Не задан ключ кеширования для репозитория: {0}".ApplyFormat(this.GetType().ToString()));
}
if (_cacheProvider.Contains(_cacheKey))
{
values = _cacheProvider.GetCacheData<IDictionary<TKey, TEntity>>(_cacheKey);
}
else
{
IEnumerable<TEntity> data = this.GetData();
values = new Dictionary<TKey, TEntity>();
// добавить обработку дубликатов
foreach (var item in data)
{
TKey key = item.PersistentKey;
if (values.ContainsKey(key))
{
throw new Exception("Элемент с ключом {0} уже добавлен в коллекцию".ApplyFormat(key));
}
values.Add(item.PersistentKey, item);
}
_cacheProvider.SetCacheData(_cacheKey, values,
new MemoryCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1),
Priority = CacheItemPriority.Normal
});
}
return values;
}
public TEntity Get(TKey key)
{
IDictionary<TKey, TEntity> values = this.Select();
if (values.ContainsKey(key))
return values[key];
else
return default(TEntity);
}
public void ClearCache()
{
_cacheProvider.PurgeCacheItems(_cacheKey);
}
}
}

View File

@ -0,0 +1,22 @@
using Kit.Helpers.Log;
namespace Kit.Helpers.Repository
{
public interface ISelectContext
{
IDictionary<object, object?> Items { get; }
ICallStackLog CallStackLog { get; set; }
}
public class SelectContext : ISelectContext
{
public SelectContext()
{
Items = new Dictionary<object, object?>();
CallStackLog = Log.CallStackLog.Empty;
}
public IDictionary<object, object?> Items { get; }
public ICallStackLog CallStackLog { get; set; }
}
}

View File

@ -0,0 +1,9 @@
using System.Linq;
namespace Kit.Helpers.Repository.Paging
{
public interface IPageFilter
{
IQueryable<TEntity> ChangePage<TEntity>(IQueryable<TEntity> query);
}
}

View File

@ -0,0 +1,19 @@
namespace Kit.Helpers.Repository.Paging
{
public interface ISqlPageFilter
{
int TotalRows { get; set; }
int? PageNo { get; set; }
int? PageSize { get; set; }
string Sort { get; set; }
}
public class SqlPageFilter:ISqlPageFilter
{
public int TotalRows { get; set; }
public int? PageNo { get; set; }
public int? PageSize { get; set; }
public string Sort { get; set; }
}
}

View File

@ -0,0 +1,72 @@
using System.Linq;
namespace Kit.Helpers.Repository.Paging
{
/// <summary>
/// Фильтр с полями постраничной фильтрации.
/// <para>Применяется, например в PagingController</para>
/// </summary>
public class PagingByPage : IPageFilter
{
public int? Start { get; set; }
public int? Lenght { get; set; }
public string SortColumnName { get; set; }
public string SortDirection { get; set; }
public string Tabs { get; set; }
public string pageParameterId { get; set; }
/// <summary>
/// Номер страницы. Нумерация с 1-цы
/// </summary>
public int? PageNo { get; set; }
public int? PageSize { get; set; }
public int? TotalRows { get; set; }
public int? CountPages { get; set; }
/// <summary>
/// Есть значения для разбиения на страницы
/// </summary>
public bool HasSize
{
get
{
return
this.PageNo.HasValue &&
(this.PageNo > 0) &&
this.PageSize.HasValue &&
(this.PageSize > 0);
}
}
public string FormId
{
get
{
return _formId;
}
set
{
_formId = value;
}
}
public IQueryable<TEntity> ChangePage<TEntity>(IQueryable<TEntity> query)
{
if (query == null) return null;
if (!(this.PageNo.HasValue && this.PageSize.HasValue)) return query;
return query.Skip((this.PageNo.Value - 1) * this.PageSize.Value).Take(this.PageSize.Value);
}
#region Fields ---------------------------------------
/// <summary>
/// Для передачи в частичное представление Id формы, которая владеет просмотром страниц
/// </summary>
private string _formId = "";
#endregion
}
}

View File

@ -0,0 +1,21 @@
namespace Kit.Helpers.Repository.Paging
{
public class PagingDatatable
{
//public int? Start { get; set; }
//public int? Length { get; set; }
public string SearchValue { get; set; }
//public string SortColumnName { get; set; }
//public string SortDirection { get; set; }
//public int TotalCount { get; set; }
//public int FilterCount { get; set; }
public int? Start { get; set; }
public int? Length { get; set; }
public int FilterRow { get; set; }
public int TotalRow { get; set; }
public string SortColumnName { get; set; }
public string SortDirection { get; set; }
}
}

View File

@ -0,0 +1,57 @@
//using RiskProf.Files.Domain;
//using Kit.Helpers.SQLite;
//namespace Kit.Helpers.DbEmbedded.Repository
//{
// public class SqlFileVersionInfo
// {
// public SqlFileVersionInfo()
// {
// Type = string.Empty;
// Version = 0;
// }
// public string Type { get; set; }
// public long Version { get; set; }
// }
// public interface ISqlFileVersionRepository
// {
// SqlFileVersionInfo? Get(SQLiteFile file);
// void Set(SQLiteFile file, SqlFileVersionInfo info);
// }
// public class SqlFileVersionRepository : ISqlFileVersionRepository
// {
// public SqlFileVersionRepository()
// {
// }
// private const string _createIfNotExists = "create table if not exists fileVersion(type text, version integer); ";
// public SqlFileVersionInfo? Get(SQLiteFile file)
// {
// return file.PrepareExecute<SqlFileVersionInfo>(_createIfNotExists + "select type, version from fileVersion")
// .AsSqlText()
// .AddConverter((record, list) =>
// {
// list.Add(new SqlFileVersionInfo
// {
// Type = record.Get<string>("type"),
// Version = record.Get<long>("version"),
// });
// })
// .ExecuteSelectMany()
// .SingleOrDefault();
// }
// public void Set(SQLiteFile file, SqlFileVersionInfo info)
// {
// file.PrepareExecute(_createIfNotExists + "delete from fileVersion; insert into fileVersion (type, version) values (@type, @version)")
// .AsSqlText()
// .AddParameter("type", info.Type)
// .AddParameter("version", info.Version)
// .ExecuteNonQuery();
// }
// }
//}

View File

@ -0,0 +1,188 @@
using Kit.Helpers.Service;
namespace Kit.Helpers.Repository
{
public abstract class SQLiteFileRepository<TContentKey> : ContentBase<TContentKey>, IContent<TContentKey>
{
public override ContentTypes Type => ContentTypes.Sqlite;
protected readonly ILockService<TContentKey> _lockService;
protected readonly ISQLiteVersionService _sqLiteVersionService;
/// <summary> Ключ типа файла для версионности </summary>
protected readonly string _fileTypeKey;
/// <summary> </summary>
/// <param name="lockService"></param>
/// <param name="sqLiteVersionService"></param>
/// <param name="fileTypeKey">Ключ типа файла для версионности</param>
public SQLiteFileRepository(
ISQLiteVersionService sqLiteVersionService,
ISQLiteGlobalVarRepository sqLiteGlobalVarRepository,
string fileTypeKey
)
{
_lockService = new LockService<TContentKey>();
_sqLiteVersionService = sqLiteVersionService;
_fileTypeKey = fileTypeKey;
}
protected abstract Stream? ReadFile(TContentKey key);
protected abstract bool ExistsFile(TContentKey key);
protected abstract void SaveFile(TContentKey key, Stream stream);
private void CheckFileContentType(IFileContent fileContent)
{
Check.IsTrue(fileContent is FileContentSqlite, $"{nameof(fileContent)} is not {nameof(FileContentSqlite)}");
}
private FileContentSqlite CreateTempFile() => new FileContentSqlite(new ConnectionString
{
Type = ConnectionStringType.SQLite,
Value = Path.GetTempFileName()
});
public IFileContent GetContentData(TContentKey key)
{
return _lockService.Lock(key, () =>
{
bool isUpdated = false;
var fileContent = CreateTempFile();
try
{
using (Stream? srcStream = ReadFile(key))
{
Check.IsNotNull(srcStream, $"{nameof(srcStream)} is null");
using (FileStream fileStream = File.OpenWrite(fileContent.ConnectionString.Value))
{
srcStream.CopyTo(fileStream);
}
}
isUpdated = _sqLiteVersionService.UpdateByTypes(fileContent);
if (isUpdated)
{
ExecSave(key, fileContent);
}
return fileContent;
}
finally
{
File.Delete(fileContent.ConnectionString.Value);
}
});
}
public IFileContent GetContentDataDefault()
{
FileContentSqlite fileContent = CreateTempFile();
_sqLiteVersionService.AppendType(fileContent, FileTypeKeys.SQLiteAll);
_sqLiteVersionService.AppendType(fileContent, _fileTypeKey);
_sqLiteVersionService.UpdateByTypes(fileContent);
return fileContent;
}
public bool CreateContentData(TContentKey key, IFileContent content, bool skipIfExists = true)
{
CheckFileContentType(content);
return _lockService.Lock(key, () =>
{
if (ExistsFile(key))
{
if (skipIfExists) return false;
Check.IsNotTrue(true, "Документ уже существует");
}
ExecSave(key, (FileContentSqlite)content);
return true;
});
}
public void UpdateContentData(TContentKey key, IFileContent file)
{
Check.IsTrue(file is FileContentSqlite, $"{nameof(file)} is not {nameof(FileContentSqlite)}");
_lockService.Lock(key, () =>
{
ExecSave(key, (FileContentSqlite)file);
});
}
private void ExecSave(TContentKey key, FileContentSqlite fileContent)
{
using (Stream stream = File.OpenRead(fileContent.ConnectionString.Value))
{
SaveFile(key, stream);
}
}
public int GetRowVersion(IFileContent fileContent)
{
CheckFileContentType(fileContent);
return _sqLiteVersionService.GetRowVersion((FileContentSqlite)fileContent);
}
}
public class SQLiteFileFullPathRepository : SQLiteFileRepository<string>
{
public SQLiteFileFullPathRepository(ISQLiteVersionService sqLiteVersionService, ISQLiteGlobalVarRepository sqLiteGlobalVarRepository, string fileTypeKey) : base(sqLiteVersionService, sqLiteGlobalVarRepository, fileTypeKey) { }
protected override bool ExistsFile(string fullPath)
{
return File.Exists(fullPath);
}
protected override Stream? ReadFile(string fullPath)
{
return File.Open(fullPath, FileMode.OpenOrCreate);
}
protected override void SaveFile(string fullPath, Stream stream)
{
using (Stream streamFile = File.Open(fullPath, FileMode.Create))
{
long posOld = 0;
if (stream.CanSeek)
posOld = stream.Position;
stream.CopyTo(streamFile);
if (stream.CanSeek)
stream.Position = posOld;
}
}
public void Init(string fullPath)
{
if (File.Exists(fullPath))
GetContentData(fullPath);
else
CreateContentData(fullPath, GetContentDataDefault(), true);
}
}
public interface ISQLiteFileFullPathFactory
{
SQLiteFileFullPathRepository Create(string fileTypeKey);
}
public class SQLiteFileFullPathFactory : ISQLiteFileFullPathFactory
{
private readonly ISQLiteVersionService _sqLiteVersionService;
private readonly ISQLiteGlobalVarRepository _sqLiteGlobalVarRepository;
public SQLiteFileFullPathFactory(ISQLiteVersionService sqLiteVersionService, ISQLiteGlobalVarRepository sqLiteGlobalVarRepository)
{
_sqLiteVersionService = sqLiteVersionService;
_sqLiteGlobalVarRepository = sqLiteGlobalVarRepository;
}
public SQLiteFileFullPathRepository Create(string fileTypeKey) => new SQLiteFileFullPathRepository(_sqLiteVersionService, _sqLiteGlobalVarRepository, fileTypeKey);
}
}

View File

@ -0,0 +1,48 @@
namespace Kit.Helpers.Repository
{
public interface ISQLiteGlobalVarRepository
{
string? Get(FileContentSqlite fileContentSqlite, string key);
void Set(FileContentSqlite fileContentSqlite, string key, string? value);
}
public class SQLiteGlobalVarRepository : ISQLiteGlobalVarRepository
{
public SQLiteGlobalVarRepository() { }
private const string _createIfNotExists = "create table if not exists global_var (key text not null primary key, value text not null); ";
private const string _getByKey = "select main.value from global_var main where main.key = :key; ";
private const string _deleteByKey = "delete from global_var where main.key = :key; ";
private const string _setByKey = "insert into global_var (key, value) values (:key, :value); ";
public string? Get(FileContentSqlite fileContentSqlite, string key)
{
return fileContentSqlite.ConnectionString.PrepareExecute(_createIfNotExists + _getByKey)
.AsSqlText()
.WithStrictSyntax()
.AddParameter("key", key)
.ExecuteScalar<string?>();
}
public void Set(FileContentSqlite fileContentSqlite, string key, string? value)
{
string request = _createIfNotExists + _deleteByKey;
if (string.IsNullOrWhiteSpace(value) == false)
{
request += _setByKey;
}
else
{
value = null;
}
fileContentSqlite.ConnectionString.PrepareExecute(request)
.AsSqlText()
.WithStrictSyntax()
.AddParameter("key", key)
.AddParameterIfNotNull("value", value)
.ExecuteNonQuery();
}
}
}

View File

@ -0,0 +1,52 @@
namespace Kit.Helpers.Repository
{
public class SQLiteFileVersionInfo
{
public SQLiteFileVersionInfo()
{
Type = string.Empty;
Version = 0;
}
public string Type { get; set; }
public long Version { get; set; }
}
public interface ISQLiteVersionRepository
{
IEnumerable<SQLiteFileVersionInfo> Select(FileContentSqlite fileContentSqlite);
void Set(FileContentSqlite fileContentSqlite, SQLiteFileVersionInfo info);
}
public class SQLiteVersionRepository : ISQLiteVersionRepository
{
public SQLiteVersionRepository() { }
private const string _createIfNotExists = "create table if not exists file_version (type text not null primary key, version integer not null); ";
private const string _select = "select type, version from file_version; ";
private const string _setByType = "delete from file_version where type = :type; insert into file_version (type, version) values (:type, :version); ";
public IEnumerable<SQLiteFileVersionInfo> Select(FileContentSqlite fileContentSqlite)
{
return fileContentSqlite.ConnectionString.PrepareExecute<SQLiteFileVersionInfo>(_createIfNotExists + _select)
.AsSqlText()
.WithStrictSyntax()
.AddConverter((record, list) => list.Add(new SQLiteFileVersionInfo
{
Type = record.Get<string>("type"),
Version = record.Get<long>("version"),
}))
.ExecuteSelectMany();
}
public void Set(FileContentSqlite fileContentSqlite, SQLiteFileVersionInfo info)
{
fileContentSqlite.ConnectionString.PrepareExecute(_createIfNotExists + _setByType)
.AsSqlText()
.WithStrictSyntax()
.AddParameter("type", info.Type)
.AddParameter("version", info.Version)
.ExecuteNonQuery();
}
}
}

View File

@ -0,0 +1,162 @@
using Kit.Helpers;
using Kit.Helpers.Repository.Converter;
using Kit.Helpers.Repository.Xml.Exceptions;
using System;
using System.Linq;
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public abstract class EntityXmlPersistentComplexRepository<TEntity, TKey, TRootKey, TFilter, TContextKey> :
EntityXmlSelectComplexRepository<TEntity, TFilter, TKey, TRootKey, TContextKey>,
IPersistentXml2Repository<TEntity, TKey, TFilter, TContextKey>
where TEntity : XmlComplexClass<TKey, TRootKey, TContextKey>
where TFilter : IXmlComplexFilter<TContextKey, TRootKey, TKey>
{
protected string _elementsAfterPostion;
public EntityXmlPersistentComplexRepository(string elementName) : base(elementName)
{
}
public EntityXmlPersistentComplexRepository(string elementName, IConverter<XmlNode, TEntity> converter) : base(elementName, converter)
{
}
public EntityXmlPersistentComplexRepository(string rootElementName, string path, string elementName) : base(rootElementName, path, elementName)
{
}
public EntityXmlPersistentComplexRepository(string path, string elementName) : base(path, elementName)
{
}
public EntityXmlPersistentComplexRepository(string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(path, elementName, converter)
{
}
public EntityXmlPersistentComplexRepository(string rootElementName, string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(rootElementName, path, elementName, converter)
{
}
public EntityXmlPersistentComplexRepository(string elementsAfterPostion, string rootElementName, string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(rootElementName, path, elementName, converter)
{
_elementsAfterPostion = elementsAfterPostion;
}
protected virtual XmlNode GetElementAfterInsert(XmlNode root, XmlNode persistent = null)
{
if (!string.IsNullOrEmpty(_elementsAfterPostion))
{
var elements = root.SelectNodes(_elementsAfterPostion);
return elements.Cast<XmlNode>().LastOrDefault();
}
return null;
}
protected abstract IEnumerable<ItemLog> SetPersistentFromEntity(XmlDocument db, XmlNode persistent, TEntity entity);
protected virtual bool OnInserting(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
protected virtual bool OnInserted(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
protected virtual bool OnUpdating(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
protected virtual bool OnUpdated(XmlDocument db, XmlNode persistent, TEntity entity, IEnumerable<ItemLog> itemLogs)
{
if (itemLogs.IsNullOrEmpty() == false)
{
ItemLogEventManager.Invoke(entity.ContentId, itemLogs);
}
return true;
}
public virtual void Delete(XmlDocument db, TKey key)
{
var persistent = SelectNodesByKey(db, key).Cast<XmlNode>().AsQueryable().FirstOrDefault();
persistent.ParentNode.RemoveChild(persistent);
}
protected override sealed XmlNodeList SelectNodesByKey(XmlDocument db, TKey key)
{
return base.SelectNodesByKey(db, key);
}
public virtual TEntity Get(XmlDocument db, TKey key)
{
return Get(db, key, default);
}
public virtual TEntity Get(XmlDocument db, TKey key, TFilter filter)
{
XmlNodeList list = SelectNodesByKey(db, key);
return this.FillResult(db, list.Cast<XmlNode>().AsQueryable().ToList().Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter)), filter).SingleOrDefault();
}
public virtual bool Exists(XmlDocument db, TKey key)
{
XmlNodeList list = SelectNodesByKey(db, key);
return list.Count > 0;
}
protected virtual System.Linq.Expressions.Expression<Func<XmlNode, bool>> GetByKeyExpression(TKey key)
{
return x => x.Attributes["guid"].InnerText == key.ToString();
}
public IEnumerable<ItemLog> Update(XmlDocument db, TEntity entity)
{
var persistent = SelectNodesByKey(db, entity.PersistentKey).Cast<XmlNode>().AsQueryable().FirstOrDefault();
IEnumerable<ItemLog>? itemLogs = null;
if (this.OnUpdating(db, persistent, entity))
{
//int row = persistent.GetValueAttributeInt32("rowVersion");
int row = entity.RowVersion;
if (row != entity.RowVersion) throw new RowVersionException("Данные были изменены версии записи не совпадают");
itemLogs = this.SetPersistentFromEntity(db, persistent, entity);
persistent.SetAttributeValue("rowVersion", (++row).ToString());
this.OnUpdated(db, persistent, entity, itemLogs);
}
return itemLogs ?? Array.Empty<ItemLog>();
}
public TKey Insert(XmlDocument db, TEntity entity)
{
XmlNode root = GetRootNode(db, entity.RootId);
XmlNode parent = GetParentNode(root);
XmlNode persistent = parent.OwnerDocument.CreateNode(XmlNodeType.Element, _elementName, "");
this.SetPersistentFromEntity(parent.OwnerDocument, persistent, entity);
if (this.OnInserting(db, persistent, entity))
{
var after = GetElementAfterInsert(parent, persistent);
if (after == null) parent.InsertBefore(persistent, parent.FirstChild);
else
{
parent.InsertAfter(persistent, after);
}
this.OnInserted(db, persistent, entity);
}
return entity.PersistentKey;
}
public TKey InsertToNode(XmlNode db, TEntity entity)
{
XmlNode parent = GetParentNode(db);
XmlNode persistent = parent.OwnerDocument.CreateNode(XmlNodeType.Element, _elementName, "");
this.SetPersistentFromEntity(parent.OwnerDocument, persistent, entity);
var after = GetElementAfterInsert(parent, persistent);
if (after == null) parent.InsertBefore(persistent, parent.FirstChild);
else
{
parent.InsertAfter(persistent, after);
}
return entity.PersistentKey;
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.Collections.Generic;
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public abstract class EntityXmlPersistentComplexRepositoryWithCache<TEntity, TKey, TRootKey, TFilter, TContextKey> : EntityXmlPersistentComplexRepository<TEntity, TKey, TRootKey, TFilter, TContextKey>
where TEntity : XmlComplexClass<TKey, TRootKey, TContextKey>
where TFilter : IXmlComplexFilter<TContextKey, TRootKey, TKey>
{
private string _cacheKey;
protected bool _cacheOn;
private void CacheInit()
{
_cacheKey = this.GetType().FullName;
_cacheOn = true;
}
public EntityXmlPersistentComplexRepositoryWithCache(string elementName) : base(elementName)
{
this.CacheInit();
}
public EntityXmlPersistentComplexRepositoryWithCache(string path, string elementName) : base(path, elementName)
{
this.CacheInit();
}
private Dictionary<string, TEntity> GetCacheDictionary()
{
return null;
//HttpContext httpContext = HttpContext.Current;
//if (httpContext == null) return null;
//var cacheDictionary = (Dictionary<string, TEntity>)httpContext.Items[_cacheKey];
//if (cacheDictionary == null)
//{
// cacheDictionary = new Dictionary<string, TEntity>();
// httpContext.Items[_cacheKey] = cacheDictionary;
//}
//return cacheDictionary;
}
private TEntity GetFromCache(string key, Func<TEntity> getAction)
{
TEntity entity = null;
if (_cacheOn)
{
Dictionary<string, TEntity> cacheDictionary = this.GetCacheDictionary();
if (cacheDictionary != null)
{
entity = cacheDictionary.GetByKey(key);
if (entity == null)
{
entity = getAction();
cacheDictionary.SetByKey(key, entity);
}
}
}
return entity ?? getAction();
}
public override TEntity Get(XmlDocument db, TKey key)
{
string projectGuid = db.DocumentElement.GetValueAttribute("guid", string.Empty);
string cacheKey = "{0}_{1}".ApplyFormat(projectGuid, key);
return this.GetFromCache(cacheKey, () => base.Get(db, key));
}
public TEntity Get1(XmlNode xmlNode, TKey key)
{
TEntity entity = this.ConvertToEntity(xmlNode);
return entity;
}
}
}

View File

@ -0,0 +1,193 @@
using Kit.Helpers;
using Kit.Helpers.Repository.Converter;
using Kit.Helpers.Repository.Xml.Exceptions;
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public abstract class XmlSimpleClass<TKey, TContentKey> : IPersistentKey<TKey>, IContentXmlKey<TContentKey>
{
public TKey Id { get; set; }
public TContentKey ContentId { get; set; }
public TKey PersistentKey
{
get { return this.Id; }
}
public TContentKey GetContentKey
{
get { return this.ContentId; }
}
public int RowVersion { get; set; }
}
public abstract class XmlSimpleClassDb : XmlSimpleClass<Guid, int> { }
public abstract class EntityXmlPersistentSimpleRepository<TEntity, TKey, TFilter, TContextKey> :
EntityXmlSelectSimpleRepository<TEntity, TFilter, TKey, TContextKey>,
IPersistentXml2Repository<TEntity, TKey, TFilter, TContextKey>
where TEntity : XmlSimpleClass<TKey, TContextKey>
where TFilter : IXmlSimpleFilter<TContextKey, TKey>
{
protected string _elementsAfterPostion;
public EntityXmlPersistentSimpleRepository(string elementName) : base(elementName)
{
}
public EntityXmlPersistentSimpleRepository(string elementName, IConverter<XmlNode, TEntity> converter) : base(elementName, converter)
{
}
public EntityXmlPersistentSimpleRepository(string rootElementName, string path, string elementName) : base(rootElementName, path, elementName)
{
}
public EntityXmlPersistentSimpleRepository(string path, string elementName) : base(path, elementName)
{
}
public EntityXmlPersistentSimpleRepository(string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(path, elementName, converter)
{
}
public EntityXmlPersistentSimpleRepository(string rootElementName, string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(rootElementName, path, elementName, converter)
{
}
public EntityXmlPersistentSimpleRepository(string elementsAfterPostion, string rootElementName, string path, string elementName, IConverter<XmlNode, TEntity> converter) : base(rootElementName, path, elementName, converter)
{
_elementsAfterPostion = elementsAfterPostion;
}
protected virtual XmlNode GetElementAfterInsert(XmlNode root, XmlNode persistent = null)
{
if (!string.IsNullOrEmpty(_elementsAfterPostion))
{
var elements = root.SelectNodes(_elementsAfterPostion);
return elements.Cast<XmlNode>().LastOrDefault();
}
return null;
}
protected abstract IEnumerable<ItemLog> SetPersistentFromEntity(XmlDocument db, XmlNode persistent, TEntity entity);
protected virtual bool OnInserting(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
protected virtual bool OnInserted(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
public TKey Insert(XmlDocument db, TEntity entity)
{
XmlNode parent = GetParentNode(db);
XmlNode persistent = parent.OwnerDocument.CreateNode(XmlNodeType.Element, _elementName, "");
this.SetPersistentFromEntity(parent.OwnerDocument, persistent, entity);
if (this.OnInserting(db, persistent, entity))
{
var after = GetElementAfterInsert(parent, persistent);
if (after == null)
{
if (parent.FirstChild == null)
{
parent.AppendChild(persistent);
}
else
{
parent.InsertBefore(persistent, parent.FirstChild);
}
}
else
{
parent.InsertAfter(persistent, after);
}
this.OnInserted(db, persistent, entity);
}
return entity.PersistentKey;
}
public virtual TKey InsertToNode(XmlNode db, TEntity entity)
{
XmlNode parent = GetParentNode(db);
XmlNode persistent = parent.OwnerDocument.CreateNode(XmlNodeType.Element, _elementName, "");
this.SetPersistentFromEntity(parent.OwnerDocument, persistent, entity);
var after = GetElementAfterInsert(parent, persistent);
if (after == null) parent.InsertBefore(persistent, parent.FirstChild);
else
{
parent.InsertAfter(persistent, after);
}
return entity.PersistentKey;
}
protected virtual bool OnUpdating(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
protected virtual bool OnUpdated(XmlDocument db, XmlNode persistent, TEntity entity) { return true; }
private XmlNodeList SelectNodesByKey(XmlDocument db, TKey key)
{
string xPath = $"{_path}/{_elementName}";
if (xPath.StartsWith("/") == false)
{
xPath = "//" + xPath;
}
XmlNodeList list = db.SelectNodes($"{xPath}[@guid=\"{key}\"]");
if (list.Count == 0)
{
list = db.SelectNodes($"{xPath}[@guid=\"{key.ToString().ToUpper()}\"]");
}
return list;
}
public virtual void Delete(XmlDocument db, TKey key)
{
XmlNode persistent = SelectNodesByKey(db, key).Cast<XmlNode>().AsQueryable().FirstOrDefault();
persistent.ParentNode.RemoveChild(persistent);
}
public virtual TEntity Get(XmlDocument db, TKey key)
{
return Get(db, key, default);
}
public virtual TEntity Get(XmlDocument db, TKey key, TFilter filter)
{
XmlNodeList list = SelectNodesByKey(db, key);
return this.FillResult(db, list.Cast<XmlNode>().AsQueryable().ToList().Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter)), filter).SingleOrDefault();
}
public virtual bool Exists(XmlDocument db, TKey key)
{
XmlNodeList list = SelectNodesByKey(db, key);
return list.Count > 0;
}
protected virtual System.Linq.Expressions.Expression<Func<XmlNode, bool>> GetByKeyExpression(TKey key)
{
return x => x.Attributes["guid"].InnerText == key.ToString();
}
public IEnumerable<ItemLog> Update(XmlDocument db, TEntity entity)
{
XmlNode persistent = SelectNodesByKey(db, entity.PersistentKey).Cast<XmlNode>().AsQueryable().FirstOrDefault();
IEnumerable<ItemLog>? itemLogs = null;
if (this.OnUpdating(db, persistent, entity))
{
//int row = persistent.GetValueAttributeInt32("rowVersion");
int row = entity.RowVersion;
if (row != entity.RowVersion) throw new RowVersionException("Данные были изменены версии записи не совпадают");
itemLogs = this.SetPersistentFromEntity(db, persistent, entity);
persistent.SetAttributeValue("rowVersion", (++row).ToString());
this.OnUpdated(db, persistent, entity);
}
return itemLogs ?? Array.Empty<ItemLog>();
}
}
}

View File

@ -0,0 +1,101 @@
using Microsoft.AspNetCore.Http;
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public abstract class EntityXmlPersistentSimpleRepositoryWithCache<TEntity, TKey, TFilter, TContextKey> : EntityXmlPersistentSimpleRepository<TEntity, TKey, TFilter, TContextKey>
where TEntity : XmlSimpleClass<TKey, TContextKey>
where TFilter : IXmlSimpleFilter<TContextKey, TKey>
{
private string _cacheKey;
protected bool _cacheOn;
private void CacheInit()
{
_cacheKey = this.GetType().FullName;
_cacheOn = true;
}
public EntityXmlPersistentSimpleRepositoryWithCache(string elementName)
: base(elementName)
{
this.CacheInit();
}
public EntityXmlPersistentSimpleRepositoryWithCache(string path, string elementName) : base(path, elementName)
{
this.CacheInit();
}
//public EntityXmlPersistentSimpleRepository2(string elementName, TContext data, IConverter<XmlNode, TEntity> converter)
// : base(elementName, data, converter)
//{
//}
//public EntityXmlPersistentSimpleRepository2(string rootElementName, string path, string elementName, TContext data);
//public EntityXmlPersistentSimpleRepository2(string path, string elementName, TContext data, IConverter<XmlNode, TEntity> converter);
//public EntityXmlPersistentSimpleRepository2(string rootElementName, string path, string elementName, TContext data, IConverter<XmlNode, TEntity> converter);
//public EntityXmlPersistentSimpleRepository2(string elementsAfterPostion, string rootElementName, string path, string elementName, TContext data, IConverter<XmlNode, TEntity> converter);
private Dictionary<string, TEntity>? GetCacheDictionary(ISelectContext selectContext)
{
HttpContext? httpContext = HttpContextCore.Current;
if (httpContext == null && selectContext == null)
{
return null;
}
IDictionary<object, object?>? items = selectContext != null ? selectContext.Items : httpContext!.Items;
lock (items)
{
Dictionary<string, TEntity> cacheDictionary = (Dictionary<string, TEntity>)items[_cacheKey];
if (cacheDictionary == null)
{
cacheDictionary = new Dictionary<string, TEntity>();
items[_cacheKey] = cacheDictionary;
}
return cacheDictionary;
}
}
private TEntity GetFromCache(string key, Func<TEntity> getAction, ISelectContext selectContext = null)
{
TEntity entity = null;
if (_cacheOn)
{
Dictionary<string, TEntity>? cacheDictionary = this.GetCacheDictionary(selectContext);
if (cacheDictionary != null)
{
lock (cacheDictionary)
{
entity = cacheDictionary.GetByKey(key);
if (entity == null)
{
entity = getAction();
cacheDictionary.SetByKey(key, entity);
}
}
}
}
return entity ?? getAction();
}
public override TEntity Get(XmlDocument db, TKey key)
{
string projectGuid = db.DocumentElement.GetValueAttribute("guid", string.Empty);
string cacheKey = "{0}_{1}".ApplyFormat(projectGuid, key);
return this.GetFromCache(cacheKey, () => base.Get(db, key));
}
public override TEntity Get(XmlDocument db, TKey key, TFilter filter)
{
string projectGuid = db.DocumentElement.GetValueAttribute("guid", string.Empty);
string cacheKey = "{0}_{1}".ApplyFormat(projectGuid, key);
return null;// this.GetFromCache(cacheKey, () => base.Get(db, key, filter), (ISelectContext)filter.GetPropertyValue("SelectContext"));
}
}
}

View File

@ -0,0 +1,141 @@
namespace Kit.Helpers.Repository.Xml
{
public enum UpdateStatuses { Ok = 1, DocumentRowVersionNotChecked = 2, RowVersionNotChecked = 3, Cancel = 4 }
public abstract class EntityXmlRepository<TKey>
{
protected string _elementName;
protected string _path = "{0}";
protected string _rootElementName;
public EntityXmlRepository(string elementName)
{
_elementName = elementName;
}
public EntityXmlRepository(string path, string elementName)
{
_elementName = elementName;
_path = path;
}
public EntityXmlRepository(string rootElementName, string path, string elementName)
{
_elementName = elementName;
_rootElementName = rootElementName;
_path = path;
}
}
public interface IXmlFilter<TContentKey, TKey>
{
TKey GetRootKey();
TContentKey GetXmlContentKey();
}
public class XmlFilter : IXmlFilter<int, Guid>
{
public int RootXml { get; set; }
public Guid RootKey { get; set; }
public Guid? Id { get; set; }
public int GetXmlContentKey()
{
return this.RootXml;
}
public Guid GetRootKey()
{
return RootKey;
}
}
public class XmlFilterInt : IXmlFilter<int, int>
{
public int RootXml { get; set; }
public int RootKey { get; set; }
public int? Id { get; set; }
public int GetXmlContentKey()
{
return this.RootXml;
}
public int GetRootKey()
{
return RootKey;
}
}
public interface IRootXmlKey<TKey>
{
TKey RootId { get; }
}
public interface IContentXmlKey<TKey>
{
TKey GetContentKey { get; }
}
public abstract class XmlSimpleClass<TKey> : IPersistentKey<TKey>, IPersistentXmlKey<TKey>
{
public TKey Id { get; set; }
public TKey ParentId { get; set; }
public string ElementNameParent { get; set; }
public string ElementName { get; set; }
public int RowVersion { get; set; }
public TKey PersistentKey
{
get { return this.Id; }
}
public TKey PersistentXmlRootKey
{
get { return this.ParentId; }
}
}
public abstract class XmlSimpleClass : XmlSimpleClass<Guid> { }
public abstract class XmlFilterSimpleDb : IXmlSimpleFilter<int, Guid>
{
public int RootXml { get; set; }
public Guid? Id { get; set; }
public ISelectContext SelectContext { get; set; }
public virtual int GetXmlContentKey()
{
return this.RootXml;
}
public Guid GetKey()
{
return Id ?? Guid.Empty;
}
}
public abstract class XmlFilterSimple<TKey, TContentKey> : IXmlSimpleFilter<TContentKey, TKey>
where TKey: struct
{
public TContentKey RootXml { get; set; }
public TKey? Id { get; set; }
public ISelectContext SelectContext { get; set; }
public virtual TContentKey GetXmlContentKey()
{
return this.RootXml;
}
public TKey GetKey()
{
return Id ?? default;
}
}
public abstract class XmlFilterComplex<TKey, TRootKey, TContentKey> : XmlFilterSimple<TKey, TContentKey>, IXmlComplexFilter<TContentKey, TRootKey, TKey>
where TKey: struct
where TRootKey : struct
{
public TRootKey RootKey { get; set; }
public virtual TRootKey RootKeyId()
{
return RootKey;
}
}
}

View File

@ -0,0 +1,103 @@
using Kit.Helpers.Repository.Converter;
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public interface IXmlComplexFilter<TContentKey, TRootKey, TKey> : IXmlSimpleFilter<TContentKey, TKey>
{
TRootKey RootKeyId();
}
public abstract class XmlComplexClass<TKey, TRootKey, TContentKey> : XmlSimpleClass<TKey, TContentKey>, IRootXmlKey<TRootKey>
{
public TRootKey RootId { get; set; }
}
public abstract class EntityXmlSelectComplexRepository<TEntity, TFilter, TKey, TRootKey, TContextKey> :
EntityXmlSelectSimpleRepository<TEntity, TFilter, TKey, TContextKey>
where TFilter : IXmlComplexFilter<TContextKey, TRootKey, TKey>
where TEntity : XmlComplexClass<TKey, TRootKey, TContextKey>
{
public EntityXmlSelectComplexRepository(string parentElementName, string path, string element, IConverter<XmlNode, TEntity> converter) : base(parentElementName, path, element)
{
_converter = converter;
}
public EntityXmlSelectComplexRepository(string path, string element, IConverter<XmlNode, TEntity> converter) : base(path, element)
{
_converter = converter;
}
public EntityXmlSelectComplexRepository(string element, IConverter<XmlNode, TEntity> converter) : base(element)
{
_converter = converter;
}
public EntityXmlSelectComplexRepository(string element) : base(element)
{
}
public EntityXmlSelectComplexRepository(string path, string element) : base(path, element)
{
}
public EntityXmlSelectComplexRepository(string rootElementName, string path, string element) : base(rootElementName, path, element)
{
}
protected virtual XmlNodeList SelectNodesByKey(XmlDocument db, TKey key)
{
string xPath = $"{_elementName}";
if (string.IsNullOrWhiteSpace(_path) == false)
{
xPath = $"{_path}/{xPath}";
}
if (xPath.StartsWith("/") == false)
{
xPath = "//" + xPath;
}
XmlNodeList list = db.SelectNodes($"{xPath}[@guid=\"{key}\"]");
if (list.Count == 0)
{
list = db.SelectNodes($"{xPath}[@guid=\"{key.ToString().ToUpper()}\"]");
}
return list;
}
public virtual XmlNode GetRootNode(XmlDocument document, TRootKey key)
{
XmlNode result = document.SelectSingleNode("//*[@guid=\"{0}\"]".ApplyFormat(key.ToString()));
if (result == null)
result = document.SelectSingleNode("//*[@guid=\"{0}\"]".ApplyFormat(key.ToString().ToUpper()));
if (!string.IsNullOrEmpty(_rootElementName))
{
var node = result.SelectSingleNode(_rootElementName);
if (node == null)
{
node = result.OwnerDocument.CreateElement(_rootElementName);
result.AppendChild(node);
}
result = node;
}
return result;
}
public override IQueryable<XmlNode> CreateQuery(XmlDocument db, TFilter filter)
{
XmlNode root = GetRootNode(db, filter.RootKeyId());
if (root == null)
root = db.CreateElement("Empty");
XmlNode parent = GetParentNode(root);
if (parent == null)
parent = db.CreateElement("Empty");
return parent.SelectNodes(this._elementName).Cast<XmlNode>().AsQueryable();
}
}
}

View File

@ -0,0 +1,160 @@
using System.Xml;
using Kit.Helpers;
using Kit.Helpers.Repository.Converter;
namespace Kit.Helpers.Repository.Xml
{
public interface IXmlSimpleFilter<TContentKey, TKey>
{
TKey GetKey();
TContentKey GetXmlContentKey();
}
public abstract class EntityXmlSelectSimpleRepository<TEntity, TFilter, TKey, TContextKey> :
EntityXmlRepository<TContextKey>,
ISelectPageXmlRepository<TEntity, TFilter, TContextKey>
where TFilter : IXmlSimpleFilter<TContextKey, TKey>
where TEntity : IPersistentKey<TKey>
{
public EntityXmlSelectSimpleRepository(string parentElementName, string path, string element, IConverter<XmlNode, TEntity> converter)
: base(parentElementName, path, element)
{
_converter = converter;
}
public EntityXmlSelectSimpleRepository(string path, string element, IConverter<XmlNode, TEntity> converter) : base(path, element)
{
_converter = converter;
}
public EntityXmlSelectSimpleRepository(string element, IConverter<XmlNode, TEntity> converter) : base(element)
{
_converter = converter;
}
public EntityXmlSelectSimpleRepository(string element) : base(element)
{
}
public EntityXmlSelectSimpleRepository(string path, string element) : base(path, element)
{
}
public EntityXmlSelectSimpleRepository(string rootElementName, string path, string element) : base(rootElementName, path, element)
{
}
protected IConverter<XmlNode, TEntity> _converter = null;
protected virtual TEntity ConvertToEntity(XmlNode persistent)
{
return _converter.Convert(persistent);
}
protected virtual TEntity ConvertToEntity(XmlNode persistent, TFilter filter)
{
return this.ConvertToEntity(persistent);
}
public virtual XmlNode GetParentNode(XmlNode root)
{
XmlNode result = root.SelectSingleNode(_path);
if (!string.IsNullOrEmpty(_rootElementName))
{
var node = result.SelectSingleNode(_rootElementName);
if (node == null)
{
node = result.OwnerDocument.CreateElement(_rootElementName);
result.AppendChild(node);
}
result = node;
}
return result;
}
public virtual IQueryable<XmlNode> CreateQuery(XmlDocument db, TFilter filter)
{
XmlNode parent = GetParentNode(db);
if (parent == null) parent = db.CreateElement("Empty");
return parent.SelectNodes(this._elementName).Cast<XmlNode>().AsQueryable();
}
public virtual IQueryable<XmlNode> CreateQuery(XmlNode node, TFilter filter)
{
XmlNode parent = GetParentNode(node);
if (parent == null) parent = node.OwnerDocument.CreateElement("Empty");
return parent.SelectNodes(this._elementName).Cast<XmlNode>().AsQueryable();
}
protected virtual IQueryable<XmlNode> ApplyFilter(IQueryable<XmlNode> query, TFilter filter)
{
return query;
}
protected virtual TEntity FillXmlClass(TEntity entity, XmlNode persistent, TFilter filter)
{
return entity;
}
protected IQueryable<XmlNode> SelectNodesByFilter(XmlDocument db, TFilter filter)
{
IQueryable<XmlNode> query = this.CreateQuery(db, filter);
query = this.ApplyFilter(query, filter);
return query;
}
public virtual IEnumerable<TEntity> Select(XmlDocument db, TFilter filter)
{
IQueryable<XmlNode> query = this.SelectNodesByFilter(db, filter);
query = this.ApplyFilter(query, filter);
var tt = query.ToList()
.Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
.ToList();
return this.FillResult(db, tt, filter);
}
public virtual IEnumerable<TEntity> Select(XmlNode node, TFilter filter)
{
IQueryable<XmlNode> query = this.CreateQuery(node, filter);
query = this.ApplyFilter(query, filter);
var tt = query.ToList()
.Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
.ToList();
return this.FillResult(node.OwnerDocument ?? (XmlDocument)node, tt, filter);
}
public virtual IEnumerableWithPage<TEntity> SelectPage(XmlDocument db, TFilter filter, int pageNo, int pageSize)
{
IQueryable<XmlNode> query = this.CreateQuery(db, filter);
query = this.ApplyFilter(query, filter);
var result = new ListWithPage<TEntity>();
result.TotalRows = query.Count();
result.PageNo = pageNo;
result.PageSize = pageSize;
var tt = query.Skip((pageNo - 1) * pageSize).Take(pageSize)
.Select(x => this.FillXmlClass(this.ConvertToEntity(x, filter), x, filter))
.ToList();
result.AddRange(FillResult(db, tt, filter));
return result;
}
public virtual IEnumerable<TEntity> FillResult(XmlDocument db, IEnumerable<TEntity> entities, TFilter filter) { return entities; }
public int Count(XmlDocument db, TFilter filter)
{
IQueryable<XmlNode> query = this.SelectNodesByFilter(db, filter);
{
return query.Count();
}
}
}
}

View File

@ -0,0 +1,28 @@
using Kit.Helpers.Log;
using System;
namespace Kit.Helpers.Repository.Xml.Exceptions
{
public class RowVersionException : Exception
{
public IOperationLogger OperationLogger = Log.OperationLogger.Empty;
public RowVersionException()
{
}
public RowVersionException(string message)
: base(message)
{
}
public RowVersionException(string message, Exception inner)
: base(message, inner)
{
}
public RowVersionException(string message, IOperationLogger operationLogger)
: base(message)
{
OperationLogger = operationLogger;
}
}
}

View File

@ -0,0 +1,13 @@
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public interface IContentXml<TContentKey>
{
XmlDocument CreateDefault();
XmlDocument? GetContentData(TContentKey key);
bool CreateContentData(TContentKey key, XmlDocument document, bool skipIfExists = true);
void UpdateContentData(TContentKey key, XmlDocument document);
int GetRowVersion(XmlDocument document);
}
}

View File

@ -0,0 +1,32 @@
namespace Kit.Helpers.Repository.Xml
{
public interface IPersistentKey<TKey>
{
TKey PersistentKey { get; }
}
public interface IInt32PersistentKey : IPersistentKey<int> { }
public interface ISelectRepository<TEntity, TFilter>
{
IEnumerable<TEntity> Select(TFilter filter);
int Count(TFilter filter);
IEnumerable<TEntity> Select(TFilter filter, int page, int sizepage);
}
public class NothingFilter { }
public interface IPersistentRepository<TEntity, TKey, TFilter> : ISelectRepository<TEntity, TFilter>
{
TKey Insert(TEntity entity);
void Update(TKey key, TEntity entity);
void Delete(TKey key);
TEntity Get(TKey key);
// IEnumerable<TEntity> Get(IEnumerable<TKey> keys);
}
public interface IPersistentRepository<TEntity, TFilter> : IPersistentRepository<TEntity, int, TFilter>
{
}
}

View File

@ -0,0 +1,55 @@
using System.Xml;
namespace Kit.Helpers.Repository.Xml
{
public interface IPersistentXmlKey<TKey>
{
string ElementName { get; set; }
TKey PersistentXmlRootKey { get; }
}
public interface ISelectXmlRepository<TEntity, TFilter>
{
IEnumerable<TEntity> Select(XmlDocument db, TFilter filter);
/// <summary> Получить количество xml узлов, подходящих под фильтр. Облегчённый аналог <see cref="Select(XmlDocument, TFilter)"/>.Count() </summary>
int Count(XmlDocument db, TFilter filter);
IEnumerable<TEntity> Select(XmlDocument db, TFilter filter, int page, int sizepage);
}
public interface ISelectPageXmlRepository<TEntity, TFilter, TContextKey>
{
IEnumerable<TEntity> Select(XmlDocument db, TFilter filter);
IEnumerable<TEntity> Select(XmlNode node, TFilter filter);
/// <summary> Получить количество xml узлов, подходящих под фильтр. Облегчённый аналог <see cref="Select(XmlDocument, TFilter)"/>.Count() </summary>
int Count(XmlDocument db, TFilter filter);
IEnumerableWithPage<TEntity> SelectPage(XmlDocument db, TFilter filter, int pageNo, int pageSize);
}
public interface IPersistentXml2Repository<TEntity, TKey, TFilter, TContextKey> : ISelectPageXmlRepository<TEntity, TFilter, TContextKey>
{
TEntity Get(XmlDocument db, TKey key);
TEntity Get(XmlDocument db, TKey key, TFilter filter);
bool Exists(XmlDocument db, TKey key);
IEnumerable<ItemLog> Update(XmlDocument db, TEntity entity);
TKey InsertToNode(XmlNode db, TEntity entity);
TKey Insert(XmlDocument db, TEntity entity);
void Delete(XmlDocument db, TKey key);
// IEnumerable<TEntity> Get(IEnumerable<TKey> keys);
}
public interface IPersistentXmlRepository<TEntity, TKey, TFilter, TContextKey> : ISelectXmlRepository<TEntity, TFilter>
{
TEntity Get(XmlDocument db, TKey key);
TKey Insert(XmlNode db, TEntity entity);
// IEnumerable<TEntity> Get(IEnumerable<TKey> keys);
}
public interface IPersistentXmlRepository<TEntity, TKey, TFilter> : IPersistentXmlRepository<TEntity, TKey, TFilter, int>
{
}
public interface IPersistentXmlRepository<TEntity, TFilter> : IPersistentXmlRepository<TEntity, int, TFilter, int>
{
}
}

View File

@ -0,0 +1,29 @@
using System.Xml;
namespace Kit.Helpers.Repository.Xml;
public interface IRowVersionRepository
{
int GetRowVersion(XmlNode xmlNode);
void SetRowVersion(XmlNode xmlNode, int newVersionId);
int IncreaseRowVersion(XmlNode xmlNode);
}
public class RowVersionRepository : IRowVersionRepository
{
public RowVersionRepository() { }
private readonly string _attrNameRowVersion = "RowVersion";
public int GetRowVersion(XmlNode xmlNode) => xmlNode.GetValueAttributeInt32(_attrNameRowVersion);
public void SetRowVersion(XmlNode xmlNode, int newVersionId) => xmlNode.SetAttributeValue(_attrNameRowVersion, newVersionId.ToString());
public int IncreaseRowVersion(XmlNode xmlNode)
{
int newVersionId = GetRowVersion(xmlNode) + 1;
SetRowVersion(xmlNode, newVersionId);
return newVersionId;
}
}

View File

@ -0,0 +1,49 @@
using System.Xml;
namespace Kit.Helpers.Repository.Xml;
public class XmlFileVersionInfo
{
public XmlFileVersionInfo()
{
Type = string.Empty;
Version = 0;
}
public string Type { get; set; }
public long Version { get; set; }
}
public interface IXmlFileVersionRepository
{
XmlFileVersionInfo? Get(XmlDocument document);
void Set(XmlDocument document, XmlFileVersionInfo info);
}
public class XmlFileVersionRepository : IXmlFileVersionRepository
{
public XmlFileVersionRepository() { }
public XmlFileVersionInfo? Get(XmlDocument document)
{
XmlNode? fileVersionNode = document.DocumentElement?.SelectSingleNode("FileVersion");
if (fileVersionNode == null) return null;
return new XmlFileVersionInfo
{
Type = fileVersionNode.GetValueAttribute("type"),
Version = fileVersionNode.GetValueAttributeInt64("version"),
};
}
public void Set(XmlDocument document, XmlFileVersionInfo info)
{
Check.IsNotNull(info, "info is not set");
Check.IsNotNull(document.DocumentElement, "DocumentElement is not set");
XmlNode fileVersionNode = document.DocumentElement.SetElementValue("FileVersion", string.Empty);
fileVersionNode.SetAttributeValue("type", info.Type);
fileVersionNode.SetAttributeValue("version", info.Version.ToString());
}
}

View File

@ -0,0 +1,392 @@
namespace Kit.Helpers.Routes
{
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public class ControllerBaseRouteFluent<TController> : IEndPointFluent<ControllerBaseRouteFluent<TController>>
where TController : ControllerBase
{
private readonly IRouteBuilder _routeTable;
private readonly List<RouteFluent> _routes;
public ControllerBaseRouteFluent(IRouteBuilder routeTable)
{
_routeTable = routeTable;
_routes = new List<RouteFluent>();
}
#region Generic Result Actions
private void AddRouteLambda(string url, LambdaExpression actionSelector)
{
// получаем имя контроллера
string controllerName = typeof(TController).Name;
if (controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && controllerName.Length > "Controller".Length)
{
controllerName = controllerName.Remove(controllerName.Length - "Controller".Length, "Controller".Length);
}
// получаем имя действия
var unaryExpression = (UnaryExpression)actionSelector.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var constantExpression = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)constantExpression.Value;
string actionName = methodInfo.Name;
_routes.Add( _routeTable.AddRoute($"{controllerName}_{actionName}_{Guid.NewGuid()}",url, controllerName, actionName, methodInfo));
}
public delegate TResult GenericResult0Delegate<TResult>();
public delegate TResult GenericResult1Delegate<TResult, TInput1>(TInput1 input1);
public delegate TResult GenericResult2Delegate<TResult, TInput1, TInput2>(TInput1 input1, TInput2 input2);
public delegate TResult GenericResult3Delegate<TResult, TInput1, TInput2, TInput3>(TInput1 input1, TInput2 input2, TInput3 input3);
public delegate TResult GenericResult4Delegate<TResult, TInput1, TInput2, TInput3, TInput4>(TInput1 input1, TInput2 input2, TInput3 input3, TInput4 input4);
public delegate TResult GenericResult5Delegate<TResult, TInput1, TInput2, TInput3, TInput4, TInput5>(TInput1 input1, TInput2 input2, TInput3 input3, TInput4 input4, TInput5 input5);
public ControllerBaseRouteFluent<TController> GenericAction<TResult>(string url, Expression<Func<TController, GenericResult0Delegate<TResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> GenericAction<TResult, TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<TResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> GenericAction<TResult, TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<TResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> GenericAction<TResult, TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<TResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> GenericAction<TResult, TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<TResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> GenericAction<TResult, TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<TResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region JsonResult Actions
public ControllerBaseRouteFluent<TController> JsonAction(string url, Expression<Func<TController, GenericResult0Delegate<JsonResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> JsonAction<TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<JsonResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> JsonAction<TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<JsonResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> JsonAction<TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<JsonResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> JsonAction<TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<JsonResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> JsonAction<TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<JsonResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region EmptyResult Actions
public ControllerBaseRouteFluent<TController> EmptyAction(string url, Expression<Func<TController, GenericResult0Delegate<EmptyResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> EmptyAction<TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<EmptyResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> EmptyAction<TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<EmptyResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> EmptyAction<TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<EmptyResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> EmptyAction<TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<EmptyResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> EmptyAction<TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<EmptyResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region ContentResult Actions
public ControllerBaseRouteFluent<TController> ContentAction(string url, Expression<Func<TController, GenericResult0Delegate<ContentResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ContentAction<TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<ContentResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ContentAction<TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<ContentResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ContentAction<TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<ContentResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ContentAction<TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<ContentResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ContentAction<TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<ContentResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region PartialViewResult Actions
public ControllerBaseRouteFluent<TController> PartialViewAction(string url, Expression<Func<TController, GenericResult0Delegate<PartialViewResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> PartialViewAction<TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<PartialViewResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> PartialViewAction<TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<PartialViewResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> PartialViewAction<TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<PartialViewResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> PartialViewAction<TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<PartialViewResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> PartialViewAction<TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<PartialViewResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region ActionResult Actions
public ControllerBaseRouteFluent<TController> ActionAction(string url, Expression<Func<TController, GenericResult0Delegate<IActionResult>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ActionAction<TInput1>(string url, Expression<Func<TController, GenericResult1Delegate<IActionResult, TInput1>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ActionAction<TInput1, TInput2>(string url, Expression<Func<TController, GenericResult2Delegate<IActionResult, TInput1, TInput2>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ActionAction<TInput1, TInput2, TInput3>(string url, Expression<Func<TController, GenericResult3Delegate<IActionResult, TInput1, TInput2, TInput3>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ActionAction<TInput1, TInput2, TInput3, TInput4>(string url, Expression<Func<TController, GenericResult4Delegate<IActionResult, TInput1, TInput2, TInput3, TInput4>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
public ControllerBaseRouteFluent<TController> ActionAction<TInput1, TInput2, TInput3, TInput4, TInput5>(string url, Expression<Func<TController, GenericResult5Delegate<IActionResult, TInput1, TInput2, TInput3, TInput4, TInput5>>> actionSelector)
{
this.AddRouteLambda(url, actionSelector);
return this;
}
#endregion
#region IRouteFluent<ControllerRouteFluent<TController>>
public ControllerBaseRouteFluent<TController> NeedHttpGet(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpGet(required);
return this;
}
public ControllerBaseRouteFluent<TController> NeedHttpPost(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpPost(required);
return this;
}
public ControllerBaseRouteFluent<TController> WithDefaults(object defaults)
{
if (_routes.Any()) _routes.Last().WithDefaults(defaults);
return this;
}
public ControllerBaseRouteFluent<TController> WithConstraints(object constraints)
{
if (_routes.Any()) _routes.Last().WithConstraints(constraints);
return this;
}
public ControllerBaseRouteFluent<TController> WithNamespaces(string[] namespaces)
{
if (_routes.Any()) _routes.Last().WithNamespaces(namespaces);
return this;
}
public ControllerBaseRouteFluent<TController> NeedSecurityKey(string securityKeyName)
{
if (_routes.Any()) _routes.Last().NeedSecurityKey(securityKeyName);
return this;
}
public ControllerBaseRouteFluent<TController> WithDataTokens(object dataTokens)
{
if (_routes.Any()) _routes.Last().WithDataTokens(dataTokens);
return this;
}
public ControllerBaseRouteFluent<TController> WithDelegate(Action<RouteFluent> routeFluentAction)
{
if (_routes.Any()) routeFluentAction(_routes.Last());
return this;
}
#endregion
#region IRouteFluent<ControllerRouteFluent<TController>> for ALL
public ControllerBaseRouteFluent<TController> NeedHttpGet_All(bool required = true)
{
_routes.ForEach(x => x.NeedHttpGet(required));
return this;
}
public ControllerBaseRouteFluent<TController> NeedHttpPost_All(bool required = true)
{
_routes.ForEach(x => x.NeedHttpPost(required));
return this;
}
public ControllerBaseRouteFluent<TController> WithDefaults_All(object defaults)
{
_routes.ForEach(x => x.WithDefaults(defaults));
return this;
}
public ControllerBaseRouteFluent<TController> WithConstraints_All(object constraints)
{
_routes.ForEach(x => x.WithConstraints(constraints));
return this;
}
public ControllerBaseRouteFluent<TController> WithNamespaces_All(string[] namespaces)
{
_routes.ForEach(x => x.WithNamespaces(namespaces));
return this;
}
public ControllerBaseRouteFluent<TController> NeedSecurityKey_All(string securityKeyName)
{
_routes.ForEach(x => x.NeedSecurityKey(securityKeyName));
return this;
}
public ControllerBaseRouteFluent<TController> WithDataTokens_All(object dataTokens)
{
_routes.ForEach(x => x.WithDataTokens(dataTokens));
return this;
}
public ControllerBaseRouteFluent<TController> WithDelegate_All(Action<RouteFluent> routeFluentAction)
{
_routes.ForEach(x => routeFluentAction(x));
return this;
}
#endregion
}
}

View File

@ -0,0 +1,174 @@
namespace Kit.Helpers.Routes
{
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public partial class ControllerRouteFluent<TController> : IEndPointFluent<ControllerRouteFluent<TController>>
where TController : Controller
{
private readonly IRouteBuilder _routeTable;
private readonly List<RouteFluent> _routes;
public ControllerRouteFluent(IRouteBuilder routeTable)
{
_routeTable = routeTable;
_routes = new List<RouteFluent>();
}
#region Generic Result Actions
private ControllerRouteFluent<TController> AddRouteLambda(string url, LambdaExpression actionSelector)
{
// получаем имя контроллера
string controllerName = typeof(TController).Name;
if (controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) && controllerName.Length > "Controller".Length)
{
controllerName = controllerName.Remove(controllerName.Length - "Controller".Length, "Controller".Length);
}
// получаем имя действия
var unaryExpression = (UnaryExpression)actionSelector.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var constantExpression = (ConstantExpression)methodCallExpression.Object;
var methodInfo = (MethodInfo)constantExpression.Value;
string actionName = methodInfo.Name;
_routes.Add(_routeTable.AddRoute($"{controllerName}_{actionName}_{Guid.NewGuid()}", url, controllerName, actionName, methodInfo));
return this;
}
#endregion
#region IRouteFluent<ControllerRouteFluent<TController>>
public ControllerRouteFluent<TController> NeedHttpGet(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpGet(required);
return this;
}
public ControllerRouteFluent<TController> NeedHttpPost(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpPost(required);
return this;
}
public ControllerRouteFluent<TController> NeedHttpPut(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpPut(required);
return this;
}
public ControllerRouteFluent<TController> NeedHttpDelete(bool required = true)
{
if (_routes.Any()) _routes.Last().NeedHttpDelete(required);
return this;
}
public ControllerRouteFluent<TController> WithBody(bool required = true)
{
if (_routes.Any()) _routes.Last().WithBody(required);
return this;
}
public ControllerRouteFluent<TController> WithDefaults(object defaults)
{
if (_routes.Any()) _routes.Last().WithDefaults(defaults);
return this;
}
public ControllerRouteFluent<TController> WithConstraints(object constraints)
{
if (_routes.Any()) _routes.Last().WithConstraints(constraints);
return this;
}
public ControllerRouteFluent<TController> WithNamespaces(string[] namespaces)
{
if (_routes.Any()) _routes.Last().WithNamespaces(namespaces);
return this;
}
public ControllerRouteFluent<TController> NeedSecurityKey(string securityKeyName)
{
if (_routes.Any()) _routes.Last().NeedSecurityKey(securityKeyName);
return this;
}
public ControllerRouteFluent<TController> WithDataTokens(object dataTokens)
{
if (_routes.Any()) _routes.Last().WithDataTokens(dataTokens);
return this;
}
public ControllerRouteFluent<TController> WithDelegate(Action<RouteFluent> routeFluentAction)
{
if (_routes.Any()) routeFluentAction(_routes.Last());
return this;
}
public ControllerRouteFluent<TController> ForbidForReadonly(bool value = true)
{
if (_routes.Any()) _routes.Last().ForbidForReadonly(value);
return this;
}
#endregion
#region IRouteFluent<ControllerRouteFluent<TController>> for ALL
public ControllerRouteFluent<TController> NeedHttpGet_All(bool required = true)
{
_routes.ForEach(x => x.NeedHttpGet(required));
return this;
}
public ControllerRouteFluent<TController> NeedHttpPost_All(bool required = true)
{
_routes.ForEach(x => x.NeedHttpPost(required));
return this;
}
public ControllerRouteFluent<TController> WithDefaults_All(object defaults)
{
_routes.ForEach(x => x.WithDefaults(defaults));
return this;
}
public ControllerRouteFluent<TController> WithConstraints_All(object constraints)
{
_routes.ForEach(x => x.WithConstraints(constraints));
return this;
}
public ControllerRouteFluent<TController> WithNamespaces_All(string[] namespaces)
{
_routes.ForEach(x => x.WithNamespaces(namespaces));
return this;
}
public ControllerRouteFluent<TController> NeedSecurityKey_All(string securityKeyName)
{
_routes.ForEach(x => x.NeedSecurityKey(securityKeyName));
return this;
}
public ControllerRouteFluent<TController> WithDataTokens_All(object dataTokens)
{
_routes.ForEach(x => x.WithDataTokens(dataTokens));
return this;
}
public ControllerRouteFluent<TController> WithDelegate_All(Action<RouteFluent> routeFluentAction)
{
_routes.ForEach(x => routeFluentAction(x));
return this;
}
#endregion
}
}

Some files were not shown because too many files have changed in this diff Show More