mirror of
https://github.com/hmaxnl/DotBased.git
synced 2025-01-18 18:14:20 +01:00
Compare commits
5 Commits
1011e34d69
...
552085b478
Author | SHA1 | Date | |
---|---|---|---|
|
552085b478 | ||
|
74d01fc648 | ||
|
d684b395db | ||
|
327ebde153 | ||
|
0eb939c755 |
|
@ -6,19 +6,12 @@ using Serilog;
|
||||||
using ILogger = Serilog.ILogger;
|
using ILogger = Serilog.ILogger;
|
||||||
|
|
||||||
var serilogLogger = SetupSerilog();
|
var serilogLogger = SetupSerilog();
|
||||||
|
|
||||||
LogService.AddLogAdapter(new SerilogAdapter(serilogLogger));
|
LogService.AddLogAdapter(new SerilogAdapter(serilogLogger));
|
||||||
|
|
||||||
var logger = LogService.RegisterLogger(typeof(Program));
|
var logger = LogService.RegisterLogger(typeof(Program));
|
||||||
|
|
||||||
logger.Trace("Test TRACE log! {StringValue} {AnotherValue}", "WOW", "W0W");
|
logger.Information("Whoah... Hi!");
|
||||||
logger.Debug("Test DEBUG log! {IntVal}", 69);
|
|
||||||
logger.Information("Test INFO log! {DoubVal}", 4.20);
|
|
||||||
logger.Warning("Test WARNING log! {StrVal} {IntVAl} {StrChar}", "Over", 9000, '!');
|
|
||||||
logger.Error(new NullReferenceException("Test exception"),"Test ERROR log!");
|
|
||||||
logger.Fatal(new NullReferenceException("Test exception"),"Test FATAL log!");
|
|
||||||
|
|
||||||
Console.ReadKey();
|
Console.ReadKey(); // Hold console app open.
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class SerilogAdapter : LogAdapterBase
|
||||||
}
|
}
|
||||||
switch (capsule.Severity)
|
switch (capsule.Severity)
|
||||||
{
|
{
|
||||||
|
case LogSeverity.Verbose:
|
||||||
case LogSeverity.Trace:
|
case LogSeverity.Trace:
|
||||||
default:
|
default:
|
||||||
logger.Write(new LogEvent(capsule.TimeStamp, LogEventLevel.Verbose, null, template, properties ?? ArraySegment<LogEventProperty>.Empty, ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom()));
|
logger.Write(new LogEvent(capsule.TimeStamp, LogEventLevel.Verbose, null, template, properties ?? ArraySegment<LogEventProperty>.Empty, ActivityTraceId.CreateRandom(), ActivitySpanId.CreateRandom()));
|
||||||
|
|
97
DotBased/Collections/InstanceContainer.cs
Normal file
97
DotBased/Collections/InstanceContainer.cs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
using DotBased.Logging;
|
||||||
|
|
||||||
|
namespace DotBased.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Container to store instances
|
||||||
|
/// <remarks>WIP!</remarks>
|
||||||
|
/// </summary>
|
||||||
|
public class InstanceContainer : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ILogger _log = LogService.RegisterLogger(typeof(InstanceContainer));
|
||||||
|
private readonly Dictionary<string, InstanceNode> _tCollection = new Dictionary<string, InstanceNode>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register a instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The instace will be created by the <see cref="Get{TInstance}"/> function</remarks>
|
||||||
|
/// <param name="key">Key to indentify the instance</param>
|
||||||
|
/// <typeparam name="TInstance">The instance type</typeparam>
|
||||||
|
public void Register<TInstance>(string key) => _tCollection.Add(key, new InstanceNode(null, typeof(TInstance)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Add an already constructed instance to the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key to identify instance</param>
|
||||||
|
/// <param name="instance">Constructed instance</param>
|
||||||
|
public void Add(string key, object instance) => _tCollection.Add(key, new InstanceNode(instance, instance.GetType()));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove a instance from the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">Key to get the instance</param>
|
||||||
|
/// <param name="dispose">Dispose the instance if it inherits the 'IDisposable' interface</param>
|
||||||
|
public void Remove(string key, bool dispose = true)
|
||||||
|
{
|
||||||
|
if (!_tCollection.TryGetValue(key, out var iNode))
|
||||||
|
return;
|
||||||
|
switch (iNode.Instance)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
break;
|
||||||
|
case IDisposable instance when dispose:
|
||||||
|
_log.Debug("Disposing disposable object... (InstanceContainer)");
|
||||||
|
instance.Dispose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_tCollection.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the instance that is stored in the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>If the instance is not yet constructed this wil activate the instance</remarks>
|
||||||
|
/// <param name="key">Key to identify the instance</param>
|
||||||
|
/// <typeparam name="TInstance">Instasnce type</typeparam>
|
||||||
|
/// <returns>The instance that is at the given key</returns>
|
||||||
|
public TInstance? Get<TInstance>(string key)
|
||||||
|
{
|
||||||
|
if (!_tCollection.TryGetValue(key, out InstanceNode node) || node.InstanceType != typeof(TInstance))
|
||||||
|
return default;
|
||||||
|
if (node.Instance != null)
|
||||||
|
return (TInstance)node.Instance;
|
||||||
|
node.Instance = Activator.CreateInstance(node.InstanceType);
|
||||||
|
// Override the old node with the new data, else the next 'Get' will reactivate a another instance.
|
||||||
|
_tCollection[key] = node;
|
||||||
|
if (node.Instance != null) return (TInstance)node.Instance;
|
||||||
|
_log.Warning("Instance is null!");
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (var kvp in _tCollection)
|
||||||
|
{
|
||||||
|
switch (kvp.Value.Instance)
|
||||||
|
{
|
||||||
|
case null:
|
||||||
|
continue;
|
||||||
|
case IDisposable disposable:
|
||||||
|
_log.Verbose("Disposing: {Key}", kvp.Key);
|
||||||
|
disposable.Dispose();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal struct InstanceNode
|
||||||
|
{
|
||||||
|
public InstanceNode(object? instance, Type instanceType)
|
||||||
|
{
|
||||||
|
Instance = instance;
|
||||||
|
InstanceType = instanceType;
|
||||||
|
}
|
||||||
|
public object? Instance;
|
||||||
|
public readonly Type InstanceType;
|
||||||
|
}
|
45
DotBased/Collections/KeyContainerBase.cs
Normal file
45
DotBased/Collections/KeyContainerBase.cs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace DotBased.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base class for creating containers that has tree like keys
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="separator">The separator used for the keys</param>
|
||||||
|
/// <typeparam name="TContainer">Container data value</typeparam>
|
||||||
|
public abstract class KeyContainerBase<TContainer>(char separator = '.')
|
||||||
|
where TContainer : KeyContainerBase<TContainer>, new()
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, TContainer> _containers = new Dictionary<string, TContainer>();
|
||||||
|
public ReadOnlyDictionary<string, TContainer> Containers => new ReadOnlyDictionary<string, TContainer>(_containers);
|
||||||
|
|
||||||
|
public TContainer this[string key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (key.Contains(separator))
|
||||||
|
return AddFromQueue(new KeyQueue(key, separator));
|
||||||
|
if (!_containers.ContainsKey(key))
|
||||||
|
AddContainer(key, new TContainer());
|
||||||
|
return _containers[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void AddContainer(string key, TContainer container) => _containers[key] = container;
|
||||||
|
public bool RemoveContainer(string key) => _containers.Remove(key);
|
||||||
|
public TContainer GetContainer(string key) => _containers[key];
|
||||||
|
|
||||||
|
TContainer AddFromQueue(KeyQueue queue)
|
||||||
|
{
|
||||||
|
if (queue.IsEmpty) return (TContainer)this;
|
||||||
|
string queueKey = queue.Next();
|
||||||
|
AddContainer(queueKey, new TContainer());
|
||||||
|
return _containers[queueKey].AddFromQueue(queue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal class KeyQueue(string key, char divider)
|
||||||
|
{
|
||||||
|
public string Next() => _key.Dequeue();
|
||||||
|
public bool IsEmpty => _key.Count <= 0;
|
||||||
|
|
||||||
|
private readonly Queue<string> _key = new Queue<string>(key.Split(divider, StringSplitOptions.RemoveEmptyEntries));
|
||||||
|
}
|
74
DotBased/Collections/PropertyContainer.cs
Normal file
74
DotBased/Collections/PropertyContainer.cs
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
|
namespace DotBased.Collections;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Property container to store string, long, int, double and bool properties
|
||||||
|
/// </summary>
|
||||||
|
public class PropertyContainer : KeyContainerBase<PropertyContainer>
|
||||||
|
{
|
||||||
|
public PropertyContainer()
|
||||||
|
{ }
|
||||||
|
public PropertyContainer(char separator) : base(separator)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
private readonly Dictionary<string, object> _data = new Dictionary<string, object>();
|
||||||
|
public ReadOnlyDictionary<string, object> Data => new ReadOnlyDictionary<string, object>(_data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set a property with the corresponding key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key that will correspond to the property</param>
|
||||||
|
/// <param name="sValue">The string property value</param>
|
||||||
|
/// <param name="overridable">If the key already exists overwrite the property</param>
|
||||||
|
public void Set(string key, string sValue, bool overridable = true)
|
||||||
|
{
|
||||||
|
if (ContainsKey(key) && !overridable)
|
||||||
|
return;
|
||||||
|
_data[key] = sValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(string key, long lValue, bool overridable = true)
|
||||||
|
{
|
||||||
|
if (ContainsKey(key) && !overridable)
|
||||||
|
return;
|
||||||
|
_data[key] = lValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(string key, double dValue, bool overridable = true)
|
||||||
|
{
|
||||||
|
if (ContainsKey(key) && !overridable)
|
||||||
|
return;
|
||||||
|
_data[key] = dValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Set(string key, bool bValue, bool overridable = true)
|
||||||
|
{
|
||||||
|
if (ContainsKey(key) && !overridable)
|
||||||
|
return;
|
||||||
|
_data[key] = bValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if the key exists in this container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key to check</param>
|
||||||
|
/// <returns>True if the key exists otherwise false</returns>
|
||||||
|
public bool ContainsKey(string key) => _data.ContainsKey(key);
|
||||||
|
|
||||||
|
public string GetString(string key) => Convert.ToString(_data[key]) ?? string.Empty;
|
||||||
|
public long GetLong(string key) => Convert.ToInt64(_data[key]);
|
||||||
|
public double GetDouble(string key) => Convert.ToDouble(_data[key]);
|
||||||
|
public bool GetBool(string key) => Convert.ToBoolean(_data[key]);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the property at the passed key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key">The key where to remove the property</param>
|
||||||
|
/// <returns>True is the property is removed otherwise false</returns>
|
||||||
|
public bool Remove(string key) => _data.Remove(key);
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all the properties in this container.
|
||||||
|
/// </summary>
|
||||||
|
public void ClearData() => _data.Clear();
|
||||||
|
}
|
|
@ -7,8 +7,4 @@
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>default</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Collections\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -5,6 +5,8 @@ namespace DotBased.Logging;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ILogger
|
public interface ILogger
|
||||||
{
|
{
|
||||||
|
public void Verbose(string message, params object?[]? parameters);
|
||||||
|
|
||||||
public void Trace(string message, params object?[]? parameters);
|
public void Trace(string message, params object?[]? parameters);
|
||||||
|
|
||||||
public void Debug(string message, params object?[]? parameters);
|
public void Debug(string message, params object?[]? parameters);
|
||||||
|
|
|
@ -2,10 +2,11 @@ namespace DotBased.Logging;
|
||||||
|
|
||||||
public enum LogSeverity
|
public enum LogSeverity
|
||||||
{
|
{
|
||||||
Trace = 0,
|
Verbose = 0,
|
||||||
Debug = 1,
|
Trace = 1,
|
||||||
Info = 2,
|
Debug = 2,
|
||||||
Warning = 3,
|
Info = 3,
|
||||||
Error = 4,
|
Warning = 4,
|
||||||
Fatal = 5
|
Error = 5,
|
||||||
|
Fatal = 6
|
||||||
}
|
}
|
|
@ -10,6 +10,18 @@ public class Logger(CallerInformation caller, ref Action<LogCapsule> logProcesso
|
||||||
ProcessLog(capsule);
|
ProcessLog(capsule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Verbose(string message, params object?[]? parameters)
|
||||||
|
{
|
||||||
|
Log(new LogCapsule()
|
||||||
|
{
|
||||||
|
Logger = this,
|
||||||
|
Message = message,
|
||||||
|
Parameters = parameters,
|
||||||
|
Severity = LogSeverity.Verbose,
|
||||||
|
TimeStamp = DateTime.Now
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public override void Trace(string message, params object?[]? parameters)
|
public override void Trace(string message, params object?[]? parameters)
|
||||||
{
|
{
|
||||||
Log(new LogCapsule()
|
Log(new LogCapsule()
|
||||||
|
|
|
@ -11,6 +11,7 @@ public abstract class LoggerBase(CallerInformation caller, ref Action<LogCapsule
|
||||||
|
|
||||||
internal readonly Action<LogCapsule> ProcessLog = logProcessorHandler;
|
internal readonly Action<LogCapsule> ProcessLog = logProcessorHandler;
|
||||||
|
|
||||||
|
public abstract void Verbose(string message, params object?[]? parameters);
|
||||||
public abstract void Trace(string message, params object?[]? parameters);
|
public abstract void Trace(string message, params object?[]? parameters);
|
||||||
public abstract void Debug(string message, params object?[]? parameters);
|
public abstract void Debug(string message, params object?[]? parameters);
|
||||||
public abstract void Information(string message, params object?[]? parameters);
|
public abstract void Information(string message, params object?[]? parameters);
|
||||||
|
|
129
DotBased/Utilities/Cryptography.cs
Normal file
129
DotBased/Utilities/Cryptography.cs
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
|
||||||
|
namespace DotBased.Utilities;
|
||||||
|
|
||||||
|
public static class Cryptography
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* https://gist.github.com/therightstuff/aa65356e95f8d0aae888e9f61aa29414
|
||||||
|
*/
|
||||||
|
public static Result<string> ExportPublicKeyToPem(RSACryptoServiceProvider csp)
|
||||||
|
{
|
||||||
|
var outputStream = new StringWriter();
|
||||||
|
var parameters = csp.ExportParameters(false);
|
||||||
|
if (parameters.Exponent == null || parameters.Modulus == null)
|
||||||
|
return Result<string>.Failed("RSAParameters are empty!");
|
||||||
|
using (var stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var writer = new BinaryWriter(stream);
|
||||||
|
writer.Write((byte)0x30); // SEQUENCE
|
||||||
|
using (var innerStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var innerWriter = new BinaryWriter(innerStream);
|
||||||
|
innerWriter.Write((byte)0x30); // SEQUENCE
|
||||||
|
EncodeLength(innerWriter, 13);
|
||||||
|
innerWriter.Write((byte)0x06); // OBJECT IDENTIFIER
|
||||||
|
byte[] rsaEncryptionOid = new byte[] { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01 };
|
||||||
|
EncodeLength(innerWriter, rsaEncryptionOid.Length);
|
||||||
|
innerWriter.Write(rsaEncryptionOid);
|
||||||
|
innerWriter.Write((byte)0x05); // NULL
|
||||||
|
EncodeLength(innerWriter, 0);
|
||||||
|
innerWriter.Write((byte)0x03); // BIT STRING
|
||||||
|
using (var bitStringStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var bitStringWriter = new BinaryWriter(bitStringStream);
|
||||||
|
bitStringWriter.Write((byte)0x00); // # of unused bits
|
||||||
|
bitStringWriter.Write((byte)0x30); // SEQUENCE
|
||||||
|
using (var paramsStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var paramsWriter = new BinaryWriter(paramsStream);
|
||||||
|
EncodeIntegerBigEndian(paramsWriter, parameters.Modulus); // Modulus
|
||||||
|
EncodeIntegerBigEndian(paramsWriter, parameters.Exponent); // Exponent
|
||||||
|
var paramsLength = (int)paramsStream.Length;
|
||||||
|
EncodeLength(bitStringWriter, paramsLength);
|
||||||
|
bitStringWriter.Write(paramsStream.GetBuffer(), 0, paramsLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bitStringLength = (int)bitStringStream.Length;
|
||||||
|
EncodeLength(innerWriter, bitStringLength);
|
||||||
|
innerWriter.Write(bitStringStream.GetBuffer(), 0, bitStringLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
int length = (int)innerStream.Length;
|
||||||
|
EncodeLength(writer, length);
|
||||||
|
writer.Write(innerStream.GetBuffer(), 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
char[] base64 = Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length).ToCharArray();
|
||||||
|
// WriteLine terminates with \r\n, we want only \n
|
||||||
|
outputStream.Write("-----BEGIN PUBLIC KEY-----\n");
|
||||||
|
for (int i = 0; i < base64.Length; i += 64)
|
||||||
|
{
|
||||||
|
outputStream.Write(base64, i, Math.Min(64, base64.Length - i));
|
||||||
|
outputStream.Write("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.Write("-----END PUBLIC KEY-----");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result<string>.Ok(outputStream.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EncodeLength(BinaryWriter stream, int length)
|
||||||
|
{
|
||||||
|
switch (length)
|
||||||
|
{
|
||||||
|
case < 0:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(length), "Length must be non-negative");
|
||||||
|
case < 0x80:
|
||||||
|
// Short form
|
||||||
|
stream.Write((byte)length);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// Long form
|
||||||
|
int temp = length;
|
||||||
|
int bytesRequired = 0;
|
||||||
|
while (temp > 0)
|
||||||
|
{
|
||||||
|
temp >>= 8;
|
||||||
|
bytesRequired++;
|
||||||
|
}
|
||||||
|
stream.Write((byte)(bytesRequired | 0x80));
|
||||||
|
for (int i = bytesRequired - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
stream.Write((byte)(length >> (8 * i) & 0xff));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EncodeIntegerBigEndian(BinaryWriter stream, byte[] value, bool forceUnsigned = true)
|
||||||
|
{
|
||||||
|
stream.Write((byte)0x02); // INTEGER
|
||||||
|
int prefixZeros = value.TakeWhile(t => t == 0).Count();
|
||||||
|
if (value.Length - prefixZeros == 0)
|
||||||
|
{
|
||||||
|
EncodeLength(stream, 1);
|
||||||
|
stream.Write((byte)0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (forceUnsigned && value[prefixZeros] > 0x7f)
|
||||||
|
{
|
||||||
|
// Add a prefix zero to force unsigned if the MSB is 1
|
||||||
|
EncodeLength(stream, value.Length - prefixZeros + 1);
|
||||||
|
stream.Write((byte)0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EncodeLength(stream, value.Length - prefixZeros);
|
||||||
|
}
|
||||||
|
for (int i = prefixZeros; i < value.Length; i++)
|
||||||
|
{
|
||||||
|
stream.Write(value[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
DotBased/Utilities/Culture.cs
Normal file
99
DotBased/Utilities/Culture.cs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace DotBased.Utilities;
|
||||||
|
|
||||||
|
public static class Culture
|
||||||
|
{
|
||||||
|
private static List<CultureInfo> _sysCultures = new List<CultureInfo>();
|
||||||
|
private static Dictionary<string, RegionInfo> _regions = new Dictionary<string, RegionInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get all system known cultures.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Will be cached after first call, to clear the internal list call <see cref="ClearCached"/> function</remarks>
|
||||||
|
/// <returns>The list with <see cref="CultureInfo"/>'s the system knows</returns>
|
||||||
|
public static IEnumerable<CultureInfo> GetSystemCultures()
|
||||||
|
{
|
||||||
|
if (_sysCultures.Count == 0)
|
||||||
|
_sysCultures = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList();
|
||||||
|
return _sysCultures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the regions the system knows.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>The list will internally be cached, clear the cache with the <see cref="ClearCached"/> function</remarks>
|
||||||
|
/// <returns>A list with regions from the system</returns>
|
||||||
|
public static Dictionary<string, RegionInfo> GetRegions()
|
||||||
|
{
|
||||||
|
if (_regions.Count == 0)
|
||||||
|
{
|
||||||
|
var cultureInfos = GetSystemCultures().Where(cul => !cul.IsNeutralCulture).Where(cul => cul.LCID != 0x7F);
|
||||||
|
foreach (var culture in cultureInfos)
|
||||||
|
{
|
||||||
|
var region = new RegionInfo(culture.Name);
|
||||||
|
_regions.Add(culture.Name, region);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _regions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the specified cache.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearCached(CacheType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case CacheType.Culture:
|
||||||
|
_sysCultures.Clear();
|
||||||
|
break;
|
||||||
|
case CacheType.Region:
|
||||||
|
_regions.Clear();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(type), type, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public enum CacheType
|
||||||
|
{
|
||||||
|
Culture,
|
||||||
|
Region
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Currency
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Formats the currency amount to the given culture currency.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">The amount to format</param>
|
||||||
|
/// <param name="culture">The culture to be formatted in</param>
|
||||||
|
/// <returns>Formatted amount in the given culture format</returns>
|
||||||
|
public static string FormatAmountToCultureCurrency(double amount, CultureInfo culture) => string.Format(culture, "{0:C}", amount);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats the amount to the culture which is found by the ISO currency symbol.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>WIP & slow!</remarks>
|
||||||
|
/// <param name="amount">The amount to fomrat</param>
|
||||||
|
/// <param name="isoCurencySymbol">Three-character ISO 4217 currency symbol</param>
|
||||||
|
/// <returns>Formatted amount in the given ISO currency symbol</returns>
|
||||||
|
public static string FormatAmountFromIsoCurrency(double amount, string isoCurencySymbol)
|
||||||
|
{
|
||||||
|
var culture = CultureInfo.CurrentCulture;
|
||||||
|
var systemRegion = new RegionInfo(culture.Name);
|
||||||
|
if (systemRegion.ISOCurrencySymbol != isoCurencySymbol)
|
||||||
|
{
|
||||||
|
string? result = GetRegions().Where(x => x.Value.ISOCurrencySymbol == isoCurencySymbol).Select(x => x.Key).FirstOrDefault();
|
||||||
|
culture = GetSystemCultures().FirstOrDefault(x => x.Name == result);
|
||||||
|
}
|
||||||
|
return culture == null ? string.Empty : FormatAmountToCultureCurrency(amount, culture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a list of ISO 3 letter currency symbols.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List with ISOCurrencySymbols</returns>
|
||||||
|
public static IEnumerable<string> GetIsoCurrencySymbols() => GetRegions().Select(x => x.Value.ISOCurrencySymbol).Distinct().ToList();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user