Commit d4dde2c4e3c5731acdfdb523c6aa34213af305f7

Authored by Schwirg László
1 parent 32c3a934

v1.31.0

- Multirun script-ek végrehajtása
Vrh.Log4Pro.MaintenanceConsole/ConsoleFunction - ColorConsole.cs
... ... @@ -10,6 +10,12 @@ using System.Diagnostics;
10 10  
11 11 using Vrh.XmlProcessing;
12 12 using System.Xml.Linq;
  13 +using Microsoft.SqlServer.Management.Smo;
  14 +using static Vrh.Log4Pro.MaintenanceConsole.CommandLineParserNS.CLP.Module.ScheduledTaskManager.Function;
  15 +using static Vrh.Log4Pro.MaintenanceConsole.FileCleanerManagerNS.FolderToClean.XmlStructure.FolderToClean.Conditions.Condition.Attributes;
  16 +using System.Reflection;
  17 +using System.Threading;
  18 +using System.Windows.Forms;
13 19  
14 20 namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS
15 21 {
... ... @@ -68,17 +74,35 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS
68 74 /// Visszaadja a lenyomott billentyűt, ha van, commandmode-ban pedig az enter-t
69 75 /// </summary>
70 76 /// <returns></returns>
71   - public static ConsoleKeyInfo ReadKey()
72   - {
73   - if (SilentMode) { return GetConsoleKey(ConsoleKey.Enter); }
74   - else { return Console.ReadKey(); }
75   - }
  77 + public static ConsoleKeyInfo? ReadKey(TimeSpan? maxwaittime)
  78 + {
  79 + if (SilentMode) { return GetConsoleKey(ConsoleKey.Enter); }
  80 + if (maxwaittime ==null || maxwaittime == TimeSpan.Zero) { return Console.ReadKey(); }
  81 + else
  82 + {
  83 + var strtime = DateTime.Now;
  84 + var endtime = strtime.Add(maxwaittime.Value);
  85 + var nexttime = strtime;
  86 + while (nexttime <= endtime)
  87 + {
  88 + if (Console.KeyAvailable) return Console.ReadKey(true);
  89 + Thread.Sleep(50);
  90 + nexttime = DateTime.Now;
  91 + }
  92 + return null;
  93 + }
  94 + }
  95 + public static ConsoleKeyInfo ReadKey()
  96 + {
  97 + if (SilentMode) { return GetConsoleKey(ConsoleKey.Enter); }
  98 + else { return Console.ReadKey(); }
  99 + }
76 100  
77   - /// <summary>
78   - /// Visszaadja a lenyomott billentyűt, ha van, commandmode-ban pedig az enter-t
79   - /// </summary>
80   - /// <returns></returns>
81   - public static ConsoleKeyInfo GetConsoleKey(ConsoleKey ck, bool shift = false, bool alt=false,bool control=false)
  101 + /// <summary>
  102 + /// Visszaadja a lenyomott billentyűt, ha van, commandmode-ban pedig az enter-t
  103 + /// </summary>
  104 + /// <returns></returns>
  105 + public static ConsoleKeyInfo GetConsoleKey(ConsoleKey ck, bool shift = false, bool alt=false,bool control=false)
82 106 {
83 107 switch (ck)
84 108 {
... ...
Vrh.Log4Pro.MaintenanceConsole/Manager - MaintenanceToolManager.cs
... ... @@ -65,7 +65,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS
65 65 ExternalProcess.StartInfo.FileName = config.Exe;
66 66 ExternalProcess.StartInfo.WindowStyle = config.ProcessWindowsStyle;
67 67  
68   - if (!Tools.ResolveArguments(config.ArgumentParameters, config.Arguments, out string resolvedtext)) { return o; };
  68 + if (!Tools.KvpString.Resolve(config.ArgumentParameters, config.Arguments, out string resolvedtext)) { return o; };
69 69 ExternalProcess.StartInfo.Arguments = resolvedtext;
70 70 ExternalProcess.Start();
71 71 int waitingtime = config.WaitForExit ? -1 : 0;
... ... @@ -633,8 +633,8 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS
633 633 Exe = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Exe), x, "");
634 634 this.Key = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Key), x, "");
635 635 this.Description = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Description), x, Exe);
636   - Tools.ResolveArguments(ArgumentParameters, this.Key,out this.Key,interactive:false);
637   - Tools.ResolveArguments(ArgumentParameters, this.Description, out this.Description, interactive: false);
  636 + Tools.KvpString.Resolve(ArgumentParameters, this.Key,out this.Key,disableinteractive:true);
  637 + Tools.KvpString.Resolve(ArgumentParameters, this.Description, out this.Description, disableinteractive: true);
638 638 Arguments = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Arguments), x, XmlStructure.ExternalUtility.Attributes.Arguments.Values.DEFAULT);
639 639  
640 640 ProcessWindowsStyle = GetValue<ProcessWindowStyle>(nameof(XmlStructure.ExternalUtility.Attributes.WindowStyle), x, XmlStructure.ExternalUtility.Attributes.WindowStyle.Values.DEFAULT);
... ...
Vrh.Log4Pro.MaintenanceConsole/Manager - SQLDataBaseManager.cs
... ... @@ -26,6 +26,12 @@ using System.Text.RegularExpressions;
26 26 using Microsoft.SqlServer.Management.Common;
27 27 using Microsoft.SqlServer.Management.Smo;
28 28 using Microsoft.Data.SqlClient;
  29 +using Microsoft.Identity.Client;
  30 +using VRH.Common.Log4ProIS;
  31 +using System.Security.Cryptography;
  32 +using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
  33 +using System.Windows.Controls;
  34 +using Menu = Vrh.Log4Pro.MaintenanceConsole.MenuNS.Menu;
