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,6 +10,12 @@ using System.Diagnostics;
10 10
11 using Vrh.XmlProcessing; 11 using Vrh.XmlProcessing;
12 using System.Xml.Linq; 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 namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS 20 namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS
15 { 21 {
@@ -68,17 +74,35 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS @@ -68,17 +74,35 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ColorConsoleNS
68 /// Visszaadja a lenyomott billentyűt, ha van, commandmode-ban pedig az enter-t 74 /// Visszaadja a lenyomott billentyűt, ha van, commandmode-ban pedig az enter-t
69 /// </summary> 75 /// </summary>
70 /// <returns></returns> 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 switch (ck) 107 switch (ck)
84 { 108 {
Vrh.Log4Pro.MaintenanceConsole/Manager - MaintenanceToolManager.cs
@@ -65,7 +65,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS @@ -65,7 +65,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS
65 ExternalProcess.StartInfo.FileName = config.Exe; 65 ExternalProcess.StartInfo.FileName = config.Exe;
66 ExternalProcess.StartInfo.WindowStyle = config.ProcessWindowsStyle; 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 ExternalProcess.StartInfo.Arguments = resolvedtext; 69 ExternalProcess.StartInfo.Arguments = resolvedtext;
70 ExternalProcess.Start(); 70 ExternalProcess.Start();
71 int waitingtime = config.WaitForExit ? -1 : 0; 71 int waitingtime = config.WaitForExit ? -1 : 0;
@@ -633,8 +633,8 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS @@ -633,8 +633,8 @@ namespace Vrh.Log4Pro.MaintenanceConsole.MaintenanceToolManagerNS
633 Exe = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Exe), x, ""); 633 Exe = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Exe), x, "");
634 this.Key = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Key), x, ""); 634 this.Key = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Key), x, "");
635 this.Description = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Description), x, Exe); 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 Arguments = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Arguments), x, XmlStructure.ExternalUtility.Attributes.Arguments.Values.DEFAULT); 638 Arguments = GetValue(nameof(XmlStructure.ExternalUtility.Attributes.Arguments), x, XmlStructure.ExternalUtility.Attributes.Arguments.Values.DEFAULT);
639 639
640 ProcessWindowsStyle = GetValue<ProcessWindowStyle>(nameof(XmlStructure.ExternalUtility.Attributes.WindowStyle), x, XmlStructure.ExternalUtility.Attributes.WindowStyle.Values.DEFAULT); 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,6 +26,12 @@ using System.Text.RegularExpressions;
26 using Microsoft.SqlServer.Management.Common; 26 using Microsoft.SqlServer.Management.Common;
27 using Microsoft.SqlServer.Management.Smo; 27 using Microsoft.SqlServer.Management.Smo;
28 using Microsoft.Data.SqlClient; 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 namespace Vrh.Log4Pro.MaintenanceConsole.SQLDataBaseManagerNS 36 namespace Vrh.Log4Pro.MaintenanceConsole.SQLDataBaseManagerNS
31 { 37 {
@@ -926,27 +932,30 @@ GO @@ -926,27 +932,30 @@ GO
926 else { return null; } 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 catch (ApplicationException e) { ColorConsole.WriteLine("FATAL ERROR! in script parameter substitution!", ConsoleColor.Red); returntext = null; } 960 catch (ApplicationException e) { ColorConsole.WriteLine("FATAL ERROR! in script parameter substitution!", ConsoleColor.Red); returntext = null; }
952 catch (Exception e) { ColorConsole.WriteLine("FATAL ERROR! " + e.MessageNested(), ConsoleColor.Red); returntext = null; } 961 catch (Exception e) { ColorConsole.WriteLine("FATAL ERROR! " + e.MessageNested(), ConsoleColor.Red); returntext = null; }
@@ -985,29 +994,49 @@ GO @@ -985,29 +994,49 @@ GO
985 foreach (var s in sqld.Xml_SQLScriptList) 994 foreach (var s in sqld.Xml_SQLScriptList)
986 { 995 {
987 ColorConsole.Write(s.Key, ConsoleColor.Yellow, bracket: "[]",suffix:" ",prefix: " "); 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 vlist.Add(s.Key); 1003 vlist.Add(s.Key);
992 } 1004 }
993 var scriptkey = ColorConsole.ReadLine("Select the script! [empty]=next DB, [*]=all, [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: vlist); 1005 var scriptkey = ColorConsole.ReadLine("Select the script! [empty]=next DB, [*]=all, [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: vlist);
994 if (string.IsNullOrWhiteSpace(scriptkey)) { continue; } 1006 if (string.IsNullOrWhiteSpace(scriptkey)) { continue; }
995 if (scriptkey.ToUpper() == "EX") { return o; } 1007 if (scriptkey.ToUpper() == "EX") { return o; }
996 SQLDataBase.SQLScript ss = sqld.Xml_SQLScriptList.FirstOrDefault(s=>s.Key==scriptkey); 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 else 1010 else
1003 { 1011 {
1004 - ColorConsole.WriteLine(ssScriptText);  
1005 var confirm = ColorConsole.ReadLine("Enter CONFIRM to start! [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: new List<string>() {"CONFIRM"}); 1012 var confirm = ColorConsole.ReadLine("Enter CONFIRM to start! [EX]=exit.", ConsoleColor.Yellow, prefix:" ", suffix: " --> ", validitylist: new List<string>() {"CONFIRM"});
1006 if (confirm == "CONFIRM") 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 else {ColorConsole.WriteLine($"Script was NOT executed!", ConsoleColor.Red);} 1040 else {ColorConsole.WriteLine($"Script was NOT executed!", ConsoleColor.Red);}
1012 } 1041 }
1013 } 1042 }
@@ -1016,6 +1045,16 @@ GO @@ -1016,6 +1045,16 @@ GO
1016 } 1045 }
1017 return o; 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 private static object DropDB(object parameter, object o) 1058 private static object DropDB(object parameter, object o)
1020 { 1059 {
1021 var config = (parameter as Menu.ExecutorParameter).GetConfig<SQLDataBaseManagerXmlProcessor>(); 1060 var config = (parameter as Menu.ExecutorParameter).GetConfig<SQLDataBaseManagerXmlProcessor>();
@@ -1893,32 +1932,218 @@ GO @@ -1893,32 +1932,218 @@ GO
1893 { 1932 {
1894 DBKEY,DATABASE,DATASOURCE,DBOTYPE,DBONAME,DBDATAGROUP,BACKUPTS,SHRINKOPTION,SHRINKFREESPACEPERCENT, 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 sqltxt = VRH.Common.StringConstructor.ResolveConstructorR(vars, sqltxt, "{}@@"); 2148 sqltxt = VRH.Common.StringConstructor.ResolveConstructorR(vars, sqltxt, "{}@@");
1924 2149
@@ -2607,6 +2832,7 @@ GO @@ -2607,6 +2832,7 @@ GO
2607 public static class Description { public static class Values { public const string DEFAULT = ""; } } 2832 public static class Description { public static class Values { public const string DEFAULT = ""; } }
2608 public static class ScriptCommandTimeout { public static class Values { public const int DEFAULT = 10000; } } 2833 public static class ScriptCommandTimeout { public static class Values { public const int DEFAULT = 10000; } }
2609 public static class Parameters { public static class Values { public const string DEFAULT = ""; } } 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,6 +2849,7 @@ GO
2623 public string ScriptText = ""; 2849 public string ScriptText = "";
2624 public int CommandTimeout = 10000; 2850 public int CommandTimeout = 10000;
2625 public string ArgumentParameters; 2851 public string ArgumentParameters;
  2852 + public bool Multirun = false;
2626 2853
2627 public SQLScript() { } 2854 public SQLScript() { }
2628 public SQLScript(SQLScript sqls) { Name = sqls.Name; Description = sqls.Description; ScriptText= sqls.ScriptText; } 2855 public SQLScript(SQLScript sqls) { Name = sqls.Name; Description = sqls.Description; ScriptText= sqls.ScriptText; }
@@ -2634,8 +2861,10 @@ GO @@ -2634,8 +2861,10 @@ GO
2634 Name = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name.Values.DEFAULT); 2861 Name = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Name.Values.DEFAULT);
2635 FilePath = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.File), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.File.Values.DEFAULT); 2862 FilePath = GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.File), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.File.Values.DEFAULT);
2636 Description= GetValue(nameof(XmlStructure.SQLDataBase.Scripts.Script.Attributes.Description), sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Attributes.Description.Values.DEFAULT); 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 if (string.IsNullOrWhiteSpace(this.FilePath)) 2868 if (string.IsNullOrWhiteSpace(this.FilePath))
2640 { 2869 {
2641 ScriptText = GetValue(sqlscriptXml, XmlStructure.SQLDataBase.Scripts.Script.Values.DEFAULT); 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,5 +32,5 @@ using System.Runtime.InteropServices;
32 // You can specify all the values or you can default the Build and Revision Numbers 32 // You can specify all the values or you can default the Build and Revision Numbers
33 // by using the '*' as shown below: 33 // by using the '*' as shown below:
34 // [assembly: AssemblyVersion("1.0.*")] 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,6 +20,8 @@ using VRH.Common;
20 using Microsoft.Win32; 20 using Microsoft.Win32;
21 using System.Reflection; 21 using System.Reflection;
22 using Vrh.Log4Pro.MaintenanceConsole.CommandLineParserNS; 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 namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS 26 namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
25 { 27 {
@@ -325,39 +327,70 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS @@ -325,39 +327,70 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
325 if (list32.Count() > 0) { if (list64.Count() > 0) { os = "64bit"; } else { os = "32bit"; } } 327 if (list32.Count() > 0) { if (list64.Count() > 0) { os = "64bit"; } else { os = "32bit"; } }
326 return os; 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 string kvpk = null; 395 string kvpk = null;
363 string kvpv = null; 396 string kvpv = null;
@@ -368,7 +401,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS @@ -368,7 +401,7 @@ namespace Vrh.Log4Pro.MaintenanceConsole.ToolsNS
368 KeyValuePair<string, string> r = new KeyValuePair<string, string>(kvpk, kvpv); 401 KeyValuePair<string, string> r = new KeyValuePair<string, string>(kvpk, kvpv);
369 return r; 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,14 +347,17 @@
347 <Reference Include="Microsoft.CSharp" /> 347 <Reference Include="Microsoft.CSharp" />
348 <Reference Include="System.Data" /> 348 <Reference Include="System.Data" />
349 <Reference Include="System.Xml" /> 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 </Reference> 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 </Reference> 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 </Reference> 361 </Reference>
359 <Reference Include="Vrh.Web.Providers, Version=2.0.2.0, Culture=neutral, processorArchitecture=MSIL"> 362 <Reference Include="Vrh.Web.Providers, Version=2.0.2.0, Culture=neutral, processorArchitecture=MSIL">
360 <HintPath>..\packages\VRH.Web.Providers.2.0.2\lib\net452\Vrh.Web.Providers.dll</HintPath> 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,7 +73,7 @@
73 <package id="System.Threading.Timer" version="4.0.1" targetFramework="net472" /> 73 <package id="System.Threading.Timer" version="4.0.1" targetFramework="net472" />
74 <package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="net472" /> 74 <package id="System.Xml.ReaderWriter" version="4.0.11" targetFramework="net472" />
75 <package id="System.Xml.XDocument" version="4.0.11" targetFramework="net472" /> 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 <package id="VRH.Web.Providers" version="2.0.2" targetFramework="net472" /> 77 <package id="VRH.Web.Providers" version="2.0.2" targetFramework="net472" />
78 <package id="Vrh.XmlProcessing" version="2.8.0" targetFramework="net472" /> 78 <package id="Vrh.XmlProcessing" version="2.8.0" targetFramework="net472" />
79 </packages> 79 </packages>
80 \ No newline at end of file 80 \ No newline at end of file