From 1de849ec3751dc579c6bc846f29e864bc24f912b Mon Sep 17 00:00:00 2001 From: NileshGhodekar Date: Wed, 22 Mar 2017 15:53:14 +0000 Subject: [PATCH 1/5] Suppressed debug trace during Import-Module of "WebAdministration". --- src/Scripts/Register.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Scripts/Register.ps1 b/src/Scripts/Register.ps1 index f1d9c2d..00f3c29 100644 --- a/src/Scripts/Register.ps1 +++ b/src/Scripts/Register.ps1 @@ -189,7 +189,7 @@ function UpdateAssemblyBindings { try { - Import-Module "WebAdministration" + Import-Module "WebAdministration" -Debug:$false -Verbose:$false $portalSite = Get-WebSite | Where-Object { $_.Name -eq $PortalSiteName } From fd1ddef2b72aa281fb7ecdd86d0247badd47c79c Mon Sep 17 00:00:00 2001 From: NileshGhodekar Date: Wed, 22 Mar 2017 15:56:09 +0000 Subject: [PATCH 2/5] Trimming any spaces around expressions so as to avoid unnecessary invalid expression errors. --- .../FormController/ActivityTextBox.cs | 2 +- src/WorkflowActivityLibrary/Definitions/Definition.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WorkflowActivityLibrary.UI/FormController/ActivityTextBox.cs b/src/WorkflowActivityLibrary.UI/FormController/ActivityTextBox.cs index 5554614..ef81a94 100644 --- a/src/WorkflowActivityLibrary.UI/FormController/ActivityTextBox.cs +++ b/src/WorkflowActivityLibrary.UI/FormController/ActivityTextBox.cs @@ -74,7 +74,7 @@ public string Value set { - this.textBoxControl.Text = value; + this.textBoxControl.Text = !string.IsNullOrEmpty(value) ? value.Trim() : value; } } diff --git a/src/WorkflowActivityLibrary/Definitions/Definition.cs b/src/WorkflowActivityLibrary/Definitions/Definition.cs index a8f7d54..fc56cb1 100644 --- a/src/WorkflowActivityLibrary/Definitions/Definition.cs +++ b/src/WorkflowActivityLibrary/Definitions/Definition.cs @@ -30,8 +30,8 @@ public class Definition /// if set to true, the right-hand side can be assigned null value. public Definition(string left, string right, bool check) { - this.Left = left; - this.Right = right; + this.Left = !string.IsNullOrEmpty(left) ? left.Trim() : left; + this.Right = !string.IsNullOrEmpty(right) ? right.Trim() : right; this.Check = check; } From 0c832c6692b8b1d168e2b18c29300bd2cc2e93de Mon Sep 17 00:00:00 2001 From: NileshGhodekar Date: Wed, 22 Mar 2017 15:58:03 +0000 Subject: [PATCH 3/5] Minor improvements to the logger class. --- .../Common/ContextItems.cs | 5 +++++ src/WorkflowActivityLibrary/Common/Logger.cs | 20 ++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/WorkflowActivityLibrary/Common/ContextItems.cs b/src/WorkflowActivityLibrary/Common/ContextItems.cs index 69d521e..a9dabf1 100644 --- a/src/WorkflowActivityLibrary/Common/ContextItems.cs +++ b/src/WorkflowActivityLibrary/Common/ContextItems.cs @@ -40,6 +40,11 @@ internal static class ContextItems [SecurityCritical] public static void SetContextItem(object key, object value) { + if (key == null) + { + throw new ArgumentNullException("key"); + } + Hashtable contextItems = (Hashtable)CallContext.GetData(CallContextSlotName) ?? new Hashtable(); contextItems[key] = value; diff --git a/src/WorkflowActivityLibrary/Common/Logger.cs b/src/WorkflowActivityLibrary/Common/Logger.cs index 4bd6b2a..db5fb33 100644 --- a/src/WorkflowActivityLibrary/Common/Logger.cs +++ b/src/WorkflowActivityLibrary/Common/Logger.cs @@ -167,6 +167,7 @@ public static void SetContextItem(Activity activity, Guid workflowInstanceId) /// Value. Objects will be serialized. /// The following example demonstrates use of the AddContextItem method. /// Logger.SetContextItem("SessionID", myComponent.SessionId); + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed. Required.")] [SecurityCritical] public static void SetContextItem(object key, object value) { @@ -175,12 +176,16 @@ public static void SetContextItem(object key, object value) throw new ArgumentNullException("key"); } - if (value == null) + if (value is string && !string.IsNullOrEmpty(value as string)) { - throw new ArgumentNullException("value"); + // remove any curly braces as they cause FormatException in WriteEvent method when args is passed to it. + var stringValue = ((string)value).Replace("{", string.Empty).Replace("}", string.Empty); + ContextItems.SetContextItem(key, stringValue); + } + else + { + ContextItems.SetContextItem(key, value); } - - ContextItems.SetContextItem(key, value); } /// @@ -571,19 +576,20 @@ private void WriteEvent(TraceEventType eventType, int eventId, [Localizable(fals traceSource.TraceEvent(eventType, eventId, message); } } - catch (FormatException) + catch (FormatException e) { + Debug.WriteLine(e); traceSource.TraceEvent(eventType, eventId, message); } catch (Win32Exception e) { // FIM Portal run in WSS_Minimal trust level by default // the quickest way to fix this is to make FIM Portal Web Apppool Identity a local admin - System.Diagnostics.Debug.WriteLine(e); + Debug.WriteLine(e); } catch (Exception e) { - System.Diagnostics.Debug.WriteLine(e); + Debug.WriteLine(e); } } } From 0d8727a37cb17069ff0a328ffa67bd78f172526c Mon Sep 17 00:00:00 2001 From: NileshGhodekar Date: Wed, 22 Mar 2017 16:11:32 +0000 Subject: [PATCH 4/5] Added support for executing queries or stored procedures against external SQL Server or ODBC data sources. --- .gitignore | 1 + ChangeLog.md | 16 + src/Settings.StyleCop | 2 + src/VersionInfo.cs | 4 +- .../Common/EventIdentifier.cs | 115 +++ .../Common/ExpressionFunction.cs | 741 +++++++++++++++++- .../ComponentActivities/DetermineActor.cs | 1 + .../Messages.Designer.cs | 18 + src/WorkflowActivityLibrary/Messages.resx | 6 + 9 files changed, 901 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3ff33e7..90f8277 100644 --- a/.gitignore +++ b/.gitignore @@ -213,3 +213,4 @@ ModelManifest.xml /src/ReferencedAssemblies /src/Scripts /src/SolutionOutput +/src/WAL.snk diff --git a/ChangeLog.md b/ChangeLog.md index 1a13932..d1388b5 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,17 @@ All notable changes to MIMWAL project will be documented in this file. The "Unre * Support for multi-valued attributes in `[//Effective]` lookup in AuthZ workflows. * Implement Approve Request Activity. +### Version 2.17.0322.0 + +#### Added + +* Support for executing SQL stored procedures and queries with the implementation of following new functions: + * New function [CreateSqlParameter][CreateSqlParameterFunction] + * New function [CreateSqlParameter2][CreateSqlParameter2Function] + * New function [ExecuteSqlNonQuery][ExecuteSqlNonQueryFunction] + * New function [ExecuteSqlScalar][ExecuteSqlScalarFunction] + * New function [ValueByKey][ValueByKeyFunction] + ### Version 2.16.1028.0 #### Changed @@ -128,3 +139,8 @@ All notable changes to MIMWAL project will be documented in this file. The "Unre [FormatMultivaluedListFunction]: https://github.com/Microsoft/MIMWAL/wiki/FormatMultivaluedList-Function [ConvertToUniqueIdentifierFunction]: https://github.com/Microsoft/MIMWAL/wiki/ConvertToUniqueIdentifier-Function [ConvertToStringFunction]: https://github.com/Microsoft/MIMWAL/wiki/ConvertToString-Function +[CreateSqlParameterFunction]: https://github.com/Microsoft/MIMWAL/wiki/CreateSqlParameter-Function +[CreateSqlParameter2Function]: https://github.com/Microsoft/MIMWAL/wiki/CreateSqlParameter2-Function +[ExecuteSqlNonQueryFunction]: https://github.com/Microsoft/MIMWAL/wiki/ExecuteSqlNonQuery-Function +[ExecuteSqlScalarFunction]: https://github.com/Microsoft/MIMWAL/wiki/ExecuteSqlScalar-Function +[ValueByKeyFunction]: https://github.com/Microsoft/MIMWAL/wiki/ValueByKey-Function diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop index 9cafdb7..da6ba18 100644 --- a/src/Settings.StyleCop +++ b/src/Settings.StyleCop @@ -15,6 +15,7 @@ hklm lookup Utc + Sql @@ -29,6 +30,7 @@ dn + db diff --git a/src/VersionInfo.cs b/src/VersionInfo.cs index a09d88c..30fc039 100644 --- a/src/VersionInfo.cs +++ b/src/VersionInfo.cs @@ -22,7 +22,7 @@ internal static class VersionInfo /// Build Number (MMDD) /// Revision (if any on the same day) /// - internal const string Version = "2.16.1028.0"; + internal const string Version = "2.17.0322.0"; /// /// File Version information for the assembly consists of the following four values: @@ -31,6 +31,6 @@ internal static class VersionInfo /// Build Number (MMDD) /// Revision (if any on the same day) /// - internal const string FileVersion = "2.16.1028.0"; + internal const string FileVersion = "2.17.0322.0"; } } \ No newline at end of file diff --git a/src/WorkflowActivityLibrary/Common/EventIdentifier.cs b/src/WorkflowActivityLibrary/Common/EventIdentifier.cs index 80cd049..a2e4292 100644 --- a/src/WorkflowActivityLibrary/Common/EventIdentifier.cs +++ b/src/WorkflowActivityLibrary/Common/EventIdentifier.cs @@ -1208,6 +1208,31 @@ public static class EventIdentifier /// public const int ExpressionFunctionFormatMultivaluedList = 11676; + /// + /// The event identifier for ExpressionFunction CreateSqlParameter events + /// + public const int ExpressionFunctionCreateSqlParameter = 11677; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter2 events + /// + public const int ExpressionFunctionCreateSqlParameter2 = 11678; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlScalar events + /// + public const int ExpressionFunctionExecuteSqlScalar = 11679; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlNonQuery events + /// + public const int ExpressionFunctionExecuteSqlNonQuery = 11680; + + /// + /// The event identifier for ExpressionFunction ValueByKey events + /// + public const int ExpressionFunctionValueByKey = 11681; + /// /// The event identifier for LookupEvaluator Constructor events /// @@ -2878,6 +2903,96 @@ public static class EventIdentifier /// public const int ExpressionFunctionFormatMultivaluedListInvalidSecondFunctionParameterTypeError = 41676; + /// + /// The event identifier for ExpressionFunction CreateSqlParameter events + /// + public const int ExpressionFunctionCreateSqlParameterInvalidFunctionParameterCountError = 41677; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter events + /// + public const int ExpressionFunctionCreateSqlParameterInvalidFunctionParameterError = 41677; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter events + /// + public const int ExpressionFunctionCreateSqlParameterNullFunctionParameterError = 41677; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter events + /// + public const int ExpressionFunctionCreateSqlParameterInvalidFunctionParameterTypeError = 41677; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter2 events + /// + public const int ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterCountError = 41678; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter2 events + /// + public const int ExpressionFunctionCreateSqlParameter2NullFunctionParameterError = 41678; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter2 events + /// + public const int ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterError = 41678; + + /// + /// The event identifier for ExpressionFunction CreateSqlParameter2 events + /// + public const int ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError = 41678; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlScalar events + /// + public const int ExpressionFunctionExecuteSqlScalarInvalidFunctionParameterCountError = 41679; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlScalar events + /// + public const int ExpressionFunctionExecuteSqlScalarNullFunctionParameterError = 41679; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlScalar events + /// + public const int ExpressionFunctionExecuteSqlScalarInvalidFunctionParameterTypeError = 41679; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlNonQuery events + /// + public const int ExpressionFunctionExecuteSqlNonQueryInvalidFunctionParameterCountError = 41680; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlNonQuery events + /// + public const int ExpressionFunctionExecuteSqlNonQueryNullFunctionParameterError = 41680; + + /// + /// The event identifier for ExpressionFunction ExecuteSqlNonQuery events + /// + public const int ExpressionFunctionExecuteSqlNonQueryInvalidFunctionParameterTypeError = 41680; + + /// + /// The event identifier for ExpressionFunction ValueByKey events + /// + public const int ExpressionFunctionValueByKeyInvalidFunctionParameterCountError = 41681; + + /// + /// The event identifier for ExpressionFunction ValueByKey events + /// + public const int ExpressionFunctionValueByKeyNullFunctionParameterError = 41681; + + /// + /// The event identifier for ExpressionFunction ValueByKey events + /// + public const int ExpressionFunctionValueByKeyInvalidFirstFunctionParameterTypeError = 41681; + + /// + /// The event identifier for ExpressionFunction ValueByKey events + /// + public const int ExpressionFunctionValueByKeyInvalidSecondFunctionParameterTypeError = 41681; + /// /// The event identifier for LookupEvaluator Constructor events /// diff --git a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs index 7035bf0..8d3da73 100644 --- a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs +++ b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs @@ -15,6 +15,11 @@ namespace MicrosoftServices.IdentityManagement.WorkflowActivityLibrary.Common using System; using System.Collections; using System.Collections.Generic; + using System.Configuration; + using System.Data; + using System.Data.Common; + using System.Data.Odbc; + using System.Data.SqlClient; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; @@ -154,6 +159,12 @@ public object Run() case "COUNT": return this.Count(); + case "CREATESQLPARAMETER": + return this.CreateSqlParameter(); + + case "CREATESQLPARAMETER2": + return this.CreateSqlParameter2(); + case "CRLF": return this.CRLF(); @@ -181,6 +192,12 @@ public object Run() case "ESCAPEDNCOMPONENT": return this.EscapeDNComponent(); + case "EXECUTESQLNONQUERY": + return this.ExecuteSqlNonQuery(); + + case "EXECUTESQLSCALAR": + return this.ExecuteSqlScalar(); + case "FIRST": return this.First(); @@ -307,6 +324,9 @@ public object Run() case "VALUEBYINDEX": return this.ValueByIndex(); + case "VALUEBYKEY": + return this.ValueByKey(); + case "VALUETYPE": return this.ValueType(); @@ -972,6 +992,7 @@ private bool Or() /// /// true if the request parameters contain the specified attribute name. Otherwise false. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed.")] private bool ParametersContain() { @@ -1082,6 +1103,7 @@ private bool ParametersContain() /// /// List of the attribute names in the request parameters. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed.")] private List ParametersList() { @@ -1161,6 +1183,7 @@ private List ParametersList() /// /// The value of the attribute in the request parameters. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed.")] private object ParameterValue() { @@ -1266,6 +1289,7 @@ private object ParameterValue() /// /// The added value of the multi-valued attribute in the request parameters. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed.")] private object ParameterValueAdded() { @@ -1372,6 +1396,7 @@ private object ParameterValueAdded() /// /// The removed value of the multi-valued attribute in the request parameters. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1603:DocumentationMustContainValidXml", Justification = "Reviewed.")] private object ParameterValueRemoved() { @@ -1469,6 +1494,7 @@ private object ParameterValueRemoved() /// /// A table of of attribute name, modification type and modified values in the request parameters. /// + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed. Required.")] private string ParametersTable() { Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionParametersTable, "Evaluation Mode: '{0}'.", this.mode); @@ -1769,7 +1795,7 @@ private object FormatMultivaluedList() { if (this.parameters.Count < 2) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterCountError, this.function, 2, this.parameters.Count)); + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionFormatMultivaluedListInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterMinCountError, this.function, 2, this.parameters.Count)); } object parameter = this.parameters[0]; @@ -3696,6 +3722,77 @@ private object ValueByIndex() } } + /// + /// This function is used to retrieve the value at the specified key in the input Dictionary of string/object key/pair. + /// Function Syntax: ValueByKey(dictionary:[Dictionary], key:string) + /// + /// The object associated with the specified key. If the specified key does not exist, null is returned. + private object ValueByKey() + { + Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionValueByKey, "Evaluation Mode: '{0}'.", this.mode); + + try + { + if (this.parameters.Count != 2) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionValueByKeyInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterCountError, this.function, 2, this.parameters.Count)); + } + + Type parameterType = typeof(string); + object parameter = this.parameters[1]; + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionValueByKeyNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, 2)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionValueByKeyInvalidSecondFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidSecondFunctionParameterTypeError, this.function, parameterType.Name, parameter.GetType().Name)); + } + + object result = null; + + if (this.mode != EvaluationMode.Parse) + { + if (this.parameters[0] == null) + { + result = null; + } + else + { + string key = this.parameters[1] as string; + + parameterType = typeof(Dictionary); + parameter = this.parameters[0]; + if (parameter.GetType() == parameterType) + { + var dictionary = parameter as Dictionary; + if (dictionary != null && dictionary.ContainsKey(key)) + { + result = dictionary[key]; + } + } + else + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionValueByKeyInvalidFirstFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFirstFunctionParameterTypeError, this.function, parameterType.Name, parameter.GetType().Name)); + } + } + + Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionValueByKey, "ValueByKey('{0}', '{1}') returned '{2}'.", this.parameters[0], this.parameters[1], result); + } + else + { + result = null; + } + + return result; + } + finally + { + Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionFunctionValueByKey, "Evaluation Mode: '{0}'.", this.mode); + } + } + /// /// This function is used to determine the type of the supplied input object. /// Function Syntax: ValueType(value:object) @@ -4713,6 +4810,648 @@ private object SortList() } } + /// + /// This function is used to create a object for use in various ExecuteSql functions. + /// Function Syntax: CreateSqlParameter(sqlConnectionStringConfigKey:string, parameterName:string, parameterValue:object [,parameterDirection:string]) + /// + /// The object. + private DbParameter CreateSqlParameter() + { + Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionCreateSqlParameter, "Evaluation Mode: '{0}'.", this.mode); + + try + { + if (this.parameters.Count < 3 || this.parameters.Count > 4) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterMinMaxCountError, this.function, 3, 4, this.parameters.Count)); + } + + for (int i = 0; i < 2; ++i) + { + object parameter = this.parameters[i]; // 0 = SQL Connection String Config Key, 1 = Parameter Name + Type parameterType = typeof(string); + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + + if (this.mode != EvaluationMode.Parse) + { + switch (i) + { + case 0: // SQL Connection String Config Key + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + if (string.IsNullOrEmpty(providerName)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + + try + { + DbProviderFactories.GetFactory(providerName); + } + catch (DbException) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + } + + break; + case 1: // SQL Parameter Name + var parameterName = parameter as string; + if (!parameterName.StartsWith("@", StringComparison.OrdinalIgnoreCase)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterError, this.function, i, "@" + parameterName, parameterName)); + } + + break; + } + } + } + + if (this.parameters.Count == 4) + { + var i = 3; + object parameter = this.parameters[i]; // 3 = Parameter Direction + Type parameterType = typeof(string); + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + + if (this.mode != EvaluationMode.Parse) + { + try + { + Enum.Parse(typeof(ParameterDirection), parameter as string, true); + } + catch (ArgumentException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, e, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + } + + DbParameter result = null; + + if (this.mode != EvaluationMode.Parse) + { + string parameterName = this.parameters[1] as string; + object parameterValue = this.parameters[2]; + var parameterDirection = ParameterDirection.Input; + + if (this.parameters.Count == 4) + { + parameterDirection = (ParameterDirection)Enum.Parse(typeof(ParameterDirection), this.parameters[3] as string, true); + } + + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + if (string.IsNullOrEmpty(providerName)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameterNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + + var factory = DbProviderFactories.GetFactory(providerName); + + result = factory.CreateParameter(); + result.ParameterName = parameterName; + result.Direction = parameterDirection; + result.Value = (parameterValue == null) ? DBNull.Value : parameterValue; + + Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionCreateSqlParameter, "CreateSqlParameter('{0}', '{1}', '{2}', '{3}') returned '{4}'.", this.parameters[0], result.ParameterName, result.Value, result.Direction, result); + } + + return result; + } + finally + { + Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionFunctionCreateSqlParameter, "Evaluation Mode: '{0}'.", this.mode); + } + } + + /// + /// This function is used to create a object for use in various ExecuteSql functions. + /// Function Syntax: CreateSqlParameter2(sqlConnectionStringConfigKey:string, parameterName:string, parameterDirection:string, parameterType:string [, parameterSize:integer, parameterValue:object]) + /// + /// The object. + private DbParameter CreateSqlParameter2() + { + Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionCreateSqlParameter2, "Evaluation Mode: '{0}'.", this.mode); + + try + { + if (this.parameters.Count < 4 || this.parameters.Count > 6) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterMinMaxCountError, this.function, 4, 6, this.parameters.Count)); + } + + for (int i = 0; i < 4; ++i) + { + object parameter = this.parameters[i]; // 0 = SQL Connection String Config Key, 1 = Parameter Name, 2 = Parameter Direction, 3 = Parameter Type + Type parameterType = typeof(string); + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2NullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + + if (this.mode != EvaluationMode.Parse) + { + switch (i) + { + case 0: // SQL Connection String Config Key + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + if (string.IsNullOrEmpty(providerName)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2NullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + + try + { + DbProviderFactories.GetFactory(providerName); + } + catch (DbException) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2NullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + } + + break; + case 1: // SQL Parameter Name + var parameterName = parameter as string; + if (!parameterName.StartsWith("@", StringComparison.OrdinalIgnoreCase)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterError, this.function, i, "@" + parameterName, parameterName)); + } + + break; + case 2: // SQL Parameter Direction + { + try + { + Enum.Parse(typeof(ParameterDirection), parameter as string, true); + } + catch (ArgumentException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, e, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + + break; + case 3: // SQL Parameter Type + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + var factory = DbProviderFactories.GetFactory(providerName); + + var dbParameter = factory.CreateParameter(); + if (dbParameter is SqlParameter) + { + try + { + Enum.Parse(typeof(SqlDbType), parameter as string, true); + } + catch (ArgumentException) + { + try + { + Enum.Parse(typeof(DbType), parameter as string, true); + } + catch (ArgumentException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError2, e, this.function, i + 1, typeof(SqlDbType), typeof(DbType), parameter.GetType().Name)); + } + } + } + else if (dbParameter is OdbcParameter) + { + try + { + Enum.Parse(typeof(OdbcType), parameter as string, true); + } + catch (ArgumentException) + { + try + { + Enum.Parse(typeof(DbType), parameter as string, true); + } + catch (ArgumentException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError2, e, this.function, i + 1, typeof(OdbcType), typeof(DbType), parameter.GetType().Name)); + } + } + } + else + { + try + { + Enum.Parse(typeof(DbType), parameter as string, true); + } + catch (ArgumentException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, e, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + } + + break; + } + } + } + + if (this.parameters.Count > 4) + { + var i = 4; + object parameter = this.parameters[i]; // SQL Parameter Size + Type parameterType = typeof(int); + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2NullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionCreateSqlParameter2InvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + + DbParameter result = null; + + if (this.mode != EvaluationMode.Parse) + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + var factory = DbProviderFactories.GetFactory(providerName); + + var parameterName = this.parameters[1] as string; + var parameterDirection = (ParameterDirection)Enum.Parse(typeof(ParameterDirection), this.parameters[2] as string, true); + var parameterDbType = this.parameters[3] as string; + + var parameterSize = 0; + if (this.parameters.Count > 4) + { + parameterSize = Convert.ToInt32(this.parameters[4], CultureInfo.InvariantCulture); // SQL Parameter Size + } + + object parameterValue = null; + if (this.parameters.Count > 5) + { + parameterValue = this.parameters[5]; // SQL Parameter Value + } + + result = factory.CreateParameter(); + result.ParameterName = parameterName; + result.Direction = parameterDirection; + + if (result is SqlParameter) + { + try + { + ((SqlParameter)result).SqlDbType = (SqlDbType)Enum.Parse(typeof(SqlDbType), parameterDbType, true); + } + catch (ArgumentException) + { + result.DbType = (DbType)Enum.Parse(typeof(DbType), parameterDbType, true); + } + } + else if (result is OdbcParameter) + { + try + { + ((OdbcParameter)result).OdbcType = (OdbcType)Enum.Parse(typeof(OdbcType), parameterDbType, true); + } + catch (ArgumentException) + { + result.DbType = (DbType)Enum.Parse(typeof(DbType), parameterDbType, true); + } + } + else + { + result.DbType = (DbType)Enum.Parse(typeof(DbType), parameterDbType, true); + } + + if (parameterSize != 0) + { + result.Size = parameterSize; + } + + result.Value = (parameterValue == null) ? DBNull.Value : parameterValue; + + Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionCreateSqlParameter2, "CreateSqlParameter2('{0}', '{1}', '{2}', '{3}', '{4}', '{5}') returned '{6}'.", connectionStringConfigKey, result.ParameterName, result.Direction, result.DbType, result.Size, result.Value, result); + } + + return result; + } + finally + { + Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionFunctionCreateSqlParameter2, "Evaluation Mode: '{0}'.", this.mode); + } + } + + /// + /// This function is used to execute a SQL stored procedure or query against a SQL database. + /// Function Syntax: ExecuteSqlScalar(sqlConnectionStringConfigKey:string, sqlStatement:string [, parameter1:SqlParameter, parameter2:SqlParameter, ...]) + /// + /// The first column of the first row in the result set. + [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The query does not contain end-user input as well as it's parameterised.")] + private object ExecuteSqlScalar() + { + Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionExecuteSqlScalar, "Evaluation Mode: '{0}'.", this.mode); + + try + { + if (this.parameters.Count < 2) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterMinCountError, this.function, 2, this.parameters.Count)); + } + + for (int i = 0; i < 2; ++i) + { + object parameter = this.parameters[i]; + Type parameterType = typeof(string); + + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + + for (int i = 2; i < this.parameters.Count; ++i) + { + object parameter = this.parameters[i]; + Type parameterType = typeof(DbParameter); + Type parameterType2 = typeof(SqlParameter); + Type parameterType3 = typeof(OdbcParameter); + + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2) && !this.VerifyType(parameter, parameterType3)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError3, this.function, i + 1, parameterType.Name, parameterType2.Name, parameterType3.Name, parameter.GetType().Name)); + } + } + + object result = null; + + if (this.mode != EvaluationMode.Parse) + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var connectionString = connectionStringConfig != null ? connectionStringConfig.ConnectionString : null; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + + if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(providerName)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + + var commandTimeout = 30; + commandTimeout = int.TryParse(ConfigurationManager.AppSettings["WAL_DBCommandTimeout"], out commandTimeout) ? commandTimeout : 30; + + try + { + var factory = DbProviderFactories.GetFactory(providerName); + using (var dbConnection = factory.CreateConnection()) + { + dbConnection.ConnectionString = connectionString; + using (var dbCommand = factory.CreateCommand()) + { + dbCommand.Connection = dbConnection; + var dbCmdText = (this.parameters[1] as string).Trim(); + dbCommand.CommandText = dbCmdText; + + // If the command text has spaces inbetween, assume it's a query, else stored procedure as we won't support anything else. + dbCommand.CommandType = dbCmdText.Contains(" ") ? CommandType.Text : CommandType.StoredProcedure; + dbCommand.CommandTimeout = commandTimeout; + + string paramString = string.Empty; + for (var i = 2; i < this.parameters.Count; ++i) + { + var parameter = this.parameters[i] as DbParameter; + dbCommand.Parameters.Add(parameter); + paramString += parameter.ParameterName + "='" + parameter.Value + "',"; + } + + paramString = paramString.TrimEnd(','); + + if (providerName.Equals("System.Data.Odbc", StringComparison.OrdinalIgnoreCase) && dbCommand.CommandType == CommandType.StoredProcedure) + { + var paramCount = dbCommand.Parameters.Count; + var paramPlaceholder = string.Empty; + for (var i = 0; i < paramCount; ++i) + { + paramPlaceholder += string.IsNullOrEmpty(paramPlaceholder) ? "?" : ",?"; + } + + if (!string.IsNullOrEmpty(paramPlaceholder)) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "{0} CALL {1}({2}) {3}", "{", dbCmdText, paramPlaceholder, "}"); + } + } + + dbConnection.Open(); + result = dbCommand.ExecuteScalar(); + + if (result == DBNull.Value) + { + result = null; + } + + Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionExecuteSqlScalar, "ExecuteSqlScalar('{0}', '{1}, '{2}') returned '{3}'.", connectionStringConfigKey, dbCommand.CommandText, paramString, result); + } + } + } + catch (DbException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlScalarNullFunctionParameterError, e); + } + } + + return result; + } + finally + { + Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionFunctionExecuteSqlScalar, "Evaluation Mode: '{0}'.", this.mode); + } + } + + /// + /// This function is used to execute a SQL stored procedure or insert, update, delete statements against a SQL database. + /// Function Syntax: ExecuteSqlNonQuery(sqlConnectionStringConfigKey:string, sqlStatement:string [, parameter1:SqlParameter, parameter2:SqlParameter, ...]) + /// + /// The dictionary of output parameter names and their values. + [SuppressMessage("Microsoft.Security", "CA2100:Review SQL queries for security vulnerabilities", Justification = "The query does not contain end-user input as well as it's parameterised.")] + private Dictionary ExecuteSqlNonQuery() + { + Logger.Instance.WriteMethodEntry(EventIdentifier.ExpressionFunctionExecuteSqlNonQuery, "Evaluation Mode: '{0}'.", this.mode); + + try + { + if (this.parameters.Count < 2) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryInvalidFunctionParameterCountError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterMinCountError, this.function, 2, this.parameters.Count)); + } + + for (int i = 0; i < 2; ++i) + { + object parameter = this.parameters[i]; + Type parameterType = typeof(string); + + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError, this.function, i + 1, parameterType.Name, parameter.GetType().Name)); + } + } + + for (int i = 2; i < this.parameters.Count; ++i) + { + object parameter = this.parameters[i]; + Type parameterType = typeof(DbParameter); + Type parameterType2 = typeof(SqlParameter); + Type parameterType3 = typeof(OdbcParameter); + + if (parameter == null) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_NullFunctionParameterError, this.function, i + 1)); + } + + if (!this.VerifyType(parameter, parameterType) && !this.VerifyType(parameter, parameterType2) && !this.VerifyType(parameter, parameterType3)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryInvalidFunctionParameterTypeError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidFunctionParameterTypeError3, this.function, i + 1, parameterType.Name, parameterType2.Name, parameterType3.Name, parameter.GetType().Name)); + } + } + + Dictionary result = null; + + if (this.mode != EvaluationMode.Parse) + { + var connectionStringConfigKey = this.parameters[0] as string; + var connectionStringConfig = ConfigurationManager.ConnectionStrings[connectionStringConfigKey]; + var connectionString = connectionStringConfig != null ? connectionStringConfig.ConnectionString : null; + var providerName = connectionStringConfig != null ? connectionStringConfig.ProviderName : null; + + if (string.IsNullOrEmpty(connectionString) || string.IsNullOrEmpty(providerName)) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryNullFunctionParameterError, new InvalidFunctionFormatException(Messages.ExpressionFunction_InvalidConfigKeyConfiguration, this.function, connectionStringConfigKey)); + } + + var commandTimeout = 30; + commandTimeout = int.TryParse(ConfigurationManager.AppSettings["WAL_DBCommandTimeout"], out commandTimeout) ? commandTimeout : 30; + + try + { + var factory = DbProviderFactories.GetFactory(providerName); + using (var dbConnection = factory.CreateConnection()) + { + dbConnection.ConnectionString = connectionString; + using (var dbCommand = factory.CreateCommand()) + { + dbCommand.Connection = dbConnection; + var dbCmdText = (this.parameters[1] as string).Trim(); + dbCommand.CommandText = dbCmdText; + + // If the command text has spaces inbetween, assume it's a query, else stored procedure as we won't support anything else. + dbCommand.CommandType = dbCmdText.Contains(" ") ? CommandType.Text : CommandType.StoredProcedure; + dbCommand.CommandTimeout = commandTimeout; + + string paramString = string.Empty; + for (var i = 2; i < this.parameters.Count; ++i) + { + var parameter = this.parameters[i] as DbParameter; + if (parameter != null) + { + dbCommand.Parameters.Add(parameter); + paramString += parameter.ParameterName + "='" + parameter.Value + "',"; + } + } + + paramString = paramString.TrimEnd(','); + + if (providerName.Equals("System.Data.Odbc", StringComparison.OrdinalIgnoreCase) && dbCommand.CommandType == CommandType.StoredProcedure) + { + var paramCount = dbCommand.Parameters.Count; + var paramPlaceholder = string.Empty; + for (var i = 0; i < paramCount; ++i) + { + paramPlaceholder += string.IsNullOrEmpty(paramPlaceholder) ? "?" : ",?"; + } + + if (!string.IsNullOrEmpty(paramPlaceholder)) + { + dbCommand.CommandText = string.Format(CultureInfo.InvariantCulture, "{0} CALL {1}({2}) {3}", "{", dbCmdText, paramPlaceholder, "}"); + } + } + + dbConnection.Open(); + var rowsAffected = dbCommand.ExecuteNonQuery(); + + result = new Dictionary(); + result.Add("@RowsAffected", rowsAffected); + foreach (DbParameter param in dbCommand.Parameters) + { + if (param.Direction != ParameterDirection.Input) + { + result.Add(param.ParameterName, param.Value); + } + } + + string paramOutString = string.Join(",", result.Select(kvp => string.Format(CultureInfo.InvariantCulture, "{0}='{1}'", kvp.Key, kvp.Value)).ToArray()); + + Logger.Instance.WriteVerbose(EventIdentifier.ExpressionFunctionExecuteSqlNonQuery, "ExecuteSqlNonQuery('{0}', '{1}, '{2}') returned a dictionary of '{3}' parameter/value pairs.", connectionStringConfigKey, dbCommand.CommandText, paramString, paramOutString); + } + } + } + catch (DbException e) + { + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionExecuteSqlNonQueryNullFunctionParameterError, e); + } + } + + return result; + } + finally + { + Logger.Instance.WriteMethodExit(EventIdentifier.ExpressionFunctionExecuteSqlNonQuery, "Evaluation Mode: '{0}'.", this.mode); + } + } + #endregion #endregion diff --git a/src/WorkflowActivityLibrary/ComponentActivities/DetermineActor.cs b/src/WorkflowActivityLibrary/ComponentActivities/DetermineActor.cs index 0e5ad3a..0943031 100644 --- a/src/WorkflowActivityLibrary/ComponentActivities/DetermineActor.cs +++ b/src/WorkflowActivityLibrary/ComponentActivities/DetermineActor.cs @@ -316,6 +316,7 @@ private void PrepareAccountActor_ExecuteCode(object sender, EventArgs e) /// /// The source of the event. /// The instance containing the event data. + [SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily", Justification = "Reviewed.")] private void Decide_ExecuteCode(object sender, EventArgs e) { Logger.Instance.WriteMethodEntry(EventIdentifier.DetermineActorDecideExecuteCode, "ActorType: '{0}'.", this.ActorType); diff --git a/src/WorkflowActivityLibrary/Messages.Designer.cs b/src/WorkflowActivityLibrary/Messages.Designer.cs index 954c80b..4308a36 100644 --- a/src/WorkflowActivityLibrary/Messages.Designer.cs +++ b/src/WorkflowActivityLibrary/Messages.Designer.cs @@ -294,6 +294,15 @@ internal static string ExpressionFunction_FunctionSyntaxValidationError { } } + /// + /// Looks up a localized string similar to The function '{0}' is configured with an invalid or missing config key '{1}'. . + /// + internal static string ExpressionFunction_InvalidConfigKeyConfiguration { + get { + return ResourceManager.GetString("ExpressionFunction_InvalidConfigKeyConfiguration", resourceCulture); + } + } + /// /// Looks up a localized string similar to The function '{0}' expects first parameter to be of type '{1}'. It was supplied a '{2}' parameter.. /// @@ -393,6 +402,15 @@ internal static string ExpressionFunction_InvalidFunctionParameterTypeError2 { } } + /// + /// Looks up a localized string similar to The function '{0}' expects parameter at position {1} to be of type '{2}' or '{3}' or '{4}'. It was supplied a '{5}' parameter.. + /// + internal static string ExpressionFunction_InvalidFunctionParameterTypeError3 { + get { + return ResourceManager.GetString("ExpressionFunction_InvalidFunctionParameterTypeError3", resourceCulture); + } + } + /// /// Looks up a localized string similar to The request is for an update of the singled-valued attribute '{0}'. Functions ParameterValue must be used instead to read the updated value.. /// diff --git a/src/WorkflowActivityLibrary/Messages.resx b/src/WorkflowActivityLibrary/Messages.resx index 7e51f7a..febb553 100644 --- a/src/WorkflowActivityLibrary/Messages.resx +++ b/src/WorkflowActivityLibrary/Messages.resx @@ -378,4 +378,10 @@ The function '{0}' requires the at least one parameter at position '{1}' or later to be not null. + + The function '{0}' is configured with an invalid or missing config key '{1}'. + + + The function '{0}' expects parameter at position {1} to be of type '{2}' or '{3}' or '{4}'. It was supplied a '{5}' parameter. + \ No newline at end of file From e41907866441f27cad715c225b5e72baa64b674d Mon Sep 17 00:00:00 2001 From: NileshGhodekar Date: Fri, 14 Apr 2017 10:32:54 +0100 Subject: [PATCH 5/5] Instrumentation - Dumping function parameter values when a function throws an unhandled exception. --- ChangeLog.md | 4 +- src/VersionInfo.cs | 4 +- .../Common/ExpressionFunction.cs | 40 ++++++++++++++++++- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index d1388b5..01897e9 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,11 +7,11 @@ All notable changes to MIMWAL project will be documented in this file. The "Unre * Support for multi-valued attributes in `[//Effective]` lookup in AuthZ workflows. * Implement Approve Request Activity. -### Version 2.17.0322.0 +### Version 2.17.0414.0 #### Added -* Support for executing SQL stored procedures and queries with the implementation of following new functions: +* Support for executing SQL stored procedures and queries for SQL Server and ODBC Data Sources with the implementation of following new functions: * New function [CreateSqlParameter][CreateSqlParameterFunction] * New function [CreateSqlParameter2][CreateSqlParameter2Function] * New function [ExecuteSqlNonQuery][ExecuteSqlNonQueryFunction] diff --git a/src/VersionInfo.cs b/src/VersionInfo.cs index 30fc039..6957d1b 100644 --- a/src/VersionInfo.cs +++ b/src/VersionInfo.cs @@ -22,7 +22,7 @@ internal static class VersionInfo /// Build Number (MMDD) /// Revision (if any on the same day) /// - internal const string Version = "2.17.0322.0"; + internal const string Version = "2.17.0414.0"; /// /// File Version information for the assembly consists of the following four values: @@ -31,6 +31,6 @@ internal static class VersionInfo /// Build Number (MMDD) /// Revision (if any on the same day) /// - internal const string FileVersion = "2.17.0322.0"; + internal const string FileVersion = "2.17.0414.0"; } } \ No newline at end of file diff --git a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs index 8d3da73..e107a8e 100644 --- a/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs +++ b/src/WorkflowActivityLibrary/Common/ExpressionFunction.cs @@ -350,7 +350,45 @@ public object Run() } catch (Exception ex) { - throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionRunUnknownFunctionExecutionError, new InvalidFunctionOperationException(Messages.ExpressionFunction_UnknownFunctionExecutionError, this.mode.ToString().ToLowerInvariant(), ex, this.function, ex.Message)); + // dump all function parameters in an error log entry + string paramString = string.Empty; + if (this.mode != EvaluationMode.Parse) + { + try + { + if (this.parameters != null && this.parameters.Count > 0) + { + for (var i = 0; i < this.parameters.Count; ++i) + { + var parameter = this.parameters[i]; + paramString += "'"; + if (parameter != null) + { + try + { + paramString += Convert.ToString(parameter) + "',"; + } + catch (Exception) + { + // let it print without the ending single quote as an indication of the conversion failure + } + } + else + { + paramString += "',"; + } + } + } + + paramString = "(" + paramString.TrimEnd(',') + ")"; + } + catch (Exception) + { + // do nothing - silenty ignore + } + } + + throw Logger.Instance.ReportError(EventIdentifier.ExpressionFunctionRunUnknownFunctionExecutionError, new InvalidFunctionOperationException(Messages.ExpressionFunction_UnknownFunctionExecutionError, this.mode.ToString().ToLowerInvariant(), ex, this.function + paramString, ex.Message)); } finally {