29 35  
30 36 namespace Vrh.Log4Pro.MaintenanceConsole.SQLDataBaseManagerNS
31 37 {
... ... @@ -926,27 +932,30 @@ GO
926 932 else { return null; }
927 933 }
928 934  
929   - string ssScriptText = null;
930   - string LastUpdatedTrigger_parameters = $"DBNAME={triggertoexecute.Db};SCHEMA={triggertoexecute.Schema};TABLE={triggertoexecute.Table};COLUMN={triggertoexecute.Column};TRIGGER={LastUpdatedTrigger_triggername};";
931   - if (!Tools.ResolveArguments(LastUpdatedTrigger_parameters, RemoveLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
932   - try { SQLDataBaseManagerCore.ExecuteSQLScript(sqlcs, ssScriptText, 5000, null); }
933   - catch (Exception e) { ColorConsole.WriteLine(e.MessageNested(), ConsoleColor.Yellow); }
934   - if (!Tools.ResolveArguments(LastUpdatedTrigger_parameters, RemoveLastUpdatedColumn_Script, out ssScriptText)) { throw new ApplicationException(); }
935   - try { SQLDataBaseManagerCore.ExecuteSQLScript(sqlcs, ssScriptText, 5000, null); }
936   - catch (Exception e) { ColorConsole.WriteLine(e.MessageNested(), ConsoleColor.Yellow); }
937   -
938   - if (!triggertoexecute.Remove)
  935 + using (var sqlc = ServerConnectionPool.GetSqlConnection(sqlcs, open: true))
939 936 {
940   - if (!Tools.ResolveArguments(LastUpdatedTrigger_parameters, CreateLastUpdatedColumn_Script, out ssScriptText)) { throw new ApplicationException(); }
941   - SQLDataBaseManagerCore.ExecuteSQLScript(sqlcs, ssScriptText, 5000, null);
  937 + string ssScriptText = null;
  938 + string LastUpdatedTrigger_parameters = $"DBNAME={triggertoexecute.Db};SCHEMA={triggertoexecute.Schema};TABLE={triggertoexecute.Table};COLUMN={triggertoexecute.Column};TRIGGER={LastUpdatedTrigger_triggername};";
  939 + if (!Tools.KvpString.Resolve(LastUpdatedTrigger_parameters, RemoveLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
  940 + try { SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, 5000, null); }
  941 + catch (Exception e) { ColorConsole.WriteLine(e.MessageNested(), ConsoleColor.Yellow); }
  942 + if (!Tools.KvpString.Resolve(LastUpdatedTrigger_parameters, RemoveLastUpdatedColumn_Script, out ssScriptText)) { throw new ApplicationException(); }
  943 + try { SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, 5000, null); }
  944 + catch (Exception e) { ColorConsole.WriteLine(e.MessageNested(), ConsoleColor.Yellow); }
  945 +
  946 + if (!triggertoexecute.Remove)
  947 + {
  948 + if (!Tools.KvpString.Resolve(LastUpdatedTrigger_parameters, CreateLastUpdatedColumn_Script, out ssScriptText)) { throw new ApplicationException(); }
  949 + SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, 5000, null);
942 950  
943   - if (!Tools.ResolveArguments(LastUpdatedTrigger_parameters, CreateLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
944   - SQLDataBaseManagerCore.ExecuteSQLScript(sqlcs, ssScriptText, 5000, null);
  951 + if (!Tools.KvpString.Resolve(LastUpdatedTrigger_parameters, CreateLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
  952 + SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, 5000, null);
945 953  
946   - if (!Tools.ResolveArguments(LastUpdatedTrigger_parameters, EnableLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
947   - SQLDataBaseManagerCore.ExecuteSQLScript(sqlcs, ssScriptText, 5000, null);
  954 + if (!Tools.KvpString.Resolve(LastUpdatedTrigger_parameters, EnableLastUpdatedTrigger_Script, out ssScriptText)) { throw new ApplicationException(); }
  955 + SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, 5000, null);
  956 + }
948 957 }
949   - ColorConsole.WriteLine($"SUCCESS! {removeactionText} trigger to store LastUpdate TimeStamp: {LastUpdatedTrigger_fulldesignation}", ConsoleColor.Green);
  958 + ColorConsole.WriteLine($"SUCCESS! {removeactionText} trigger to store LastUpdate TimeStamp: {LastUpdatedTrigger_fulldesignation}", ConsoleColor.Green);
950 959 }
951 960 catch (ApplicationException e) { ColorConsole.WriteLine("FATAL ERROR! in script parameter substitution!", ConsoleColor.Red); returntext = null; }
952 961 catch (Exception e) { ColorConsole.WriteLine("FATAL ERROR! " + e.MessageNested(), ConsoleColor.Red); returntext = null; }
... ... @@ -985,29 +994,49 @@ GO
985 994 foreach (var s in sqld.Xml_SQLScriptList)
986 995 {
987 996 ColorConsole.Write(s.Key, ConsoleColor.Yellow, bracket: "[]",suffix:" ",prefix: " ");
988   - var fromfile = string.IsNullOrWhiteSpace(s.FilePath) ? "": $",from:{s.FilePath}";
989   - ColorConsole.Write($"{s.Description} ({s.Name}{fromfile})", ConsoleColor.Yellow, prefix: "Script:");
990   - ColorConsole.WriteLine();
  997 + ColorConsole.Write(s.Name, ConsoleColor.Yellow, bracket: "[]", prefix: "Script:");
  998 + if (s.Multirun) ColorConsole.Write("MULTIRUN", ConsoleColor.Red, bracket: "[]",prefix: " ");
  999 + ColorConsole.Write(s.Description, ConsoleColor.Yellow, prefix: " ");
  1000 + if (!string.IsNullOrWhiteSpace(s.FilePath)) { ColorConsole.Write(s.FilePath, ConsoleColor.Yellow, prefix: ", from file:");
  1001 + }
  1002 + ColorConsole.WriteLine();
991 1003 vlist.Add(s.Key);
992 1004 }
993 1005 var scriptkey = ColorConsole.ReadLine("Select the script! [empty]=next DB, [*]=all, [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: vlist);
994 1006 if (string.IsNullOrWhiteSpace(scriptkey)) { continue; }
995 1007 if (scriptkey.ToUpper() == "EX") { return o; }
996 1008 SQLDataBase.SQLScript ss = sqld.Xml_SQLScriptList.FirstOrDefault(s=>s.Key==scriptkey);
997   - if (!Tools.ResolveArguments(ss.ArgumentParameters, ss.ScriptText,out string ssScriptText)) { return o; }
998   - if (ss.ScriptText == null)
999   - {
1000   - ColorConsole.WriteLine($"Nothing to execute!. Check script definition entry!", ConsoleColor.Red);
1001   - }
  1009 + if (string.IsNullOrWhiteSpace(ss.ScriptText)) { ColorConsole.WriteLine($"Nothing to execute!. Check script definition entry!", ConsoleColor.Red); }
1002 1010 else
1003 1011 {
1004   - ColorConsole.WriteLine(ssScriptText);
1005 1012 var confirm = ColorConsole.ReadLine("Enter CONFIRM to start! [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: new List<string>() {"CONFIRM"});
1006 1013 if (confirm == "CONFIRM")
1007 1014 {
1008   - SQLDataBaseManagerCore.ExecuteSQLScript(sqld.SQLCS, ssScriptText, ss.CommandTimeout, null);
1009   - ColorConsole.WriteLine($"Script executed. Name:{sqld.DBName}, script name: {ss.Name}", ConsoleColor.Green);
1010   - }
  1015 + bool multirunmode = ss.Multirun;
  1016 + int commandtimeout = ss.CommandTimeout;
  1017 + ReturnInfoJSON result = null;
  1018 + using (var sqlc = ServerConnectionPool.GetSqlConnection(sqld.SQLCS, open: true))
  1019 + {
  1020 + if (multirunmode) { result = SQLDataBaseManagerCore.ExecuteMultirunSQLScript(sqlc, ss, ExitAfterOneRun); }
  1021 + else
  1022 + {
  1023 + if (!Tools.KvpString.Resolve(ss.ArgumentParameters, ss.ScriptText, out string ssScriptText)) { return o; }
  1024 + ColorConsole.WriteLine(ssScriptText);
  1025 + result = SQLDataBaseManagerCore.ExecuteSQLScript(sqlc, ssScriptText, commandtimeout, null);
  1026 + }
  1027 + }
  1028 + if (result.ReturnValue == 0)
  1029 + {
  1030 + ColorConsole.WriteLine(result.ReturnMessage, ConsoleColor.White);
  1031 + ColorConsole.WriteLine($"Script execution SUCCESS. Name:{sqld.DBName}, script name: {ss.Name}", ConsoleColor.Green);
  1032 + }
  1033 + else
  1034 + {
  1035 + ColorConsole.WriteLine(result.ReturnMessage, ConsoleColor.White);
  1036 + ColorConsole.WriteLine($"Script execution FAILURE. Name:{sqld.DBName}, script name: {ss.Name}", ConsoleColor.Red);
  1037 + }
  1038 +
  1039 + }
1011 1040 else {ColorConsole.WriteLine($"Script was NOT executed!", ConsoleColor.Red);}
1012 1041 }
1013 1042 }
... ... @@ -1016,6 +1045,16 @@ GO
1016 1045 }
1017 1046 return o;
1018 1047 }
  1048 + static bool ExitAfterOneRun(ReturnInfoJSON runresult)
  1049 + {
  1050 + if (runresult != null)
  1051 + {
  1052 + ColorConsole.Write(runresult.ReturnValue==0?"OK":"NOK", runresult.ReturnValue == 0 ? ConsoleColor.Green : ConsoleColor.Red);
  1053 + ColorConsole.WriteLine(runresult.ReturnMessage, ConsoleColor.White,prefix:" ");
  1054 + }
  1055 + var returnkey = ColorConsole.ReadKey(new TimeSpan(0, 0, 5));
  1056 + return returnkey != null;
  1057 + }
1019 1058 private static object DropDB(object parameter, object o)
1020 1059 {
1021 1060 var config = (parameter as Menu.ExecutorParameter).GetConfig<SQLDataBaseManagerXmlProcessor>();
... ... @@ -1893,32 +1932,218 @@ GO
1893 1932 {
1894 1933 DBKEY,DATABASE,DATASOURCE,DBOTYPE,DBONAME,DBDATAGROUP,BACKUPTS,SHRINKOPTION,SHRINKFREESPACEPERCENT,
1895 1934 }
1896   - #endregion DBSubstitution
  1935 + #endregion DBSubstitution
  1936 +
  1937 + #region ExecuteSQLScriptWithMultipleRuns
  1938 + /// <summary>
  1939 + /// Executes a multirun script. Its parameters are in the <Script Parameters=> attribute, as list of KEY=VALUE; pairs.
  1940 + /// Keys:
  1941 + /// DAYSBACK (C# int value) or LIMITDATE (C# DateTime value): how many days from now, or from when the older rows should be deleted
  1942 + /// NUMOFDAYSINONERUN (C# int value): how many days are deleted in one run
  1943 + /// DELAYBETWEENRUNS (C# TimeSpan value): how much delay will be between runs
  1944 + /// TESTMODE (C# bool value): if testmode, no delete takes place
  1945 + /// </summary>
  1946 + /// <param name="sqlc">connectionstring az sql server-hez</param>
  1947 + /// <param name="sqlscript">az sql script (multirun interfész szerint!!!)
  1948 + /// this should be used in the script in the following way:
  1949 + /// DECLARE @RUNNINGMODE AS varchar(20) = '{RUNNINGMODE}'
  1950 + /// IF @RUNNINGMODE = 'GETTOTALRECORDS'
  1951 + /// THEN
  1952 + /// -- this part of the script should count the number of the rows of the dab, that will decrease after each run
  1953 + /// SELECT @RV, @RM -- @RV:0, @RM:the number of rows
  1954 + /// END
  1955 + /// ELSE IF @RUNNINGMODE = 'GETMINDATE'
  1956 + /// THEN
  1957 + /// -- this part of the script should return a DateTime value, that is the oldest date in the database
  1958 + /// SELECT @RV, @RM -- @RV:0, @RM:the Date, that is considered the oldest; no rows will be deleted before this
  1959 + /// END
  1960 + /// ELSE IF @RUNNINGMODE = 'ONERUN'
  1961 + /// THEN
  1962 + /// -- this part of the script deletes some rows from the database, older then a limitdate(parameter)
  1963 + /// -- parameter usage: DECLARE @RUNDELETEBEFOREDATE AS DATE = '{RUNDELETEBEFOREDATE}'
  1964 + /// SELECT @RV, @RM -- @RV:(0=OK, otherwise NOK), @RM:message with the result description
  1965 + /// END
  1966 + /// </param>
  1967 + /// <param name="ExitAfterOneRun">az a függvény, ami minde run után végrehajtásra kerül;
  1968 + /// bemenő paramétere az előző run eredménye,
  1969 + /// kimenő paramétere pedig igaz, ha ki kell lépni a végrehajtásból.
  1970 + /// </param>
  1971 + /// <returns></returns>
  1972 + public static ReturnInfoJSON ExecuteMultirunSQLScript(SqlConnection sqlc, SQLDataBase.SQLScript sqlscript, Func<ReturnInfoJSON, bool> ExitAfterOneRun=null)
  1973 + {
  1974 + var multirunresult = new List<KeyValuePair<string, string>>();
  1975 +
  1976 + var argkvplist = (new Tools.KvpString(sqlscript.ArgumentParameters)).ResolveInteractive();
  1977 + if(argkvplist==null) { return Finalize(0, Add(multirunresult, ERRMSG_NOTHINGTOEXECUTE)); } //user exit with selecting EX
  1978 +
  1979 + bool par_testmode;
  1980 + int par_limitdays;
  1981 + DateTime par_limitdate;
  1982 + TimeSpan par_lengthofonerun;
  1983 + TimeSpan par_delaybetweenruns;
  1984 +
  1985 + #region parameterek feldolgozása
  1986 + var testmodestring = argkvplist.GetValue(XMLPAR_TESTMODE, "false");
  1987 + if ( !bool.TryParse(testmodestring, out par_testmode)) { return Finalize(1, Add(multirunresult, "Result", $"Parameter error:{XMLPAR_TESTMODE}")); }
  1988 +
  1989 + var daysbackstring = argkvplist.GetValue(XMLPAR_DAYSBACK, null);
  1990 + var limitdatestring = argkvplist.GetValue(XMLPAR_LIMITDATE, null);
  1991 + if (daysbackstring == null && limitdatestring == null) { par_limitdays = 90; par_limitdate = DateTime.Now.Subtract(new TimeSpan(par_limitdays, 0, 0, 0)); }
  1992 + else if (!string.IsNullOrEmpty(daysbackstring) && !string.IsNullOrEmpty(limitdatestring)) { return Finalize(1, Add(multirunresult, "Result", $"Parameter error:{XMLPAR_DAYSBACK} and {XMLPAR_LIMITDATE} are both defined.")); }
  1993 + else if (!string.IsNullOrEmpty(limitdatestring))
  1994 + {
  1995 + if (!DateTime.TryParse(limitdatestring, out par_limitdate)) return Finalize(1, Add(multirunresult, "Result", $"Parameter error:" + XMLPAR_LIMITDATE));
  1996 + par_limitdays = (int)DateTime.Now.Subtract(par_limitdate).TotalDays;
  1997 + }
  1998 + else /*if (!string.IsNullOrEmpty(daysbackstring)) */
  1999 + {
  2000 + if(!int.TryParse(daysbackstring, out par_limitdays)) return Finalize(1, Add(multirunresult, "Result", $"Parameter error:" + XMLPAR_DAYSBACK));
  2001 + par_limitdate = DateTime.Now.Subtract(new TimeSpan(par_limitdays, 0, 0, 0));
  2002 + }
1897 2003  
1898   - #region ExecuteSQLScript
1899   - /// <summary>
1900   - /// Egy SQL script végrehajtása (GO-val lezárt batch-eket tartalmazhat)
1901   - /// </summary>
1902   - /// <param name="sqlconnectionstring">sql connection string</param>
1903   - /// <param name="sqltxt">a script</param>
1904   - /// <param name="commandtimeout">az egyes batch-ek végrehajtási időzítése</param>
1905   - /// <param name="vars">
1906   - /// behelyettesítendő változók (a scriptben ezek nevei {} között kell legyenek)
1907   - /// a következő neveket felismeri akkor is, ha nincsenek benne a sztótárban:
1908   - /// DATASOURCE: a sql server neve domain/név formában
1909   - /// DATABASE: az aadatbázis neve
1910   - /// </param>
1911   - /// <returns>
1912   - /// egy ReturnInfoJSON struktúrát, amiben a ReturnValue és ReturnMessage a script által előállított két
1913   - /// (egy int és egy string) értéket tartalmazza pl. egy ilyen eredményt: SELECT @returncode, @returnmessage;
1914   - /// Ha a végrehajtás nem ad vissza eredményt, akkor az első érték 0, a második pedig null;
1915   - ///
1916   - /// </returns>
1917   - public static ReturnInfoJSON ExecuteSQLScript(string sqlconnectionstring, string sqltxt, int commandtimeout, Dictionary<string, string> vars)
1918   - {
1919   - using (var sqlc = ServerConnectionPool.GetSqlConnection(sqlconnectionstring,open:true)) { return ExecuteSQLScript(sqlc,sqltxt, commandtimeout, vars); }
1920   - }
1921   - public static ReturnInfoJSON ExecuteSQLScript(SqlConnection sqlconnection, string sqltxt, int commandtimeout, Dictionary<string, string> vars)
  2004 + if (!TimeSpan.TryParse(argkvplist.GetValue(XMLPAR_NUMOFDAYSINONERUN, "#$NONE#$"), out par_lengthofonerun)) { par_lengthofonerun = new TimeSpan(0, 12, 0); }
  2005 + if (!TimeSpan.TryParse(argkvplist.GetValue(XMLPAR_DELAYBETWEENRUNS, "#$NONE#$"), out par_delaybetweenruns)) { par_delaybetweenruns = new TimeSpan(0, 0, 10); }
  2006 + #endregion parameterek feldolgozása
  2007 + if (par_limitdays<=0) { return Finalize(0, Add(multirunresult, "Result", $"SKIPPED (function disabled).")); }
  2008 +
  2009 + Add(multirunresult, $"LIMIT", $"{par_limitdays}days (before:{par_limitdate})");
  2010 + Add(multirunresult, $"STAT", $"run length:{par_lengthofonerun.TotalHours}hours, delay between runs:{par_delaybetweenruns.TotalSeconds}sec");
  2011 + Add(multirunresult, $"TESTMODE", $"{par_testmode}");
  2012 +
  2013 + try
  2014 + {
  2015 + DateTime starttime = DateTime.Now;
  2016 + string sqltxtalmostresolved = argkvplist.Substitute(sqlscript.ScriptText); //parameters of the run are still unresolved
  2017 + if (sqltxtalmostresolved == null) {return Finalize(1, Add(multirunresult, ERRMSG_NOTHINGTOEXECUTE));} //won't really get, but we never know...
  2018 +
  2019 + ReturnInfoJSON ret1=null;
  2020 + string sqltxt = null;
  2021 + int? getdbnumofrowsresult=null;
  2022 + DateTime? getdbmindateresult=null;
  2023 +
  2024 + //get number of total records in the database
  2025 + getdbnumofrowsresult = GetDBNumofRows(sqltxtalmostresolved, multirunresult, sqlscript.CommandTimeout, sqlc);
  2026 + if (getdbnumofrowsresult == null) return Finalize(1, multirunresult);
  2027 + int numoftotaldbrecords_before = getdbnumofrowsresult.Value;
  2028 +
  2029 + //get minimum date in the database
  2030 + getdbmindateresult = GetDBMindate(sqltxtalmostresolved, multirunresult, sqlscript.CommandTimeout, sqlc);
  2031 + if (getdbmindateresult == null) return Finalize(1, multirunresult);
  2032 + DateTime mindateTS_before = getdbmindateresult.Value;
  2033 +
  2034 + var limitdatefornextrun = mindateTS_before;
  2035 + int exceptioncounter = 0;
  2036 + const int MAXEXCEPTIONS = 3; //after this number of subsequent exceptions we exit
  2037 + int deleteruncounter = 0;
  2038 + int runresult;
  2039 + while (true)
  2040 + {
  2041 + if (limitdatefornextrun >= par_limitdate) break;
  2042 + limitdatefornextrun = limitdatefornextrun + par_lengthofonerun;
  2043 + var loople = new List<KeyValuePair<string, string>>();
  2044 + if (limitdatefornextrun > par_limitdate) limitdatefornextrun = par_limitdate;
  2045 + Add(loople, $"RUN#{deleteruncounter}");
  2046 + try
  2047 + {
  2048 + sqltxt = Tools.KvpString.Substitute($"{RUNNINGMODE}={RUNNINGMODE_ONERUN};{RUNNINGMODE_ONERUN_PARAMETER_DELETEBEFOREDATE}={limitdatefornextrun:s};", sqltxtalmostresolved);
  2049 + if (sqltxt == null) { return Finalize(1, Add(multirunresult, ERRMSG_NOTHINGTOEXECUTE)); } //won't really get, but we never know...
  2050 + ret1 = ExecuteSQLScript(sqlc, sqltxt, sqlscript.CommandTimeout, null);
  2051 + Add(loople, $"LIMITDATE", limitdatefornextrun.ToString());
  2052 + Add(loople, $"RETCODE", ret1.ReturnValue.ToString());
  2053 + Add(loople, $"RETMSG", ret1.ReturnMessage);
  2054 +
  2055 + deleteruncounter++;
  2056 + exceptioncounter = 0;
  2057 + runresult = 0;
  2058 + }
  2059 + catch (Exception ex)
  2060 + {
  2061 + exceptioncounter++;
  2062 + string errmsg = ""; while (ex != null) { errmsg += ex.Message; ex = ex.InnerException; }
  2063 + Add(loople, $"EXCEPTION RESULT DeleteRun", errmsg);
  2064 + runresult = 1;
  2065 + if (exceptioncounter > MAXEXCEPTIONS) { return Finalize(1, multirunresult); }
  2066 + }
  2067 + var exitfromloop = (ExitAfterOneRun?.Invoke(Finalize(runresult, loople))) ?? false;
  2068 + if (exitfromloop) { Add(multirunresult, $"Exit requested by user"); break; }
  2069 + Thread.Sleep((int)(par_delaybetweenruns.TotalMilliseconds));
  2070 + }
  2071 +
  2072 + //get minimum date in the database
  2073 + getdbmindateresult = GetDBMindate(sqltxtalmostresolved,multirunresult, sqlscript.CommandTimeout,sqlc);
  2074 + if (getdbmindateresult==null) return Finalize(1, multirunresult);
  2075 + DateTime mindateTS_after = getdbmindateresult.Value;
  2076 +
  2077 + //get number of total records in the database
  2078 + getdbnumofrowsresult = GetDBNumofRows(sqltxtalmostresolved, multirunresult, sqlscript.CommandTimeout, sqlc);
  2079 + if (getdbnumofrowsresult == null) return Finalize(1, multirunresult);
  2080 + int numoftotaldbrecords_after = getdbnumofrowsresult.Value;
  2081 +
  2082 + Add(multirunresult, $"NUMOFRUNS", deleteruncounter.ToString());
  2083 + Add(multirunresult, $"DBRECORDS", $"{numoftotaldbrecords_before}-->{numoftotaldbrecords_after}");
  2084 + Add(multirunresult, $"MINDATE", $"{mindateTS_before}-->{mindateTS_after}");
  2085 + Add(multirunresult, $"PROCESSINGTIME", $"{DateTime.Now.Subtract(starttime)}");
  2086 + return Finalize(0, multirunresult);
  2087 + }
  2088 + catch (Exception ex) { return Finalize(1, Add(multirunresult, ex)); }
  2089 + }
  2090 + private static DateTime? GetDBMindate(string sqltxtalmostresolved, List<KeyValuePair<string, string>> multirunresult,int commandtimeout, SqlConnection sqlc)
  2091 + {
  2092 + //get minimum date in the database
  2093 + string sqltxt = Tools.KvpString.Substitute($"{RUNNINGMODE}={RUNNINGMODE_GETMINDATE};{RUNNINGMODE_ONERUN_PARAMETER_DELETEBEFOREDATE}=;", sqltxtalmostresolved);
  2094 + if (sqltxt == null) { Add(multirunresult, ERRMSG_NOTHINGTOEXECUTE); return null; } //won't really get, but we never know...
  2095 + var ret1 = ExecuteSQLScript(sqlc, sqltxt, commandtimeout, null);
  2096 + if (ret1.ReturnValue != 0) { Add(multirunresult, RUNNINGMODE_GETMINDATE + " FAILURE", ret1.ReturnMessage); return null; }
  2097 + return DateTime.Parse(ret1.ReturnMessage);//GetOldestDate.Invoke(sqlconnectionstring);
  2098 + }
  2099 + private static int? GetDBNumofRows(string sqltxtalmostresolved, List<KeyValuePair<string, string>> multirunresult, int commandtimeout, SqlConnection sqlc)
  2100 + {
  2101 + string sqltxt = Tools.KvpString.Substitute($"{RUNNINGMODE}={RUNNINGMODE_GETTOTALRECORDS};{RUNNINGMODE_ONERUN_PARAMETER_DELETEBEFOREDATE}=;", sqltxtalmostresolved);
  2102 + if (sqltxt == null) { Add(multirunresult, ERRMSG_NOTHINGTOEXECUTE); return null; } //won't really get, but we never know...
  2103 + var ret1 = ExecuteSQLScript(sqlc, sqltxt, commandtimeout, null);
  2104 + if (ret1.ReturnValue != 0) { Add(multirunresult, RUNNINGMODE_GETTOTALRECORDS + " FAILURE", ret1.ReturnMessage); return null; }
  2105 + return int.Parse(ret1.ReturnMessage);
  2106 + }
  2107 +
  2108 + const string RUNNINGMODE = "RUNNINGMODE";
  2109 + const string RUNNINGMODE_GETTOTALRECORDS = "GETTOTALRECORDS";
  2110 + const string RUNNINGMODE_GETMINDATE = "GETMINDATE";
  2111 + const string RUNNINGMODE_ONERUN = "ONERUN";
  2112 + const string RUNNINGMODE_ONERUN_PARAMETER_DELETEBEFOREDATE = "RUNDELETEBEFOREDATE";
  2113 +
  2114 + const string XMLPAR_NUMOFDAYSINONERUN = "NUMOFDAYSINONERUN";
  2115 + const string XMLPAR_DELAYBETWEENRUNS = "DELAYBETWEENRUNS";
  2116 + const string XMLPAR_TESTMODE = "TESTMODE";
  2117 + const string XMLPAR_DAYSBACK = "DAYSBACK";
  2118 + const string XMLPAR_LIMITDATE = "LIMITDATE";
  2119 +
  2120 + const string ERRMSG_NOTHINGTOEXECUTE = "FAILURE! Nothing to execute! Check script definition entry!";
  2121 +
  2122 + private static List<KeyValuePair<string, string>> Add(List<KeyValuePair<string, string>> kvp, string a, string b=null) { kvp.Add(new KeyValuePair<string, string>(a, b)); return kvp; }
  2123 + private static List<KeyValuePair<string, string>> Add(List<KeyValuePair<string, string>> kvp,Exception ex) { string errmsg = ""; while (ex != null) { errmsg += ex.Message; ex = ex.InnerException; } return Add(kvp, "EXCEPTION RESULT",errmsg); }
  2124 + private static ReturnInfoJSON Finalize(int returnvalue,List<KeyValuePair<string, string>> kvplist) { var rmsg = "";foreach (var kvp in kvplist) rmsg += string.IsNullOrWhiteSpace(kvp.Value)? $"{kvp.Key};" : $"{kvp.Key}={kvp.Value};"; return new ReturnInfoJSON() { ReturnValue = returnvalue, ReturnMessage = rmsg, }; }
  2125 + #endregion ExecuteSQLScriptWithMultipleRuns
  2126 +
  2127 + #region ExecuteSQLScript
  2128 + /// <summary>
  2129 + /// Egy SQL script végrehajtása (GO-val lezárt batch-eket tartalmazhat)
  2130 + /// </summary>
  2131 + /// <param name="sqlconnectionstring">sql connection string</param>
  2132 + /// <param name="sqltxt">a script</param>
  2133 + /// <param name="commandtimeout">az egyes batch-ek végrehajtási időzítése</param>
  2134 + /// <param name="vars">
  2135 + /// behelyettesítendő változók (a scriptben ezek nevei {} között kell legyenek)
  2136 + /// a következő neveket felismeri akkor is, ha nincsenek benne a sztótárban:
  2137 + /// DATASOURCE: a sql server neve domain/név formában
  2138 + /// DATABASE: az aadatbázis neve
  2139 + /// </param>
  2140 + /// <returns>
  2141 + /// egy ReturnInfoJSON struktúrát, amiben a ReturnValue és ReturnMessage a script által előállított két
  2142 + /// (egy int és egy string) értéket tartalmazza pl. egy ilyen eredményt: SELECT @returncode, @returnmessage;
  2143 + /// Ha a végrehajtás nem ad vissza eredményt, akkor az első érték 0, a második pedig null;
  2144 + ///
  2145 + /// </returns>
  2146 + public static ReturnInfoJSON ExecuteSQLScript(SqlConnection sqlconnection, string sqltxt, int commandtimeout, Dictionary<string, string> vars)
1922 2147 {
1923 2148 sqltxt = VRH.Common.StringConstructor.ResolveConstructorR(vars, sqltxt, "{}@@");
1924 2149  
... ... @@ -2607,6 +2832,7 @@ GO
2607 2832 public static class Description { public static class Values { public const string DEFAULT = ""; } }
2608 2833 public static class ScriptCommandTimeout { public static class Values { public const int DEFAULT = 10000; } }
2609 2834 public static class Parameters { public static class Values { public const string DEFAULT = ""; } }
  2835 + public static class Multirun { public static class Values { public const bool DEFAULT = false; } }
2610 2836 }
2611 2837 }
2612 2838 }
... ... @@ -2623,6 +2849,7 @@ GO
2623 2849 public string ScriptText = "";
2624 2850 public int CommandTimeout = 10000;
2625 2851 public string ArgumentParameters;
  2852 + public bool Multirun = false;
2626 2853  
2627 2854 public SQLScript() { }
2628 2855 public SQLScript(SQLScript sqls) { Name = sqls.Name; Description = sqls.Description; ScriptText= sqls.ScriptText; }
... ... @@ -2634,8 +2861,10 @@ GO
2634 2861 Name = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name.Values.DEFAULT);
2635 2862 FilePath = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.File), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.File.Values.DEFAULT);
2636 2863 Description= GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Description), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Description.Values.DEFAULT);
2637   - Tools.ResolveArguments(ArgumentParameters, this.Name, out this.Name, interactive: false);
2638   - Tools.ResolveArguments(ArgumentParameters, this.Description, out this.Description, interactive: false);
  2864 + Multirun= GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Multirun), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Multirun.Values.DEFAULT);
  2865 +
  2866 + Tools.KvpString.Resolve(ArgumentParameters, this.Name, out this.Name, disableinteractive: true);
  2867 + Tools.KvpString.Resolve(ArgumentParameters, this.Description, out this.Description, disableinteractive: true);
