1668 lines
64 KiB
C#
1668 lines
64 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Data.Common;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using System.Runtime.CompilerServices;
|
|
using System.IO;
|
|
using System.Xml;
|
|
using System.Security.Principal;
|
|
using System.Net;
|
|
using System.Globalization;
|
|
|
|
using C4IT.Logging;
|
|
using C4IT.Security;
|
|
using C4IT.XML;
|
|
using C4IT.FASD.Base;
|
|
|
|
using static C4IT.Logging.cLogManager;
|
|
|
|
#if !NETSTANDARD && !NETCORE
|
|
using System.Data.SqlClient;
|
|
#else
|
|
using Microsoft.Data.SqlClient;
|
|
#endif
|
|
|
|
namespace C4IT.DataHistoryProvider
|
|
{
|
|
public static class DataHistorySqlHelper
|
|
{
|
|
public const string constWellDefinedSqlFile = "DataHistorySql";
|
|
|
|
public const int constDefaultSqlTimeout = 20;
|
|
|
|
public const string constBinValueNameAdd = "_bin";
|
|
|
|
private static string ApplicationName = null;
|
|
|
|
private static string WorkstationID = null;
|
|
|
|
public readonly static Dictionary<string, cDbQuery> wellDefinedSqlStatements = new Dictionary<string, cDbQuery>();
|
|
|
|
public static int lastSqlConnectionError { get; private set; } = 0;
|
|
|
|
public static bool LogSql = false;
|
|
public static string SqlLogFileName = null;
|
|
public static object SqlLogSync = new object();
|
|
|
|
public static void Initialize(Assembly Ass = null)
|
|
{
|
|
if (WorkstationID != null)
|
|
return;
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
if (Ass == null)
|
|
Ass = Assembly.GetExecutingAssembly();
|
|
|
|
ApplicationName = cLogManagerFile.GetAssemblyTitle(Ass);
|
|
WorkstationID = Environment.MachineName;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
static DataHistorySqlHelper()
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
Initialize();
|
|
|
|
// initialize encryption enclave providers at statup, because this will not work if the first sql operation is impersonated
|
|
// see https://support.microsoft.com/en-us/topic/sqlconnection-instantiation-exception-on-net-4-6-and-later-after-august-september-2018-net-framework-updates-8486716b-6f39-8ed5-37f6-25487ee8b9ab
|
|
// see https://stackoverflow.com/questions/12791207/catastrophic-failure-when-impersonate-other-user/16777117#16777117
|
|
System.Configuration.ConfigurationManager.GetSection("SqlColumnEncryptionEnclaveProviders");
|
|
System.Configuration.ConfigurationManager.GetSection("SectionNameThatDoesNotExistInAnyConfigurationFile");
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
public static void SaveSqlTimingEntry(string SqlQueryName, DateTime start, double duration, string SqlQueryParams = null, int error = 0, string OriginName = null)
|
|
{
|
|
try
|
|
{
|
|
if (!LogSql || SqlLogFileName == null)
|
|
return;
|
|
|
|
var _strStart = start.ToString("yyyy-MM-dd-HH-mm-ss-FFFFF");
|
|
var _strEnd = DateTime.UtcNow.ToString("yyyy-MM-dd-HH-mm-ss-FFFFF");
|
|
|
|
var _line = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\r\n", _strStart, _strEnd, SqlQueryName, duration, error, SqlQueryParams ?? "", OriginName ?? "");
|
|
lock (SqlLogSync)
|
|
System.IO.File.AppendAllText(SqlLogFileName, _line, Encoding.UTF8);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
public static void SaveSqlTimingEntry(string SqlQueryName, DateTime timeStart, string SqlQueryParams = null, int error = 0, string OriginName = null)
|
|
{
|
|
try
|
|
{
|
|
if (!LogSql || SqlLogFileName == null)
|
|
return;
|
|
|
|
var end = DateTime.UtcNow;
|
|
var duration = (end - timeStart).TotalMilliseconds;
|
|
|
|
SaveSqlTimingEntry(SqlQueryName, timeStart, duration, SqlQueryParams, error, OriginName);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
public static void SaveSqlTimingEntry(string SqlQueryName, DateTime start, double duration, Dictionary<string, Object> SqlQueryParams, int error = 0, string OriginName = null)
|
|
{
|
|
try
|
|
{
|
|
if (!LogSql || SqlLogFileName == null)
|
|
return;
|
|
|
|
var strSqlParams = "";
|
|
if (SqlQueryParams != null)
|
|
{
|
|
foreach (var _entry in SqlQueryParams)
|
|
{
|
|
if (!string.IsNullOrEmpty(strSqlParams))
|
|
strSqlParams += "|";
|
|
strSqlParams += _entry.Key + "=" + _entry.Value?.ToString() ?? "";
|
|
}
|
|
}
|
|
|
|
SaveSqlTimingEntry(SqlQueryName, start, duration, strSqlParams, error, OriginName);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
public static void SaveSqlTimingEntry(string SqlQueryName, DateTime timeStart, Dictionary<string, Object> SqlQueryParams, int error = 0, string OriginName = null)
|
|
{
|
|
try
|
|
{
|
|
if (!LogSql || SqlLogFileName == null)
|
|
return;
|
|
|
|
var end = DateTime.UtcNow;
|
|
var duration = (end - timeStart).TotalMilliseconds;
|
|
|
|
SaveSqlTimingEntry(SqlQueryName, timeStart, duration, SqlQueryParams, error, OriginName);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
}
|
|
|
|
public static bool LoadWellKnownSqlStatements(out List<cXmlParserNodeMessage> ParsingMessages, Assembly ExecutingAssembly = null)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
ParsingMessages = new List<cXmlParserNodeMessage>();
|
|
|
|
cXmlParser Parser = null;
|
|
|
|
try
|
|
{
|
|
wellDefinedSqlStatements.Clear();
|
|
|
|
var Locations = new List<cXmlFileLocation>() {
|
|
new cXmlFileLocation() { LocationType = cXmlFileLocation.enumXmlLocationType.ProgramFolder, SubFolder = "", Location = constWellDefinedSqlFile + ".xml", ExecutingAssembly = ExecutingAssembly },
|
|
new cXmlFileLocation() { LocationType = cXmlFileLocation.enumXmlLocationType.EmbeddedResource, SubFolder = "", Location = constWellDefinedSqlFile + ".xml", ExecutingAssembly = ExecutingAssembly }
|
|
};
|
|
|
|
|
|
var XmlRoot = cXmlParser.OpenXmlDocument(Locations, out Parser, constWellDefinedSqlFile);
|
|
|
|
if (XmlRoot == null || Parser == null || !Parser.IsValid)
|
|
return false;
|
|
|
|
var XQueries = XmlRoot.SelectSingleNode("Queries");
|
|
if (XQueries != null)
|
|
{
|
|
Parser.EnterElement("Queries");
|
|
|
|
var XList = XQueries.SelectNodes("Query");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("Query");
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XQuery))
|
|
continue;
|
|
|
|
var Q = new cDbQuery
|
|
{
|
|
Name = cXmlParser.GetStringFromXmlAttribute(XQuery, "Name"),
|
|
Query = cXmlParser.GetInnerTextFromXmlElement(XQuery),
|
|
TimeoutFactor = cXmlParser.GetIntegerFromXmlAttribute(XQuery, "TimeoutFactor", 1),
|
|
Subsequent = cXmlParser.GetIntegerFromXmlAttribute(XQuery, "Subsequent", 0),
|
|
Description = cXmlParser.GetStringFromXmlAttribute(XQuery, "Description", "")
|
|
};
|
|
|
|
if (string.IsNullOrWhiteSpace(Q.Name))
|
|
{
|
|
Parser.AddInvalidName(XQuery);
|
|
continue;
|
|
}
|
|
else if (string.IsNullOrWhiteSpace(Q.Query))
|
|
{
|
|
Parser.AddMessage(XQuery, $"No SQL statement in element <query Name=\"{Q.Name}\">");
|
|
continue;
|
|
}
|
|
else if (wellDefinedSqlStatements.ContainsKey(Q.Name))
|
|
{
|
|
Parser.AddDuplicateName(XQuery, Q.Name);
|
|
continue;
|
|
}
|
|
|
|
wellDefinedSqlStatements.Add(Q.Name, Q);
|
|
|
|
Parser.SelectElementNext();
|
|
}
|
|
Parser.LeaveElement("Query");
|
|
}
|
|
|
|
Parser.LeaveElement("Queries");
|
|
}
|
|
|
|
if (Parser.LogMessages.Count == 0)
|
|
return true;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (Parser != null)
|
|
ParsingMessages = Parser.LogMessages;
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static bool GetWellKnownSqlStatement(string Name, out cDbQuery Query)
|
|
{
|
|
if (wellDefinedSqlStatements.TryGetValue(Name, out var RetVal))
|
|
{
|
|
Query = RetVal;
|
|
return true;
|
|
}
|
|
|
|
Query = null;
|
|
return false;
|
|
}
|
|
|
|
private static void GetFormatters(bool formatted, out string strCF, out string strTab)
|
|
{
|
|
strCF = " ";
|
|
strTab = "";
|
|
if (formatted)
|
|
{
|
|
strCF = "\n";
|
|
strTab = "\t";
|
|
}
|
|
}
|
|
|
|
public static string GetSqlCreateStatement(cDataHistoryConfigTable Table, bool UseBinValuesAsPrimaryKey, bool formatted, bool Full = false)
|
|
{
|
|
GetFormatters(formatted, out var strCF, out var strTab);
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var strColumns = "[ScanId] [uniqueidentifier] NOT NULL";
|
|
if (Table.Type == eDataHistoryTableType.Events)
|
|
strColumns += ", [EventId] [uniqueidentifier] NOT NULL";
|
|
|
|
foreach (var Entry in Table.Columns.Values)
|
|
{
|
|
if (Entry is cDataHistoryConfigColumnComputation)
|
|
continue;
|
|
if (strColumns != "")
|
|
strColumns += "," + strCF;
|
|
strColumns += strTab;
|
|
|
|
var strNull = "NULL";
|
|
if (!Table.IsNullable(Entry))
|
|
strNull = "NOT " + strNull;
|
|
|
|
strColumns += string.Format("[{0}] {1} {2}", Entry.Name, Entry.SqlType, strNull);
|
|
|
|
if (Entry.SqlTypeBin != null)
|
|
{
|
|
strColumns += "," + strCF + strTab + string.Format("[{0}{1}] {2} {3}", Entry.Name, constBinValueNameAdd, Entry.SqlTypeBin, strNull);
|
|
}
|
|
}
|
|
|
|
var strSql = string.Format("CREATE TABLE [{0}] (", Table.SourceName) + strCF + strColumns + strCF + GetSqlPrimaryKeyStatement(Table, UseBinValuesAsPrimaryKey, formatted) + ");";
|
|
|
|
|
|
strSql += strCF + strCF + $"ALTER TABLE [{Table.SourceName}] ADD CONSTRAINT [DF_{Table.SourceName}_ScanId] DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [ScanId];";
|
|
if (Table.Type == eDataHistoryTableType.Events)
|
|
strSql += strCF + strCF + $"ALTER TABLE [{Table.SourceName}] ADD CONSTRAINT [DF_{Table.SourceName}_eventId] DEFAULT (newid()) FOR [EventId];";
|
|
|
|
foreach (var Index in Table.Indexes.Values)
|
|
strSql += strCF + strCF + GetSqlIndexStatement(Index, Table, formatted);
|
|
|
|
if (Full)
|
|
{
|
|
if (Table.Type == eDataHistoryTableType.Events || Table.Type == eDataHistoryTableType.History || Table.Type == eDataHistoryTableType.HistoryEvents)
|
|
strSql += strCF + $"ALTER TABLE [{Table.SourceName}] WITH CHECK ADD CONSTRAINT [FK_{Table.SourceName}_scan-history] FOREIGN KEY([ScanId]) REFERENCES [main-scan-history] ([ScanId]) ON DELETE CASCADE;";
|
|
|
|
foreach (var Ref in Table.References.Values)
|
|
strSql += strCF + GetReferencingStatements(Table, Ref, formatted);
|
|
}
|
|
|
|
return strSql;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string GetSqlPrimaryKeyStatement(cDataHistoryConfigTable Table, bool UseBinValuesAsPrimaryKey, bool formatted)
|
|
{
|
|
try
|
|
{
|
|
GetFormatters(formatted, out var strCF, out var strTab);
|
|
|
|
var strSql = "";
|
|
|
|
foreach (var Col in Table.KeyColumns)
|
|
{
|
|
if (strSql != "")
|
|
strSql += "," + strCF;
|
|
strSql += strTab;
|
|
|
|
var strColName = Col.Name;
|
|
if (UseBinValuesAsPrimaryKey && Col.SqlTypeBin != null)
|
|
strColName += constBinValueNameAdd;
|
|
|
|
strSql += string.Format("[{0}] ASC", strColName);
|
|
}
|
|
if (Table.Type == eDataHistoryTableType.History)
|
|
strSql += "," + strCF + "[ScanId]";
|
|
else if (Table.Type == eDataHistoryTableType.Events)
|
|
{
|
|
strSql += "," + strCF + $"[{Table.EventTimeColumn.Name}]";
|
|
strSql += "," + strCF + "[EventId]";
|
|
}
|
|
else if (Table.Type == eDataHistoryTableType.HistoryEvents)
|
|
{
|
|
strSql += "," + strCF + "[ScanId]";
|
|
strSql += "," + strCF + $"[{Table.EventTimeColumn.Name}]";
|
|
}
|
|
|
|
strSql = $"CONSTRAINT [PK_{Table.SourceName}]" + strCF + "PRIMARY KEY CLUSTERED (" + strCF + strSql + ")";
|
|
|
|
return strSql;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
private static string GetSqlIndexStatement(cDataHistoryConfigTableIndex Index, cDataHistoryConfigTable Table, bool formatted = false)
|
|
{
|
|
try
|
|
{
|
|
GetFormatters(formatted, out var strCF, out var strTab);
|
|
|
|
var strCols = "";
|
|
foreach (var Entry in Index.Columns)
|
|
{
|
|
if (strCols != "")
|
|
strCols += "," + strCF;
|
|
strCols += strTab + "[" + Entry.Name + "] ASC";
|
|
}
|
|
|
|
var strUnique = "";
|
|
if (Index.Unique)
|
|
strUnique = " UNIQUE";
|
|
|
|
var strSql = $"CREATE{strUnique} INDEX [{Index.Name}]{strCF}ON [{Table.SourceName}] ({strCF}{strCols})";
|
|
|
|
return strSql;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
public static string GetReferencingName(cDataHistoryConfigTable Table, cDataHistoryConfigTableReferenceInfo RefInfo)
|
|
{
|
|
return string.Format("FK_{0}_{1}", Table.SourceName, RefInfo.ForeignTable.SourceName);
|
|
}
|
|
|
|
public static string GetReferencingStatements(cDataHistoryConfigTable Table, cDataHistoryConfigTableReferenceInfo Ref, bool formatted = false)
|
|
{
|
|
try
|
|
{
|
|
GetFormatters(formatted, out var strCF, out var strTab);
|
|
|
|
var Result = "";
|
|
Result += strCF;
|
|
var strTable = Table.SourceName;
|
|
var strTableRef = Ref.ForeignTableName;
|
|
var strName = GetReferencingName(Table, Ref);
|
|
|
|
var strCols = "";
|
|
var strColsRef = "";
|
|
var ColsForeign = Ref.ReferencedColumns.GetEnumerator();
|
|
foreach (var Col in Ref.Columns)
|
|
{
|
|
if (!ColsForeign.MoveNext())
|
|
return "";
|
|
|
|
var ColRef = ColsForeign.Current;
|
|
var strBin = "";
|
|
if (Col.SqlTypeBin != null && ColRef.SqlTypeBin != null)
|
|
strBin = "_bin";
|
|
|
|
if (strCols != "")
|
|
strCols += ", ";
|
|
strCols += strCF + strTab + "[" + Col.Name + strBin + "]";
|
|
|
|
if (strColsRef != "")
|
|
strColsRef += ", ";
|
|
strColsRef += strCF + strTab + "[" + ColRef.Name + strBin + "]";
|
|
}
|
|
|
|
var Line = string.Format("ALTER TABLE [{0}]{5}WITH CHECK ADD CONSTRAINT [{1}]{5}FOREIGN KEY ({2}){5}REFERENCES [{3}] ({4})", strTable, strName, strColsRef, strTableRef, strCols, strCF);
|
|
Result += Line;
|
|
|
|
return Result;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesVersion(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val == null)
|
|
return new object[2] { null, null };
|
|
|
|
Version v = null;
|
|
if (val is Version _ver)
|
|
v = _ver;
|
|
else if (val is byte[] _arr)
|
|
{
|
|
if (_arr.Length <= 8 && ((_arr.Length & 1) == 0))
|
|
{
|
|
var strVersion = "";
|
|
for (int i = 0; i < _arr.Length; i += 2)
|
|
{
|
|
if (strVersion != "")
|
|
strVersion += '.';
|
|
strVersion += ((int)_arr[i] + ((int)_arr[i + 1]) >> 8).ToString();
|
|
}
|
|
return new object[2] { strVersion, _arr };
|
|
}
|
|
}
|
|
else if (Version.TryParse(val.ToString(), out var _ver2))
|
|
v = _ver2;
|
|
|
|
if (v != null)
|
|
{
|
|
byte[] _arr = null;
|
|
if (v.Major < 0)
|
|
return null;
|
|
else if (v.Minor < 0)
|
|
_arr = new byte[2] { (byte)(v.Major & 0xFF), (byte)((v.Major & 0xFF00) << 8) };
|
|
else if (v.Build < 0)
|
|
_arr = new byte[4] { (byte)(v.Minor & 0xFF), (byte)((v.Minor & 0xFF00) << 8), (byte)(v.Major & 0xFF), (byte)((v.Major & 0xFF00) << 8) };
|
|
else if (v.Revision < 0)
|
|
_arr = new byte[6] { (byte)(v.Build & 0xFF), (byte)((v.Build & 0xFF00) << 8), (byte)(v.Minor & 0xFF), (byte)((v.Minor & 0xFF00) << 8), (byte)(v.Major & 0xFF), (byte)((v.Major & 0xFF00) << 8) };
|
|
else
|
|
_arr = new byte[8] { (byte)(v.Revision & 0xFF), (byte)((v.Revision & 0xFF00) << 8), (byte)(v.Build & 0xFF), (byte)((v.Build & 0xFF00) << 8), (byte)(v.Minor & 0xFF), (byte)((v.Minor & 0xFF00) << 8), (byte)(v.Major & 0xFF), (byte)((v.Major & 0xFF00) << 8) };
|
|
|
|
return new object[2] { v.ToString(), _arr };
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[2] { null, null }; ;
|
|
}
|
|
|
|
|
|
internal static object[] _GetSqlValuesMD5(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val == null)
|
|
return new object[2] { null, null };
|
|
|
|
if (val is byte[] _arr)
|
|
{
|
|
if (_arr.Length == 16)
|
|
{
|
|
StringBuilder result = new StringBuilder(_arr.Length * 2);
|
|
|
|
for (int i = 0; i < _arr.Length; i++)
|
|
result.Append(_arr[i].ToString("X2"));
|
|
|
|
return new object[2] { result, _arr };
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
var strMD5 = val.ToString();
|
|
var _arr2 = new byte[16];
|
|
if (strMD5.Length == 32)
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
var strHex = strMD5.Substring(i * 2, 2);
|
|
_arr2[i] = Convert.ToByte(strHex, 16);
|
|
}
|
|
|
|
return new object[2] { strMD5, _arr2 };
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[2] { null, null }; ;
|
|
}
|
|
|
|
|
|
internal static object[] _GetSqlValuesSID(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val == null)
|
|
return new object[2] { null, null };
|
|
|
|
if (val is byte[] arrByte)
|
|
{
|
|
var sid = new SecurityIdentifier(arrByte, 0);
|
|
var strSid = sid.ToString();
|
|
return new object[2] { strSid, arrByte };
|
|
}
|
|
|
|
var sid2 = new SecurityIdentifier(val.ToString());
|
|
var _arr = new byte[sid2.BinaryLength];
|
|
sid2.GetBinaryForm(_arr, 0);
|
|
return new object[2] { sid2.ToString(), _arr };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[2] { null, null }; ;
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesIP4(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val == null)
|
|
return new object[2] { null, null };
|
|
|
|
if (val is byte[] arrByte && arrByte.Length == 4)
|
|
{
|
|
var _ip = new IPAddress(arrByte);
|
|
var strIP = _ip.ToString();
|
|
return new object[2] { strIP, arrByte };
|
|
}
|
|
|
|
var strIp = val.ToString();
|
|
if (IPAddress.TryParse(strIp, out var _ip2))
|
|
{
|
|
if (_ip2.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
|
{
|
|
var _arr = _ip2.GetAddressBytes();
|
|
return new object[2] { _ip2.ToString(), _arr };
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[2] { null, null }; ;
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesIP6(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val == null)
|
|
return new object[2] { null, null };
|
|
|
|
if (val is byte[] arrByte)
|
|
{
|
|
var _ip = new IPAddress(arrByte);
|
|
if (_ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
|
|
{
|
|
var strIP = _ip.ToString();
|
|
return new object[2] { strIP, arrByte };
|
|
}
|
|
}
|
|
|
|
var strIp = val.ToString();
|
|
if (IPAddress.TryParse(strIp, out var _ip2))
|
|
{
|
|
if (_ip2.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
|
{
|
|
var _arr = _ip2.GetAddressBytes();
|
|
return new object[2] { _ip2.ToString(), _arr };
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[2] { null, null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesString(object val, int Cardinal)
|
|
{
|
|
try
|
|
{
|
|
var s1 = val.ToString();
|
|
if (Cardinal >= 0 && s1.Length > Cardinal)
|
|
s1 = s1.Substring(0, Cardinal);
|
|
return new object[1] { s1 };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesGuid(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val is Guid _guid)
|
|
return new object[1] { _guid };
|
|
|
|
if (Guid.TryParse(val.ToString(), out var _guid2))
|
|
return new object[1] { _guid2 };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesInt(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val is Int32 _int)
|
|
return new object[1] { _int };
|
|
|
|
var _int2 = Convert.ToInt32(val);
|
|
return new object[1] { _int2 };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesBigInt(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val is Int64 _int)
|
|
return new object[1] { _int };
|
|
|
|
var _int2 = Convert.ToInt64(val);
|
|
return new object[1] { _int2 };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesFloat(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val is float _f)
|
|
return new object[1] { _f };
|
|
|
|
var _f2 = Convert.ToSingle(val);
|
|
return new object[1] { _f2 };
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
internal static object[] _GetSqlValuesDateTime(object val)
|
|
{
|
|
try
|
|
{
|
|
if (val is DateTime _dt)
|
|
{
|
|
if (_dt.Kind != DateTimeKind.Utc)
|
|
_dt = DateTime.SpecifyKind(_dt, DateTimeKind.Utc);
|
|
return new object[1] { _dt };
|
|
}
|
|
|
|
if (DateTime.TryParse(val.ToString(), CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal, out var _dt2))
|
|
{
|
|
if (_dt2.Kind != DateTimeKind.Utc)
|
|
_dt2 = DateTime.SpecifyKind(_dt2, DateTimeKind.Utc);
|
|
return new object[1] { _dt2 };
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return new object[1] { null };
|
|
}
|
|
|
|
public static object[] GetSqlDataValueFromHistoryType(object val, enumFasdValueType type, int Cardinal)
|
|
{
|
|
try
|
|
{
|
|
object[] retVal = null;
|
|
switch (type)
|
|
{
|
|
case enumFasdValueType.VERSION:
|
|
retVal = _GetSqlValuesVersion(val);
|
|
break;
|
|
case enumFasdValueType.MD5:
|
|
retVal = _GetSqlValuesMD5(val);
|
|
break;
|
|
case enumFasdValueType.SID:
|
|
retVal = _GetSqlValuesSID(val);
|
|
break;
|
|
case enumFasdValueType.IPV4:
|
|
retVal = _GetSqlValuesIP4(val);
|
|
break;
|
|
case enumFasdValueType.IPV6:
|
|
retVal = _GetSqlValuesIP6(val);
|
|
break;
|
|
}
|
|
if (retVal != null)
|
|
{
|
|
if (retVal.Length > 1 && retVal[1] == null)
|
|
retVal[1] = System.Data.SqlTypes.SqlBinary.Null;
|
|
return retVal;
|
|
}
|
|
|
|
if (val == null)
|
|
return new object[1] { null };
|
|
if (type != enumFasdValueType.STRING && val is string str)
|
|
if (str == "")
|
|
return null;
|
|
|
|
switch (type)
|
|
{
|
|
case enumFasdValueType.STRING:
|
|
return _GetSqlValuesString(val, Cardinal);
|
|
case enumFasdValueType.GUID:
|
|
return _GetSqlValuesGuid(val);
|
|
case enumFasdValueType.INT:
|
|
return _GetSqlValuesInt(val);
|
|
case enumFasdValueType.BIGINT:
|
|
return _GetSqlValuesBigInt(val);
|
|
case enumFasdValueType.FLOAT:
|
|
return _GetSqlValuesFloat(val);
|
|
case enumFasdValueType.DATETIME:
|
|
return _GetSqlValuesDateTime(val);
|
|
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static string CreateConnectionString(cDataHistoryConfigSqlConnection Connection, int Timeout = -1, bool MARS = false, bool WithoutDatabase = false)
|
|
{
|
|
try
|
|
{
|
|
var _connBuilder = new SqlConnectionStringBuilder()
|
|
{
|
|
ApplicationName = ApplicationName,
|
|
WorkstationID = WorkstationID,
|
|
MultipleActiveResultSets = MARS,
|
|
IntegratedSecurity = !Connection.NativeAccount
|
|
};
|
|
|
|
if (Timeout >= 0)
|
|
_connBuilder.ConnectTimeout = Timeout;
|
|
else if (Connection.Timeout >= 0)
|
|
_connBuilder.ConnectTimeout = Connection.Timeout;
|
|
else
|
|
_connBuilder.ConnectTimeout = constDefaultSqlTimeout;
|
|
|
|
var _DataSource = Connection.Server;
|
|
if (!string.IsNullOrWhiteSpace(Connection.Instance))
|
|
_DataSource += @"\" + Connection.Instance;
|
|
_connBuilder.DataSource = _DataSource;
|
|
|
|
if (!WithoutDatabase)
|
|
_connBuilder.InitialCatalog = Connection.Database;
|
|
|
|
var RetVal = _connBuilder.ConnectionString;
|
|
return RetVal;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static SqlConnection OpenConnection(cDataHistoryConfigSqlConnection Connection, int Timeout = -1, bool MARS = false, bool WithoutDatabase = false, bool LogError = true)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
lastSqlConnectionError = 0;
|
|
|
|
SqlConnection _conn = null;
|
|
var _cs = CreateConnectionString(Connection, Timeout, MARS, WithoutDatabase);
|
|
try
|
|
{
|
|
if (Connection.NativeAccount)
|
|
{
|
|
Connection.Credential.Password.MakeReadOnly();
|
|
_conn = new SqlConnection(_cs, new SqlCredential(Connection.Credential.User, Connection.Credential.Password));
|
|
_conn.Open();
|
|
}
|
|
else
|
|
{
|
|
WindowsIdentity.RunImpersonated(Connection.Credential.SafeAccessToken, () =>
|
|
{
|
|
try
|
|
{
|
|
if (System.Diagnostics.Debugger.IsAttached) // when running locally, the sql server does not have a certificate.
|
|
_cs += ";TrustServerCertificate=True";
|
|
|
|
_conn = new SqlConnection(_cs);
|
|
_conn.Open();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
LogException(ex);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
#if NETFRAMEWORK
|
|
catch (SqlException E)
|
|
{
|
|
lastSqlConnectionError = E.Number;
|
|
if (LogError)
|
|
LogEntry(string.Format("error opening sql connection '{0}': {1}, {2}", _cs, E.Number, E.Message), LogLevels.Error);
|
|
throw;
|
|
}
|
|
#endif
|
|
catch
|
|
{
|
|
if (LogError)
|
|
LogEntry(string.Format("error opening sql connection: {0}", _cs), LogLevels.Error);
|
|
throw;
|
|
}
|
|
|
|
if (_conn == null || _conn.State != System.Data.ConnectionState.Open)
|
|
LogEntry(string.Format("error opening sql connection: {0}", _cs), LogLevels.Warning);
|
|
|
|
return _conn;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
if (LogError)
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<int?> ExecuteAsync(cDbConnection Connection, cDbQuery Query, Dictionary<string, Object> Parameters, cF4sdWebRequestInfo requestInfo, CancellationToken Token, DbTransaction Transaction = null, bool LogNoResult = false, bool LogSilent = false, int Timeout = 0)
|
|
{
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
if (Timeout == 0)
|
|
Timeout = Connection.Connector.Timeout * Query.TimeoutFactor;
|
|
|
|
var res = await ExecuteAsync(Connection.Connection, Query.Query, Parameters, Token, Timeout, Transaction, LogNoResult, LogSilent);
|
|
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, Parameters, 0, requestInfo?.requestName);
|
|
|
|
return res;
|
|
}
|
|
|
|
public static async Task<int?> ExecuteAsync(DbConnection Connection, string Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null, bool LogNoResult = false, bool LogSilent = false)
|
|
{
|
|
MethodBase CM = null;
|
|
if (!LogSilent)
|
|
{
|
|
CM = MethodBase.GetCurrentMethod();
|
|
LogMethodBegin(CM);
|
|
}
|
|
|
|
int? RetVal = null;
|
|
|
|
try
|
|
{
|
|
using (DbCommand DbCmd = Connection.CreateCommand())
|
|
{
|
|
if (Timeout > 0)
|
|
DbCmd.CommandTimeout = Timeout;
|
|
|
|
DbCmd.CommandText = Query;
|
|
DbCmd.Transaction = Transaction;
|
|
if (Parameters != null)
|
|
{
|
|
foreach (var Entry in Parameters)
|
|
{
|
|
DbParameter Param = DbCmd.CreateParameter();
|
|
Param.ParameterName = Entry.Key;
|
|
if (Entry.Value == null)
|
|
Param.Value = DBNull.Value;
|
|
else
|
|
Param.Value = Entry.Value;
|
|
DbCmd.Parameters.Add(Param);
|
|
}
|
|
}
|
|
RetVal = await DbCmd.ExecuteNonQueryAsync(Token);
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
LogSqlInfo("ExecuteAsync", Query, Parameters);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
public static async Task<DbDataReader> GetDataReaderAsync(cDbConnection Connection, cDbQuery Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null, bool LogNoResult = false)
|
|
{
|
|
return await GetDataReaderAsync(Connection.Connection, Query.Query, Parameters, Token, Connection.Connector.Timeout * Query.TimeoutFactor, Transaction, LogNoResult);
|
|
}
|
|
|
|
public static async Task<DbDataReader> GetDataReaderAsync(DbConnection Connection, string Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null, bool LogNoResult = false)
|
|
{
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
try
|
|
{
|
|
using (DbCommand DbCmd = Connection.CreateCommand())
|
|
{
|
|
if (Timeout < 0)
|
|
DbCmd.CommandTimeout = Timeout;
|
|
|
|
DbCmd.CommandText = Query;
|
|
DbCmd.Transaction = Transaction;
|
|
if (Parameters != null)
|
|
{
|
|
foreach (var Entry in Parameters)
|
|
{
|
|
DbParameter Param = DbCmd.CreateParameter();
|
|
Param.ParameterName = Entry.Key;
|
|
Param.Value = Entry.Value;
|
|
DbCmd.Parameters.Add(Param);
|
|
}
|
|
}
|
|
|
|
var RetVal = await DbCmd.ExecuteReaderAsync(Token);
|
|
return RetVal;
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
LogSqlInfo("ExecuteAsync", Query, Parameters);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<T> GetScalarResultAsync<T>(cDbConnection Connection, cDbQuery Query, Dictionary<string, Object> Parameters, cF4sdWebRequestInfo requestInfo, CancellationToken Token, DbTransaction Transaction = null, bool LogNoResult = false)
|
|
{
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
var res = await GetScalarResultAsync<T>(Connection.Connection, Query.Query, Parameters, Token, Connection.Connector.Timeout * Query.TimeoutFactor, Transaction, LogNoResult);
|
|
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, Parameters, 0, requestInfo?.requestName);
|
|
|
|
return res;
|
|
}
|
|
|
|
public static async Task<T> GetScalarResultAsync<T>(DbConnection Connection, string Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null, bool LogNoResult = false)
|
|
{
|
|
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
try
|
|
{
|
|
using (DbCommand DbCmd = Connection.CreateCommand())
|
|
{
|
|
if (Timeout < 0)
|
|
DbCmd.CommandTimeout = Timeout;
|
|
|
|
DbCmd.CommandText = Query;
|
|
DbCmd.Transaction = Transaction;
|
|
if (Parameters != null)
|
|
{
|
|
foreach (var Entry in Parameters)
|
|
{
|
|
DbParameter Param = DbCmd.CreateParameter();
|
|
Param.ParameterName = Entry.Key;
|
|
Param.Value = Entry.Value;
|
|
DbCmd.Parameters.Add(Param);
|
|
}
|
|
}
|
|
var oRetVal = await DbCmd.ExecuteScalarAsync(Token);
|
|
if (LogNoResult && (oRetVal == null || oRetVal is DBNull))
|
|
{
|
|
LogEntry("No result (NULL) on SQL execute", LogLevels.Error);
|
|
LogSqlInfo("GetScalarResultAsync", Query, Parameters);
|
|
}
|
|
if (oRetVal is T RetVal)
|
|
return RetVal;
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
LogSqlInfo("GetScalarResultAsync", Query, Parameters);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
public static async Task<DbDataReader> GetTableResultAsync(cDbConnection Connection, cDbQuery Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null)
|
|
{
|
|
return await GetTableResultAsync(Connection.Connection, Query.Query, Parameters, Token, Connection.Connector.Timeout * Query.TimeoutFactor, Transaction);
|
|
}
|
|
|
|
public static async Task<DbDataReader> GetTableResultAsync(DbConnection Connection, string Query, Dictionary<string, Object> Parameters, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null, bool SupportCancellation = false)
|
|
{
|
|
DbDataReader RetVal = null;
|
|
|
|
try
|
|
{
|
|
using (DbCommand DbCmd = Connection.CreateCommand())
|
|
{
|
|
if (Timeout != 0)
|
|
DbCmd.CommandTimeout = Timeout;
|
|
|
|
DbCmd.CommandText = Query;
|
|
DbCmd.Transaction = Transaction;
|
|
if (Parameters != null)
|
|
{
|
|
foreach (var Entry in Parameters)
|
|
{
|
|
DbParameter Param = DbCmd.CreateParameter();
|
|
Param.ParameterName = Entry.Key;
|
|
Param.Value = Entry.Value;
|
|
DbCmd.Parameters.Add(Param);
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
RetVal = await DbCmd.ExecuteReaderAsync(Token);
|
|
}
|
|
catch
|
|
{
|
|
if (!Token.IsCancellationRequested || !SupportCancellation)
|
|
throw;
|
|
else
|
|
{
|
|
DbCmd.Cancel();
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (RetVal == null)
|
|
{
|
|
LogEntry("No result (NULL) on SQL execute", LogLevels.Error);
|
|
LogSqlInfo("GetTableResult", Query, Parameters);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
if (E is TaskCanceledException)
|
|
{
|
|
|
|
}
|
|
cLogManager.DefaultLogger.LogException(E);
|
|
LogSqlInfo("GetTableResult", Query, Parameters);
|
|
RetVal = null;
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
public static async Task<bool> CheckIfTableExistAsync(DbConnection Connection, string TableName, cF4sdWebRequestInfo requestInfo, int LogDeep)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
var Query = "SELECT COUNT(OBJECT_ID(@TableName, N'U'))";
|
|
var Params = new Dictionary<string, Object>() {
|
|
{ "TableName", TableName }
|
|
};
|
|
|
|
var Res = await GetScalarResultAsync<int>(Connection, Query, Params, CancellationToken.None, LogNoResult: true);
|
|
if (Res >= 1)
|
|
return true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public static bool CheckDbConnection(DbConnection Connection)
|
|
{
|
|
return (Connection != null && Connection.State == System.Data.ConnectionState.Open);
|
|
}
|
|
|
|
public static void LogSqlInfo(string Func, string Query, Dictionary<string, Object> Parameters)
|
|
{
|
|
var Msg = string.Format("DataHistorySqlHelper.{0}: Query={1}", Func, Query);
|
|
if (Parameters != null)
|
|
{
|
|
var lstMsg = new List<string>
|
|
{
|
|
Msg,
|
|
"Parameters:"
|
|
};
|
|
foreach (var Entry in Parameters)
|
|
lstMsg.Add(Entry.Key + "=" + Entry.Value?.ToString());
|
|
cLogManager.DefaultLogger.LogList(LogLevels.Warning, lstMsg);
|
|
}
|
|
else
|
|
cLogManager.DefaultLogger.LogEntry(LogLevels.Warning, Msg);
|
|
}
|
|
|
|
public static async Task<Dictionary<string, cSqlReferenceInfo>> GetTableReferences(DbConnection Connection, string TableName, CancellationToken Token)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var Query = "SELECT fk.name name, tb.name tableName, cp.name tableColumn, tr.name referencedTable, cr.name referencedColumn FROM sys.foreign_keys fk";
|
|
Query += " INNER JOIN sys.tables tb ON tb.object_id = fk.parent_object_id";
|
|
Query += " INNER JOIN sys.tables tr ON tr.object_id = fk.referenced_object_id";
|
|
Query += " INNER JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id";
|
|
Query += " INNER JOIN sys.columns cp ON fkc.parent_column_id = cp.column_id AND fkc.parent_object_id = cp.object_id";
|
|
Query += " INNER JOIN sys.columns cr ON fkc.referenced_column_id = cr.column_id AND fkc.referenced_object_id = cr.object_id";
|
|
Query += " WHERE tb.name = @TableName";
|
|
|
|
var Params = new Dictionary<string, object>()
|
|
{
|
|
{ "TableName", TableName}
|
|
};
|
|
|
|
var _res = new Dictionary<string, cSqlReferenceInfo>();
|
|
using (var _reader = await GetDataReaderAsync(Connection, Query, Params, Token, LogNoResult: true))
|
|
{
|
|
if (_reader.HasRows)
|
|
{
|
|
while (await _reader.ReadAsync())
|
|
{
|
|
try
|
|
{
|
|
var _RefName = _reader.GetString(0);
|
|
if (!_res.TryGetValue(_RefName, out var _if))
|
|
{
|
|
_if = new cSqlReferenceInfo()
|
|
{
|
|
Table = _reader.GetString(1),
|
|
ReferencedTable = _reader.GetString(3),
|
|
Name = _RefName
|
|
};
|
|
_res.Add(_RefName, _if);
|
|
_if.Columns.Add(_reader.GetString(2));
|
|
_if.ReferencedColumns.Add(_reader.GetString(4));
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return _res;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<DbDataReader> getDbReaderHistoricTable(cDbConnection Conn, string Table, Dictionary<string, object> Filter, DateTime LastEventTime, CancellationToken Token)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var strFilter = "";
|
|
int i = 0;
|
|
var Params = new Dictionary<string, object>();
|
|
foreach (var Entry in Filter)
|
|
{
|
|
i++;
|
|
if (strFilter != "")
|
|
strFilter += " AND ";
|
|
strFilter += "t.[" + Entry.Key + "] = @P" + i.ToString();
|
|
Params.Add("P" + i.ToString(), Entry.Value);
|
|
}
|
|
Params.Add("LASTTIME", LastEventTime);
|
|
|
|
var Cmd = $"SELECT h.[TimeFrameFrom], h.[TimeFrameTo], t.* FROM [{Table}] t JOIN [main-scan-history] h ON h.[ScanId] = t.[ScanId] WHERE {strFilter} AND h.[State] = 1 AND h.[TimeFrameFrom] >= @LASTTIME ORDER BY h.[TimeFrameTo] DESC";
|
|
|
|
var _reader = await GetDataReaderAsync(Conn.Connection, Cmd, Params, Token);
|
|
return _reader;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<DbDataReader> getDbReaderSelectAsync(cDbConnection Conn, string Table, IEnumerable<string> Columns, Dictionary<string, object> Filter, CancellationToken Token, int Timeout = 0, DbTransaction Transaction = null)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var strFields = "";
|
|
foreach (var strField in Columns)
|
|
{
|
|
if (strFields != "")
|
|
strFields += ",";
|
|
strFields += "[" + strField + "]";
|
|
}
|
|
|
|
var strFilter = "";
|
|
int i = 0;
|
|
var Params = new Dictionary<string, object>();
|
|
foreach (var Entry in Filter)
|
|
{
|
|
i++;
|
|
if (strFilter != "")
|
|
strFilter += " AND ";
|
|
strFilter += "[" + Entry.Key + "] = @P" + i.ToString();
|
|
Params.Add("P" + i.ToString(), Entry.Value);
|
|
}
|
|
if (strFilter != "")
|
|
strFilter = " WHERE " + strFilter;
|
|
|
|
var Cmd = $"SELECT {strFields} FROM [{Table}]{strFilter}";
|
|
|
|
var Reader = await GetDataReaderAsync(Conn.Connection, Cmd, Params, Token, Timeout, Transaction);
|
|
return Reader;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<DbDataReader> getDbReaderDetailTable(cDbConnection Conn, string Table, Dictionary<string, object> Filter, string rowTime, int Age, CancellationToken Token)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var strFilter = "";
|
|
int i = 0;
|
|
var Params = new Dictionary<string, object>();
|
|
foreach (var Entry in Filter)
|
|
{
|
|
i++;
|
|
if (strFilter != "")
|
|
strFilter += " AND ";
|
|
strFilter += "[" + Entry.Key + "] = @P" + i.ToString();
|
|
Params.Add("P" + i.ToString(), Entry.Value);
|
|
}
|
|
Params.Add("AGE", Age);
|
|
|
|
var Cmd = $"SELECT * FROM [{Table}] WHERE {strFilter} AND [{rowTime}] >= DATEADD(DAY,-@Age, GETUTCDATE()) ORDER BY [{rowTime}] DESC";
|
|
|
|
var _reader = await GetDataReaderAsync(Conn.Connection, Cmd, Params, Token);
|
|
return _reader;
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public static async Task<bool> DoUpdateOrInsertAsync(cDbConnection Connection, string Table, Dictionary<string, object> Columns, List<string> IDs, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, DbTransaction Transaction = null, bool LogNoResult = false, bool LogSilent = false)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
string strSet = "";
|
|
string strWhere = "";
|
|
string strInsertCol = "";
|
|
string strInstertVal = "";
|
|
var SqlParams = new Dictionary<string, object>();
|
|
|
|
int PNr = 0;
|
|
foreach (var Entry in Columns)
|
|
{
|
|
PNr++;
|
|
var PName = "P" + PNr.ToString();
|
|
var V = Entry.Value;
|
|
if (V == null)
|
|
V = DBNull.Value;
|
|
SqlParams.Add(PName, V);
|
|
|
|
if (strInsertCol != "")
|
|
strInsertCol += ", ";
|
|
strInsertCol += '[' + Entry.Key + ']';
|
|
|
|
if (strInstertVal != "")
|
|
strInstertVal += ", ";
|
|
strInstertVal += "@" + PName;
|
|
|
|
if (IDs.Contains(Entry.Key))
|
|
{
|
|
if (strWhere != "")
|
|
strWhere += " AND ";
|
|
|
|
strWhere += '[' + Entry.Key + "]=@" + PName;
|
|
}
|
|
else
|
|
{
|
|
if (strSet != "")
|
|
strSet += ", ";
|
|
strSet += '[' + Entry.Key + "]=@" + PName;
|
|
}
|
|
}
|
|
|
|
var Query = $"UPDATE [{Table}] SET {strSet} WHERE {strWhere}\n";
|
|
Query += $"IF @@ROWCOUNT = 0 INSERT INTO [{Table}] ({strInsertCol}) VALUES ({strInstertVal})";
|
|
var n = await ExecuteAsync(Connection.Connection, Query, SqlParams, Token, Connection.Connector.Timeout * 10, LogSilent: LogSilent);
|
|
if (n > 0)
|
|
return true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry("DoUpdateOrInsertAsync", _startTime, Table, 0, requestInfo?.requestName);
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static async Task<bool> DoInsertAsync(cDbConnection Connection, string Table, Dictionary<string, object> Columns, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep, DbTransaction Transaction = null, bool LogNoResult = false, bool LogSilent = false)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); }
|
|
var _startTime = DateTime.UtcNow;
|
|
|
|
try
|
|
{
|
|
string strInsertCol = "";
|
|
string strInstertVal = "";
|
|
var SqlParams = new Dictionary<string, object>();
|
|
|
|
int PNr = 0;
|
|
foreach (var Entry in Columns)
|
|
{
|
|
PNr++;
|
|
var PName = "P" + PNr.ToString();
|
|
var V = Entry.Value;
|
|
if (V == null)
|
|
V = DBNull.Value;
|
|
SqlParams.Add(PName, V);
|
|
|
|
if (strInsertCol != "")
|
|
strInsertCol += ", ";
|
|
strInsertCol += '[' + Entry.Key + ']';
|
|
|
|
if (strInstertVal != "")
|
|
strInstertVal += ", ";
|
|
strInstertVal += "@" + PName;
|
|
|
|
}
|
|
|
|
var Query = $"INSERT INTO [{Table}] ({strInsertCol}) VALUES ({strInstertVal})";
|
|
var n = await ExecuteAsync(Connection.Connection, Query, SqlParams, Token, Connection.Connector.Timeout * 10, LogSilent: LogSilent);
|
|
if (n > 0)
|
|
return true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry("DoInsertAsync", _startTime, Table, 0, requestInfo?.requestName);
|
|
if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); }
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public static DateTime ConvertDBDateTimeToUTC(DateTime d)
|
|
{
|
|
return DateTime.SpecifyKind(d, DateTimeKind.Utc);
|
|
}
|
|
|
|
public static string GetSqlLogFileName()
|
|
{
|
|
try
|
|
{
|
|
if (!(cLogManager.Instance is cLogManagerFile lf))
|
|
return null;
|
|
|
|
if (string.IsNullOrEmpty(lf.LogFolder))
|
|
return null;
|
|
|
|
var _fn = Path.GetFileNameWithoutExtension(lf.GetLogFileName());
|
|
return Path.Combine(lf.LogFolder, _fn + "-SqlTiming.log");
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
public class cSqlReferenceInfo
|
|
{
|
|
public string Table;
|
|
public string ReferencedTable;
|
|
public string Name;
|
|
public List<string> Columns = new List<string>();
|
|
public List<string> ReferencedColumns = new List<string>();
|
|
}
|
|
|
|
public class cDbQuery
|
|
{
|
|
public string Query = "";
|
|
public string Name = "";
|
|
public int TimeoutFactor = 1;
|
|
public int Subsequent = 0;
|
|
public string Description = "";
|
|
|
|
public cDbQuery Clone(string _query = null)
|
|
{
|
|
var me = new cDbQuery()
|
|
{
|
|
Query = Query,
|
|
Name = Name,
|
|
TimeoutFactor = TimeoutFactor,
|
|
Subsequent = Subsequent,
|
|
Description = Description
|
|
};
|
|
if (_query != null)
|
|
me.Query = _query;
|
|
return me;
|
|
}
|
|
}
|
|
|
|
public class cDbConnection : IDisposable
|
|
{
|
|
public DbConnection Connection { get; private set; } = null;
|
|
public cDataHistoryConfigSqlConnection Connector { get; private set; } = null;
|
|
public bool IsOpen { get; private set; } = false;
|
|
|
|
public cDbConnection(cDataHistoryConfigSqlConnection Connector, int? Timeout = null, bool MARS = false, bool WithoutDatabase = false, bool LogError = true)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
this.Connector = Connector;
|
|
int TO = Connector.Timeout;
|
|
if (Timeout != null)
|
|
TO = (int)Timeout;
|
|
|
|
Connection = DataHistorySqlHelper.OpenConnection(Connector, TO, MARS, WithoutDatabase, LogError);
|
|
IsOpen = DataHistorySqlHelper.CheckDbConnection(Connection);
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Connection != null)
|
|
{
|
|
try
|
|
{
|
|
if (Connection.State == ConnectionState.Open)
|
|
Connection.Close();
|
|
}
|
|
catch { }
|
|
;
|
|
try
|
|
{
|
|
Connection.Dispose();
|
|
}
|
|
catch { }
|
|
;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|