Merge branch 'develop' into master
This commit is contained in:
@@ -0,0 +1,980 @@
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
using SteamWare.Logger;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace SteamWare.IO
|
||||
{
|
||||
public class Redis
|
||||
{
|
||||
#region base objects
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto currentDb locale
|
||||
/// </summary>
|
||||
private IDatabase _currDB { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto DB REDIS corrente
|
||||
/// </summary>
|
||||
public IDatabase cache //currDB
|
||||
{
|
||||
get
|
||||
{
|
||||
IDatabase answ;
|
||||
// se già valorizzato uso oggetto private...
|
||||
if (_currDB != null)
|
||||
{
|
||||
answ = _currDB;
|
||||
}
|
||||
else
|
||||
{
|
||||
// init DB (sullo 0)
|
||||
answ = connRedis.GetDatabase();
|
||||
// gestione override...
|
||||
if (memLayer.ML.confReadInt("redisDb") >= 0)
|
||||
{
|
||||
// in questo caso uso il DB configurato in app.config...
|
||||
answ = connRedis.GetDatabase(memLayer.ML.confReadInt("redisDb"));
|
||||
}
|
||||
_currDB = answ;
|
||||
}
|
||||
// restituisco oggetto DB
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connessione lazy a redis...
|
||||
/// </summary>
|
||||
private Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
|
||||
{
|
||||
string RedisConn = memLayer.ML.confReadString("RedisConn");
|
||||
if (string.IsNullOrEmpty(RedisConn))
|
||||
{
|
||||
RedisConn = "localhost,abortConnect=false,ssl=false";
|
||||
}
|
||||
|
||||
return ConnectionMultiplexer.Connect(RedisConn);
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Connessione lazy a redis...
|
||||
/// </summary>
|
||||
private Lazy<ConnectionMultiplexer> lazyConnectionAdmin = new Lazy<ConnectionMultiplexer>(() =>
|
||||
{
|
||||
string RedisConnAdmin = memLayer.ML.confReadString("RedisConnAdmin");
|
||||
if (string.IsNullOrEmpty(RedisConnAdmin))
|
||||
{
|
||||
RedisConnAdmin = "localhost,abortConnect=false,ssl=false,allowAdmin=true";
|
||||
}
|
||||
|
||||
return ConnectionMultiplexer.Connect(RedisConnAdmin);
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto statico connessione redis
|
||||
/// </summary>
|
||||
public ConnectionMultiplexer connRedis
|
||||
{
|
||||
get
|
||||
{
|
||||
return lazyConnection.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto statico connessione redis
|
||||
/// </summary>
|
||||
public ConnectionMultiplexer connRedisAdmin
|
||||
{
|
||||
get
|
||||
{
|
||||
return lazyConnectionAdmin.Value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion base objects
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Nome della variabile HASH da utilizzare (dato CodModulo / Server / DB impiegato da funzionalita' DbConfig) + keyName richiesto...
|
||||
/// </summary>
|
||||
public string redHash(string keyName)
|
||||
{
|
||||
string answ = keyName;
|
||||
try
|
||||
{
|
||||
answ = string.Format("{0}:{1}:{2}:{3}", memLayer.ML.confReadString("CodModulo"), memLayer.ML.taConfig.Connection.DataSource, memLayer.ML.taConfig.Connection.Database, keyName).Replace("\\", "_");
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Serializzazione di un oggetto generico
|
||||
/// </summary>
|
||||
/// <param name="origVal"></param>
|
||||
/// <returns></returns>
|
||||
public string serializeVal(object origVal)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = JsonConvert.SerializeObject(origVal);
|
||||
}
|
||||
catch { }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deserializzazione di un valore string in oggetto generico
|
||||
/// </summary>
|
||||
/// <param name="serVal"></param>
|
||||
/// <returns></returns>
|
||||
public object deserializeVal(string serVal)
|
||||
{
|
||||
object answ = "";
|
||||
try
|
||||
{
|
||||
answ = JsonConvert.DeserializeObject(serVal);
|
||||
}
|
||||
catch { }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce un pò di info sul server redis connesso
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string getRedisInfoData()
|
||||
{
|
||||
string answ = "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try
|
||||
{
|
||||
sb.AppendLine($"Configuration: {connRedis.Configuration}");
|
||||
sb.AppendLine($"Connected: {connRedis.IsConnected}");
|
||||
sb.AppendLine($"ClientName: {connRedis.ClientName}");
|
||||
sb.AppendLine($"Total Ops: {connRedis.OperationCount}");
|
||||
sb.AppendLine($"Status: {connRedis.GetStatus()}");
|
||||
answ = sb.ToString();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce info dei server connessi...
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public IServer[] redServInfo()
|
||||
{
|
||||
IServer[] answ = new IServer[1];
|
||||
try
|
||||
{
|
||||
answ = new IServer[connRedisAdmin.GetEndPoints().Length];
|
||||
int i = 0;
|
||||
foreach (var ep in connRedisAdmin.GetEndPoints())
|
||||
{
|
||||
var server = connRedisAdmin.GetServer(ep);
|
||||
answ[i] = server;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error($"redServInfo:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
#endregion Helpers
|
||||
|
||||
#region gestione valori in RedisCache
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce una chiave salvata in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <returns></returns>
|
||||
public string getRSV(string chiave)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = cache.StringGet(chiave);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
//logger.lg.scriviLog(string.Format("Errore in getRSV:{0}{1}", Environment.NewLine, exc), tipoLog.EXCEPTION);
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salva una chiave in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <param name="valore"></param>
|
||||
/// <returns></returns>
|
||||
public bool setRSV(string chiave, string valore)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
cache.StringSet(chiave, valore);
|
||||
answ = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezzione in setRSV:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salva una chiave in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <param name="valore"></param>
|
||||
/// <param name="TTL_sec">in secondi</param>
|
||||
/// <returns></returns>
|
||||
public bool setRSV(string chiave, string valore, int TTL_sec)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
TimeSpan expT = new TimeSpan(0, 0, TTL_sec);
|
||||
// salvo con expyry...
|
||||
cache.StringSet(chiave, valore, expT);
|
||||
answ = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezzione in setRSV:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Incrementa un contatore in Redis
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <returns></returns>
|
||||
public long setRCntI(string chiave)
|
||||
{
|
||||
long answ = 0;
|
||||
try
|
||||
{
|
||||
answ = cache.StringIncrement(chiave, 1);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezzione in setRCI:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrementa un contatore in Redis
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <returns></returns>
|
||||
public long setRCntD(string chiave)
|
||||
{
|
||||
long answ = 0;
|
||||
try
|
||||
{
|
||||
answ = cache.StringDecrement(chiave, 1);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezzione in setRCD:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce una chiave COUNTER in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <returns></returns>
|
||||
public int getRCnt(string chiave)
|
||||
{
|
||||
int answInt = 0;
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = cache.StringGet(chiave);
|
||||
answInt = Convert.ToInt32(answ);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in getRSV:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answInt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resetta (elimina) un contatore in Redis
|
||||
/// </summary>
|
||||
/// <param name="chiave"></param>
|
||||
/// <returns></returns>
|
||||
public bool resetRCnt(string chiave)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
answ = cache.KeyDelete(chiave);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in resetRCnt:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce un set KVP (Key Value Pair) salvati in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="chiavi"></param>
|
||||
/// <returns></returns>
|
||||
public RedisValue[] getRKeys(RedisKey[] chiavi)
|
||||
{
|
||||
RedisValue[] answ = null;
|
||||
try
|
||||
{
|
||||
answ = cache.StringGet(chiavi);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in getRKeys:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salva un set KVP (Key Value Pair) in RedisCache
|
||||
/// </summary>
|
||||
/// <param name="valori">Set KVP chiave-valore da salvare</param>
|
||||
/// <returns></returns>
|
||||
public bool setRKeys(KeyValuePair<RedisKey, RedisValue>[] valori)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
cache.StringSet(valori);
|
||||
answ = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in setRKeys:{0}{1}", Environment.NewLine, exc));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se ci siano valori nella KEY indicata...
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool redKeyPresent(RedisKey key)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
answ = cache.KeyExists(key);
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in redKeyPresent per la key {2}:{0}{1}", Environment.NewLine, exc, key));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se ci siano valori nella KEY indicata (string)
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool redKeyPresentSz(string key)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
RedisKey chiave = key;
|
||||
answ = redKeyPresent(chiave);
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se ci siano valori nella hash indicata...
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool redHashPresent(RedisKey key)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
answ = cache.HashGetAll(key).Length > 0;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error(string.Format("Eccezione in redHashPresent per la key {2}{0}{1}", Environment.NewLine, exc, key));
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifica se ci siano valori nella hash indicata (string)
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool redHashPresentSz(string key)
|
||||
{
|
||||
bool answ = false;
|
||||
try
|
||||
{
|
||||
RedisKey chiave = key;
|
||||
answ = redHashPresent(chiave);
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera tutti i valori dalla hash
|
||||
/// </summary>
|
||||
/// <param name="hashKey"></param>
|
||||
/// <returns></returns>
|
||||
public KeyValuePair<string, string>[] redGetHash(string hashKey)
|
||||
{
|
||||
KeyValuePair<string, string>[] answ = new KeyValuePair<string, string>[1];
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
HashEntry[] valori = cache.HashGetAll(chiave);
|
||||
answ = new KeyValuePair<string, string>[valori.Length];
|
||||
int i = 0;
|
||||
foreach (HashEntry item in valori)
|
||||
{
|
||||
answ[i] = new KeyValuePair<string, string>(item.Name, item.Value);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera tutti i valori dalla hash in formato Dictionary
|
||||
/// </summary>
|
||||
/// <param name="hashKey"></param>
|
||||
/// <returns></returns>
|
||||
public Dictionary<string, string> redGetHashDict(string hashKey)
|
||||
{
|
||||
Dictionary<string, string> answ = new Dictionary<string, string>();
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
HashEntry[] valori = cache.HashGetAll(chiave);
|
||||
foreach (HashEntry item in valori)
|
||||
{
|
||||
answ.Add(item.Name, item.Value);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera UN SINGOLO VALORE dalla hash per un dato field
|
||||
/// </summary>
|
||||
/// <param name="hashKey"></param>
|
||||
/// <param name="hashField"></param>
|
||||
/// <returns></returns>
|
||||
public string redGetHashField(string hashKey, string hashField)
|
||||
{
|
||||
string answ = "";
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
RedisValue campo = hashField;
|
||||
RedisValue valOut = cache.HashGet(chiave, campo);
|
||||
answ = valOut.ToString();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salvataggio di una hash di valori
|
||||
/// </summary>
|
||||
/// <param name="hashKey">chiave</param>
|
||||
/// <param name="hashFields">valori</param>
|
||||
/// <returns></returns>
|
||||
public bool redSaveHash(string hashKey, KeyValuePair<string, string>[] hashFields)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
HashEntry[] valori = new HashEntry[hashFields.Length];
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<string, string> kvp in hashFields)
|
||||
{
|
||||
valori[i] = new HashEntry(kvp.Key, kvp.Value);
|
||||
i++;
|
||||
}
|
||||
cache.HashSet(chiave, valori);
|
||||
answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salvataggio di una hash di valori in formato Dictionary
|
||||
/// </summary>
|
||||
/// <param name="hashKey">chiave</param>
|
||||
/// <param name="hashFields">valori</param>
|
||||
/// <returns></returns>
|
||||
public bool redSaveHashDict(string hashKey, Dictionary<string, string> hashFields)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
HashEntry[] valori = new HashEntry[hashFields.Count];
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<string, string> kvp in hashFields)
|
||||
{
|
||||
valori[i] = new HashEntry(kvp.Key, kvp.Value);
|
||||
i++;
|
||||
}
|
||||
cache.HashSet(chiave, valori);
|
||||
answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salvataggio di una hash di valori
|
||||
/// </summary>
|
||||
/// <param name="hashKey">chiave</param>
|
||||
/// <param name="hashListKVP">valori come lista KVP</param>
|
||||
/// <returns></returns>
|
||||
public bool redSaveHashList(string hashKey, List<KeyValuePair<string, string>> hashListKVP)
|
||||
{
|
||||
bool answ = false;
|
||||
if (connRedis.IsConnected)
|
||||
{
|
||||
// cerco se ci sia valore in redis...
|
||||
IDatabase cache = connRedis.GetDatabase();
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
HashEntry[] valori = new HashEntry[hashListKVP.Count];
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<string, string> kvp in hashListKVP)
|
||||
{
|
||||
valori[i] = new HashEntry(kvp.Key, kvp.Value);
|
||||
i++;
|
||||
}
|
||||
cache.HashSet(chiave, valori);
|
||||
answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salvataggio di una hash di valori
|
||||
/// </summary>
|
||||
/// <param name="hashKey">chiave</param>
|
||||
/// <param name="hashFields">valori</param>
|
||||
/// <param name="expireSeconds">scadenza preimpostata hash (secondi) | defaoult = -1 (non scade)</param>
|
||||
/// <returns></returns>
|
||||
public bool redSaveHash(string hashKey, KeyValuePair<string, string>[] hashFields, double expireSeconds = -1)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
answ = redSaveHash(hashKey, hashFields);
|
||||
if (expireSeconds > 0)
|
||||
{
|
||||
cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds));
|
||||
}
|
||||
//answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Salvataggio di una hash di valori in formato Dictionary
|
||||
/// </summary>
|
||||
/// <param name="hashKey">chiave</param>
|
||||
/// <param name="hashFields">valori</param>
|
||||
/// <param name="expireSeconds">scadenza preimpostata hash (secondi) | defaoult = -1 (non scade)</param>
|
||||
/// <returns></returns>
|
||||
public bool redSaveHashDict(string hashKey, Dictionary<string, string> hashFields, double expireSeconds = -1)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = hashKey;
|
||||
answ = redSaveHashDict(hashKey, hashFields);
|
||||
if (expireSeconds > 0)
|
||||
{
|
||||
cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds));
|
||||
}
|
||||
//answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elimina una key (hash, string)
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public bool redDelKey(string key)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
try
|
||||
{
|
||||
RedisKey chiave = key;
|
||||
cache.KeyDelete(chiave);
|
||||
answ = true;
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flush completo cache redis
|
||||
/// </summary>
|
||||
/// <param name="keyPattern">** = tutti</param>
|
||||
/// <returns></returns>
|
||||
public bool redFlushKey(string keyPattern)
|
||||
{
|
||||
bool answ = false;
|
||||
// cerco se ci sia valore in redis...
|
||||
// se vuoto = ALL...
|
||||
keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern;
|
||||
try
|
||||
{
|
||||
foreach (var ep in connRedis.GetEndPoints())
|
||||
{
|
||||
var server = connRedis.GetServer(ep);
|
||||
var keys = server.Keys(database: memLayer.ML.confReadInt("redisDb"), pattern: $"{keyPattern}*");
|
||||
foreach (var key in keys)
|
||||
{
|
||||
cache.KeyDelete(key);
|
||||
}
|
||||
}
|
||||
answ = true;
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error($"Eccezione in redFlushKey{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Conta num oggetti cache redis che rispondono a pattern
|
||||
/// </summary>
|
||||
/// <param name="keyPattern">** = tutti</param>
|
||||
/// <returns></returns>
|
||||
public int redCountKey(string keyPattern)
|
||||
{
|
||||
int answ = 0;
|
||||
// cerco se ci sia valore in redis...
|
||||
// se vuoto = ALL...
|
||||
keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern;
|
||||
try
|
||||
{
|
||||
foreach (var ep in connRedis.GetEndPoints())
|
||||
{
|
||||
var server = connRedis.GetServer(ep);
|
||||
foreach (var key in server.Keys(pattern: keyPattern))
|
||||
{
|
||||
answ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error($"Eccezione in redCountKey:{Environment.NewLine}{exc}");
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce numero record in Redis DB
|
||||
/// </summary>
|
||||
public long numRecRedis
|
||||
{
|
||||
get
|
||||
{
|
||||
long answ = 0;
|
||||
try
|
||||
{
|
||||
foreach (var ep in connRedis.GetEndPoints())
|
||||
{
|
||||
var server = connRedis.GetServer(ep);
|
||||
answ += server.DatabaseSize();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
return answ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce oggetti cache redis che rispondono a pattern
|
||||
/// </summary>
|
||||
/// <param name="keyPattern">** = tutti</param>
|
||||
/// <param name="orderBy">Tipo di ordinamento per kvp</param>
|
||||
/// <returns></returns>
|
||||
public List<KeyValuePair<string, int>> redGetCounterByKey(string keyPattern, kvpOrderBy orderBy)
|
||||
{
|
||||
int numAnsw = redCountKey(keyPattern);
|
||||
RedisKey[] chiavi = new RedisKey[numAnsw];
|
||||
List<KeyValuePair<string, int>> answ = new List<KeyValuePair<string, int>>();
|
||||
// se vuoto = ALL...
|
||||
keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern;
|
||||
|
||||
// recupero in primis elenco chiavi
|
||||
try
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var ep in connRedis.GetEndPoints())
|
||||
{
|
||||
var server = connRedis.GetServer(ep);
|
||||
foreach (var key in server.Keys(pattern: keyPattern))
|
||||
{
|
||||
chiavi[i] = key;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
{
|
||||
Logging.Instance.Error($"Eccezione in redGetCounterByKey{Environment.NewLine}{exc}");
|
||||
}
|
||||
// ora recupero valori!
|
||||
var valori = getRKeys(chiavi);
|
||||
int currVal = 0;
|
||||
// popolo rispsota
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < numAnsw; i++)
|
||||
{
|
||||
Int32.TryParse(valori[i], out currVal);
|
||||
answ.Add(new KeyValuePair<string, int>(chiavi[i], currVal));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
// se richiesto riordino...
|
||||
switch (orderBy)
|
||||
{
|
||||
case kvpOrderBy.KeyAsc:
|
||||
answ.Sort(CompareKey);
|
||||
break;
|
||||
|
||||
case kvpOrderBy.KeyDesc:
|
||||
answ.Sort(CompareKeyDesc);
|
||||
break;
|
||||
|
||||
case kvpOrderBy.ValAsc:
|
||||
answ.Sort(CompareVal);
|
||||
break;
|
||||
|
||||
case kvpOrderBy.ValDesc:
|
||||
answ.Sort(CompareValDesc);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return answ;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua comaprazione x CHIAVE in KVP ASC
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
private int CompareKey(KeyValuePair<string, int> x, KeyValuePair<string, int> y)
|
||||
{
|
||||
return x.Key.CompareTo(y.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua comaprazione x VALORE in KVP ASC
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareVal(KeyValuePair<string, int> x, KeyValuePair<string, int> y)
|
||||
{
|
||||
return x.Value.CompareTo(y.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua comaprazione x CHIAVE in KVP DESC
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
private int CompareKeyDesc(KeyValuePair<string, int> x, KeyValuePair<string, int> y)
|
||||
{
|
||||
return y.Key.CompareTo(x.Key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua comaprazione x VALORE in KVP DESC
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns></returns>
|
||||
public int CompareValDesc(KeyValuePair<string, int> x, KeyValuePair<string, int> y)
|
||||
{
|
||||
return y.Value.CompareTo(x.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tipologia di ordinamento x liste KVP
|
||||
/// </summary>
|
||||
public enum kvpOrderBy
|
||||
{
|
||||
/// <summary>
|
||||
/// Ordinamento ASCending per KEY
|
||||
/// </summary>
|
||||
KeyAsc,
|
||||
|
||||
/// <summary>
|
||||
/// Ordinamento DESCending per KEY
|
||||
/// </summary>
|
||||
KeyDesc,
|
||||
|
||||
/// <summary>
|
||||
/// Ordinamento ASCending per VAL
|
||||
/// </summary>
|
||||
ValAsc,
|
||||
|
||||
/// <summary>
|
||||
/// Ordinamento DESCending per VAL
|
||||
/// </summary>
|
||||
ValDesc
|
||||
}
|
||||
|
||||
#endregion gestione valori in RedisCache
|
||||
|
||||
#region gestione Stack / List
|
||||
|
||||
/// <summary>
|
||||
/// Lunghezza Stack
|
||||
/// </summary>
|
||||
/// <param name="stackName"></param>
|
||||
/// <returns></returns>
|
||||
public long StackLen(string stackName)
|
||||
{
|
||||
return connRedis.GetDatabase().ListLength((RedisKey)stackName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mette in Stack un valore (F.I.L.O.)
|
||||
/// </summary>
|
||||
/// <param name="stackName"></param>
|
||||
/// <param name="value"></param>
|
||||
public void StackPush(string stackName, string value)
|
||||
{
|
||||
connRedis.GetDatabase().ListRightPush((RedisKey)stackName, (RedisValue)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera valore da Stack (F.I.L.O.)
|
||||
/// </summary>
|
||||
/// <param name="stackName"></param>
|
||||
/// <returns></returns>
|
||||
public string StackPop(string stackName)
|
||||
{
|
||||
return connRedis.GetDatabase().ListRightPop((RedisKey)stackName).ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lunghezza List
|
||||
/// </summary>
|
||||
/// <param name="queueName"></param>
|
||||
/// <returns></returns>
|
||||
public long ListLen(string queueName)
|
||||
{
|
||||
return connRedis.GetDatabase().ListLength((RedisKey)queueName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mette un valore in List (F.I.F.O.)
|
||||
/// </summary>
|
||||
/// <param name="queueName"></param>
|
||||
/// <returns></returns>
|
||||
public void ListPush(string queueName, string value)
|
||||
{
|
||||
connRedis.GetDatabase().ListRightPush((RedisKey)queueName, (RedisValue)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupera valore da List (F.I.F.O.)
|
||||
/// </summary>
|
||||
/// <param name="queueName"></param>
|
||||
/// <returns></returns>
|
||||
public string ListPop(string queueName)
|
||||
{
|
||||
return connRedis.GetDatabase().ListLeftPop((RedisKey)queueName).ToString();
|
||||
}
|
||||
|
||||
#endregion gestione Stack / List
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SteamWare.IO</RootNamespace>
|
||||
<AssemblyName>SteamWare.IO</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.6.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
<NuGetPackageImportStamp>
|
||||
@@ -172,6 +172,7 @@
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Redis.cs" />
|
||||
<Compile Include="SteamWare.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
Generated
+551
-457
File diff suppressed because it is too large
Load Diff
+162
-136
@@ -9,141 +9,167 @@ using System.Windows.Forms;
|
||||
|
||||
namespace TestBench
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
public MainForm()
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
InitializeComponent();
|
||||
myInitComponent();
|
||||
public MainForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
myInitComponent();
|
||||
}
|
||||
|
||||
private void myInitComponent()
|
||||
{
|
||||
// imposto folders
|
||||
txtSourceDir.Text = @"C:\TMP\";
|
||||
txtDestDir.Text = @"L:\TMP\";
|
||||
|
||||
lblHwSwData.Text = SteamWare.HwSwInfo.man(Assembly.GetExecutingAssembly()).librariesVers;
|
||||
|
||||
// reset conf CDV
|
||||
memLayer.ML.resetAppConf();
|
||||
}
|
||||
|
||||
private void BtnCopyAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
// effettuo copia con steamware libs...
|
||||
string sourceDir = txtSourceDir.Text.Trim();
|
||||
string destDir = txtDestDir.Text.Trim();
|
||||
if (sourceDir != "" && destDir != "")
|
||||
{
|
||||
lblOutMessage.Text = "inizio copia...";
|
||||
lblOutMessage.Refresh();
|
||||
fileMover.DirectoryCopy(sourceDir, destDir, chkRecursive.Checked);
|
||||
lblOutMessage.Text = "Copia completata!";
|
||||
}
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
// apro selettore file...
|
||||
openFileDialog1.ShowDialog();
|
||||
}
|
||||
|
||||
private void openFileDialog1_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
// prendo i files selezionati --> merge in pdf e indico quanti...
|
||||
List<string> elencoFiles = new List<string>();
|
||||
foreach (var item in openFileDialog1.FileNames)
|
||||
{
|
||||
elencoFiles.Add(item);
|
||||
}
|
||||
int numFiles = 0;
|
||||
string outPath = $"{Directory.GetCurrentDirectory()}\\{txtMergedName.Text}";
|
||||
try
|
||||
{
|
||||
numFiles = pdfUtils.mergePdfFiles(txtMergedName.Text, elencoFiles);
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
lblNumMerged.Text = $"Effettuato merge di {numFiles} files!";
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = memLayer.ML.getRedisInfoData();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
lblRedis.Text = answ;
|
||||
}
|
||||
|
||||
private void btnResetCdv_Click(object sender, EventArgs e)
|
||||
{
|
||||
memLayer.ML.resetAppConf();
|
||||
}
|
||||
|
||||
private void btnShowCdv_Click(object sender, EventArgs e)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("----- REDIS CDV data -----");
|
||||
foreach (var item in memLayer.ML.AppConf)
|
||||
{
|
||||
sb.AppendLine($"{item.Key}: {item.Value}");
|
||||
}
|
||||
lblRedCDV.Text = sb.ToString();
|
||||
}
|
||||
|
||||
private void btnStartSched_Click(object sender, EventArgs e)
|
||||
{
|
||||
// avvia uno scheduler che apre una messagebox secondo programmazione impostata...
|
||||
|
||||
// For Interval in Minutes
|
||||
// This Scheduler will start at 22:00 and call after every 30 Minutes
|
||||
// IntervalInSeconds(start_hour, start_minute, minutes)
|
||||
|
||||
// calcolo istante dal PROSSIMO minuto
|
||||
DateTime avvio = DateTime.Now;
|
||||
lblStartTimer.Text = $"Avvio timer: {avvio:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
avvio = avvio.AddMinutes(1).AddSeconds(-avvio.Second);
|
||||
lblNextSched.Text = $"Scadenza timer: {avvio:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
|
||||
clockTimer.Start();
|
||||
|
||||
// calcolo periodo in minuti guardando quanto selezionato
|
||||
int ore = 0;
|
||||
int min = 0;
|
||||
int sec = 0;
|
||||
int.TryParse(txtOre.Text, out ore);
|
||||
int.TryParse(txtMin.Text, out min);
|
||||
int.TryParse(txtSec.Text, out sec);
|
||||
// calcolo
|
||||
double intSecondi = ore * 60 * 60 + min * 60 + sec;
|
||||
|
||||
TaskSched.IntervalInSeconds(avvio.Hour, avvio.Minute, intSecondi,
|
||||
() =>
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
|
||||
// Initializes the variables to pass to the MessageBox.Show method.
|
||||
string message = $"E' scattato il timer alle ore {DateTime.Now}.";
|
||||
string caption = "Timer Elapsed";
|
||||
MessageBoxButtons buttons = MessageBoxButtons.OK;
|
||||
DialogResult result;
|
||||
|
||||
//lblNextSched.Text = $"Scadenza timer: {adesso.AddSeconds(intSecondi):ddd yyyy-MM-dd HH:mm:ss}";
|
||||
// Displays the MessageBox.
|
||||
result = MessageBox.Show(message, caption, buttons);
|
||||
});
|
||||
}
|
||||
|
||||
private void clockTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
lblClock.Text = $"Clock: {adesso:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
|
||||
updateQueue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorno da lettura coda...
|
||||
/// </summary>
|
||||
private void updateQueue()
|
||||
{
|
||||
// leggo da coda
|
||||
SteamWare.IO.Redis myRedis = new SteamWare.IO.Redis();
|
||||
string myKey = "testQueue";
|
||||
if (myRedis.ListLen(myKey) > 0)
|
||||
{
|
||||
// aggiorno coda
|
||||
lblQueueRead.Text = $"{myRedis.ListPop(myKey)}{Environment.NewLine}{lblQueueRead.Text}";
|
||||
}
|
||||
}
|
||||
|
||||
private void btnSend_Click(object sender, EventArgs e)
|
||||
{
|
||||
// pubblico su coda...
|
||||
SteamWare.IO.Redis myRedis = new SteamWare.IO.Redis();
|
||||
string myKey = "testQueue";
|
||||
myRedis.ListPush(myKey, $"{DateTime.Now} | {txtQueueMessage.Text.Trim()}");
|
||||
txtQueueMessage.Text = "";
|
||||
}
|
||||
}
|
||||
|
||||
private void myInitComponent()
|
||||
{
|
||||
// imposto folders
|
||||
txtSourceDir.Text = @"C:\TMP\";
|
||||
txtDestDir.Text = @"L:\TMP\";
|
||||
|
||||
lblHwSwData.Text = SteamWare.HwSwInfo.man(Assembly.GetExecutingAssembly()).librariesVers;
|
||||
|
||||
// reset conf CDV
|
||||
memLayer.ML.resetAppConf();
|
||||
}
|
||||
|
||||
private void BtnCopyAll_Click(object sender, EventArgs e)
|
||||
{
|
||||
// effettuo copia con steamware libs...
|
||||
string sourceDir = txtSourceDir.Text.Trim();
|
||||
string destDir = txtDestDir.Text.Trim();
|
||||
if (sourceDir != "" && destDir != "")
|
||||
{
|
||||
lblOutMessage.Text = "inizio copia...";
|
||||
lblOutMessage.Refresh();
|
||||
fileMover.DirectoryCopy(sourceDir, destDir, chkRecursive.Checked);
|
||||
lblOutMessage.Text = "Copia completata!";
|
||||
}
|
||||
}
|
||||
|
||||
private void button1_Click(object sender, EventArgs e)
|
||||
{
|
||||
// apro selettore file...
|
||||
openFileDialog1.ShowDialog();
|
||||
}
|
||||
|
||||
private void openFileDialog1_FileOk(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
// prendo i files selezionati --> merge in pdf e indico quanti...
|
||||
List<string> elencoFiles = new List<string>();
|
||||
foreach (var item in openFileDialog1.FileNames)
|
||||
{
|
||||
elencoFiles.Add(item);
|
||||
}
|
||||
int numFiles = 0;
|
||||
string outPath = $"{Directory.GetCurrentDirectory()}\\{txtMergedName.Text}";
|
||||
try
|
||||
{
|
||||
numFiles = pdfUtils.mergePdfFiles(txtMergedName.Text, elencoFiles);
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
lblNumMerged.Text = $"Effettuato merge di {numFiles} files!";
|
||||
}
|
||||
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
string answ = "";
|
||||
try
|
||||
{
|
||||
answ = memLayer.ML.getRedisInfoData();
|
||||
}
|
||||
catch
|
||||
{ }
|
||||
|
||||
lblRedis.Text = answ;
|
||||
}
|
||||
|
||||
private void btnResetCdv_Click(object sender, EventArgs e)
|
||||
{
|
||||
memLayer.ML.resetAppConf();
|
||||
}
|
||||
|
||||
private void btnShowCdv_Click(object sender, EventArgs e)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("----- REDIS CDV data -----");
|
||||
foreach (var item in memLayer.ML.AppConf)
|
||||
{
|
||||
sb.AppendLine($"{item.Key}: {item.Value}");
|
||||
}
|
||||
lblRedCDV.Text = sb.ToString();
|
||||
}
|
||||
|
||||
private void btnStartSched_Click(object sender, EventArgs e)
|
||||
{
|
||||
// avvia uno scheduler che apre una messagebox secondo programmazione impostata...
|
||||
|
||||
// For Interval in Minutes
|
||||
// This Scheduler will start at 22:00 and call after every 30 Minutes
|
||||
// IntervalInSeconds(start_hour, start_minute, minutes)
|
||||
|
||||
// calcolo istante dal PROSSIMO minuto
|
||||
DateTime avvio = DateTime.Now;
|
||||
lblStartTimer.Text = $"Avvio timer: {avvio:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
avvio = avvio.AddMinutes(1).AddSeconds(-avvio.Second);
|
||||
lblNextSched.Text = $"Scadenza timer: {avvio:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
|
||||
clockTimer.Start();
|
||||
|
||||
// calcolo periodo in minuti guardando quanto selezionato
|
||||
int ore = 0;
|
||||
int min = 0;
|
||||
int sec = 0;
|
||||
int.TryParse(txtOre.Text, out ore);
|
||||
int.TryParse(txtMin.Text, out min);
|
||||
int.TryParse(txtSec.Text, out sec);
|
||||
// calcolo
|
||||
double intSecondi = ore * 60 * 60 + min * 60 + sec;
|
||||
|
||||
TaskSched.IntervalInSeconds(avvio.Hour, avvio.Minute, intSecondi,
|
||||
() =>
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
|
||||
// Initializes the variables to pass to the MessageBox.Show method.
|
||||
string message = $"E' scattato il timer alle ore {DateTime.Now}.";
|
||||
string caption = "Timer Elapsed";
|
||||
MessageBoxButtons buttons = MessageBoxButtons.OK;
|
||||
DialogResult result;
|
||||
|
||||
lblNextSched.Text = $"Scadenza timer: {adesso.AddSeconds(intSecondi):ddd yyyy-MM-dd HH:mm:ss}";
|
||||
// Displays the MessageBox.
|
||||
result = MessageBox.Show(message, caption, buttons);
|
||||
});
|
||||
}
|
||||
|
||||
private void clockTimer_Tick(object sender, EventArgs e)
|
||||
{
|
||||
DateTime adesso = DateTime.Now;
|
||||
lblClock.Text = $"Clock: {adesso:ddd yyyy-MM-dd HH:mm:ss}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user