2639 2868 if (string.IsNullOrWhiteSpace(this.FilePath))
2640 2869 {
2641 2870 ScriptText = GetValue(sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Values.DEFAULT);
... ...
Vrh.Log4Pro.MaintenanceConsole/Properties/AssemblyInfo.cs
... ... @@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
32 32 // You can specify all the values or you can default the Build and Revision Numbers
33 33 // by using the '*' as shown below:
34 34 // [assembly: AssemblyVersion("1.0.*")]
35   -[assembly: AssemblyVersion("1.30.1.0")]
36   -[assembly: AssemblyFileVersion("1.30.1.0")]
  35 +[assembly: AssemblyVersion("1.31.0.0")]
  36 +[assembly: AssemblyFileVersion("1.31.0.0")]
... ...
Vrh.Log4Pro.MaintenanceConsole/Tools.cs
... ... @@ -20,6 +20,8 @@ using VRH.Common;
20 20 using Microsoft.Win32;
21 21 using System.Reflection;
22 22 using Vrh.Log4Pro.MaintenanceConsole.CommandLineParserNS;
  23 +using System.Runtime.CompilerServices;
  24 +using static Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS.MaintenanceToolsXmlProcessor.XmlStructure.ExternalUtility.Attributes;
23 25  
24 26 namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
25 27 {
... ... @@ -325,39 +327,70 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
325 327 if (list32.Count() > 0) { if (list64.Count() > 0) { os = "64bit"; } else { os = "32bit"; } }
326 328 return os;
327 329 }
328   - public static bool ResolveArguments(string parameterkvpstring, string stringwithparameters, out string resolvedtext, bool interactive = true)
  330 + public class KvpString
329 331 {
330   - var argumentparametersdictionary = parameterkvpstring.Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
331   - .Select(kvp => CreateKVP(kvp))
332   - .Where(kvp => kvp.Key != null)
333   - .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
334   - Dictionary<string, string> resolveddictionary = new Dictionary<string, string>();
335   - resolvedtext = stringwithparameters;
336   - foreach (var kvp in argumentparametersdictionary)
  332 + public KvpString(string parameterkvpstring) { this.kvpstring = parameterkvpstring; BuildDict(force:true);}
  333 + private string kvpstring;
  334 + private Dictionary<string, string> kvpdict = null;
  335 + private Dictionary<string, string> kvpdictresolved = null;
  336 + public string GetValue(string argumentname, string notexistvalue = null)
  337 + {
  338 + BuildDict();
  339 + return this.kvpdictresolved.ContainsKey(argumentname) ? this.kvpdictresolved[argumentname] : notexistvalue;
  340 + }
  341 + private void BuildDict(bool force=false)
  342 + {
  343 + if (this.kvpdict != null && !force) return;
  344 + this.kvpdict = (this.kvpstring).Split(new char[] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries)
  345 + .Select(kvp => CreateKVP(kvp))
  346 + .Where(kvp => kvp.Key != null)
  347 + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
  348 + this.kvpdictresolved = this.kvpdict.ToDictionary(d => d.Key, d => d.Value);
  349 + }
  350 + public KvpString ResolveInteractive(bool disable = false)
  351 + {
  352 + this.kvpdictresolved = new Dictionary<string, string>();
  353 + foreach (var kvp in this.kvpdict)
  354 + {
  355 + if (kvp.Value == null) { this.kvpdictresolved.Add(kvp.Key, ""); }
  356 + else if (kvp.Value.StartsWith("?") && !disable)
  357 + {
  358 + // "?default?prompt"
  359 + string prompt = $"Enter value for {kvp.Key}:";
  360 + string kvpdefaultvalue = null;
  361 + if (kvp.Value.Length > 1)
  362 + {
  363 + var ppp = kvp.Value.Substring(1).Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
  364 + if (!string.IsNullOrWhiteSpace(ppp[0])) { kvpdefaultvalue = ppp[0]; };
  365 + if (kvp.Value.Substring(1).IndexOf('?') != -1) { prompt = ppp[1]; }
  366 + }
  367 + string value = ColorConsole.ReadLine(prompt, ConsoleColor.Yellow, defaultvalue: kvpdefaultvalue);
  368 + if (value.ToUpper() == "EX") { return null; }
  369 + this.kvpdictresolved.Add(kvp.Key, value);
  370 + }
  371 + else if (kvp.Value.StartsWith("?") && disable) this.kvpdictresolved.Add(kvp.Key, $"{{{kvp.Key}}}");
  372 + else { this.kvpdictresolved.Add(kvp.Key, kvp.Value); }
  373 + }
  374 + return this;
  375 + }
  376 + public string Substitute(string stringwithparameters)
337 377 {
338   - if (kvp.Value == null) { resolveddictionary.Add(kvp.Key, "");}
339   - else if (kvp.Value.StartsWith("?") && interactive)
340   - {
341   - // "?default?prompt"
342   - string prompt = $"Enter value for {kvp.Key}:";
343   - string kvpdefaultvalue = null;
344   - if (kvp.Value.Length > 1)
345   - {
346   - var ppp = kvp.Value.Substring(1).Split(new char[] { '?' }, StringSplitOptions.RemoveEmptyEntries);
347   - if (!string.IsNullOrWhiteSpace(ppp[0])) { kvpdefaultvalue = ppp[0]; };
348   - if (kvp.Value.Substring(1).IndexOf('?') != -1) { prompt = ppp[1]; }
349   - }
350   - string value = ColorConsole.ReadLine(prompt, ConsoleColor.Yellow, defaultvalue: kvpdefaultvalue);
351   - if (value.ToUpper() == "EX") { return false; }
352   - resolveddictionary.Add(kvp.Key, value);
353   - }
354   - else if (kvp.Value.StartsWith("?") && !interactive) resolveddictionary.Add(kvp.Key, $"{{{kvp.Key}}}");
355   - else { resolveddictionary.Add(kvp.Key, kvp.Value); }
  378 + return VRH.Common.StringConstructor.ResolveConstructorR(this.kvpdictresolved, stringwithparameters, "{}@@");
  379 + }
  380 + public static string Substitute(string kvpstring, string stringwithparameters)
  381 + {
  382 + return VRH.Common.StringConstructor.ResolveConstructorR(new KvpString(kvpstring).kvpdictresolved, stringwithparameters, "{}@@");
  383 + }
  384 + public static bool Resolve(string kvpstring,string stringwithparameters, out string resolvedtext, bool disableinteractive = false)
  385 + {
  386 + resolvedtext = null;
  387 + var resolvedkvpstring = (new Tools.KvpString(kvpstring)).ResolveInteractive(disableinteractive);
  388 + if (resolvedkvpstring == null) return false;//exit with EX
  389 + resolvedtext = resolvedkvpstring.Substitute(stringwithparameters);
  390 + return true;
356 391 }
357   - resolvedtext = VRH.Common.StringConstructor.ResolveConstructorR(resolveddictionary, stringwithparameters, "{}@@");
358   - return true;
359   - }
360   - private static KeyValuePair<string, string> CreateKVP(string kvpstring)
  392 + }
  393 + private static KeyValuePair<string, string> CreateKVP(string kvpstring)
