Добавление базовых библиотек
This commit is contained in:
parent
37e709e30d
commit
3c4f7b0622
|
|
@ -1,7 +0,0 @@
|
|||
namespace Kit.Core.Helpers
|
||||
{
|
||||
public class Class1
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -1 +0,0 @@
|
|||
1de42344d47904be68c52d310f760c9a33b8396e0e8e253be53c0fe180725da2
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "oGlU1raL4Zc=",
|
||||
"success": true,
|
||||
"projectFilePath": "C:\\KIT\\Kit.Core\\Kit.Core.Helpers\\Kit.Core.Helpers.csproj",
|
||||
"expectedPackageFiles": [],
|
||||
"logs": []
|
||||
}
|
||||
59
Kit.Core.sln
59
Kit.Core.sln
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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 { }
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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) { }
|
||||
}
|
||||
|
|
@ -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) { }
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
namespace Kit.Helpers
|
||||
{
|
||||
public static partial class SystemBooleanExtensionMethods
|
||||
{
|
||||
public static string ToJsString(this bool value)
|
||||
{
|
||||
return value ? "true" : "false";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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("ь", "я");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
|
|
@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
//});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
|
|
@ -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;
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
|
@ -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>> { }
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using System.Linq;
|
||||
|
||||
namespace Kit.Helpers.Repository.Paging
|
||||
{
|
||||
public interface IPageFilter
|
||||
{
|
||||
IQueryable<TEntity> ChangePage<TEntity>(IQueryable<TEntity> query);
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
|
@ -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);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Reference in New Issue