361 394 {
362 395 string kvpk = null;
363 396 string kvpv = null;
... ... @@ -368,7 +401,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
368 401 KeyValuePair<string, string> r = new KeyValuePair<string, string>(kvpk, kvpv);
369 402 return r;
370 403 }
371   - catch { return new KeyValuePair<string, string>(kvpk, null); }
  404 + catch { return new KeyValuePair<string, string>(kvpk, ""); }
372 405 }
373 406 }
374 407  
... ...
Vrh.Log4Pro.MaintenanceConsole/Vrh.Log4Pro.MaintenanceConsole.csproj
... ... @@ -347,14 +347,17 @@
347 347 <Reference Include="Microsoft.CSharp" />
348 348 <Reference Include="System.Data" />
349 349 <Reference Include="System.Xml" />
350   - <Reference Include="VRH.Common, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
351   - <HintPath>..\packages\VRH.Common.3.0.1\lib\net45\VRH.Common.dll</HintPath>
  350 + <Reference Include="VRH.Common, Version=4.1.1.0, Culture=neutral, processorArchitecture=MSIL">
  351 + <HintPath>..\packages\VRH.Common.4.1.1\lib\net462\VRH.Common.dll</HintPath>
352 352 </Reference>
353   - <Reference Include="VRH.Common.COM, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
354   - <HintPath>..\packages\VRH.Common.3.0.1\lib\net45\VRH.Common.COM.dll</HintPath>
  353 + <Reference Include="VRH.Common.COM, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
  354 + <HintPath>..\packages\VRH.Common.4.1.1\lib\net462\VRH.Common.COM.dll</HintPath>
355 355 </Reference>
356   - <Reference Include="VRH.Common.EF, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
357   - <HintPath>..\packages\VRH.Common.3.0.1\lib\net45\VRH.Common.EF.dll</HintPath>
  356 + <Reference Include="VRH.Common.EF, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
  357 + <HintPath>..\packages\VRH.Common.4.1.1\lib\net462\VRH.Common.EF.dll</HintPath>
  358 + </Reference>
  359 + <Reference Include="VRH.Common.Log4ProIS, Version=4.1.1.0, Culture=neutral, processorArchitecture=MSIL">
  360 + <HintPath>..\packages\VRH.Common.4.1.1\lib\net462\VRH.Common.Log4ProIS.dll</HintPath>
358 361 </Reference>
359 362 <Reference Include="Vrh.Web.Providers, Version=2.0.2.0, Culture=neutral, processorArchitecture=MSIL">
360 363 <HintPath>..\packages\VRH.Web.Providers.2.0.2\lib\net452\Vrh.Web.Providers.dll</HintPath>
... ...
Vrh.Log4Pro.MaintenanceConsole/packages.config
... ... @@ -73,7 +73,7 @@
73 73 <package id="System.Threading.Timer" version="4.0.1" targetFramework="net472" />
74 74 <package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="net472" />
75 75 <package id="System.Xml.XDocument" version="4.0.11" targetFramework="net472" />
76   - <package id="VRH.Common" version="3.0.1" targetFramework="net472" />
  76 + <package id="VRH.Common" version="4.1.1" targetFramework="net472" />
77 77 <package id="VRH.Web.Providers" version="2.0.2" targetFramework="net472" />
78 78 <package id="Vrh.XmlProcessing" version="2.8.0" targetFramework="net472" />
79 79 </packages>
80 80 \ No newline at end of file
... ...