diff --git a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs index 56f5da655..82b3a5cce 100644 --- a/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs +++ b/dotnet/src/dotnetframework/GxClasses/Core/GXApplication.cs @@ -3262,7 +3262,7 @@ public void ExecuteBeforeCommit(string callerName) { inBeforeCommit = true; if (beforeCommitObj != null) - ClassLoader.ExecuteVoidRef(beforeCommitObj, "execute", new Object[] { callerName }); + ClassLoader.ExecuteVoidRef(beforeCommitObj, "execute", new Object[] { callerName }, "Before Commit"); inBeforeCommit = false; } } @@ -3273,7 +3273,7 @@ public void ExecuteAfterCommit(string callerName) { inAfterCommit = true; if (afterCommitObj != null) - ClassLoader.ExecuteVoidRef(afterCommitObj, "execute", new Object[] { callerName }); + ClassLoader.ExecuteVoidRef(afterCommitObj, "execute", new Object[] { callerName },"After Commit"); inAfterCommit = false; } } @@ -3284,7 +3284,7 @@ public void ExecuteBeforeRollback(string callerName) { inBeforeRollback = true; if (beforeRollbackObj != null) - ClassLoader.ExecuteVoidRef(beforeRollbackObj, "execute", new Object[] { callerName }); + ClassLoader.ExecuteVoidRef(beforeRollbackObj, "execute", new Object[] { callerName }, "Before Rollback"); inBeforeRollback = false; } } @@ -3295,7 +3295,7 @@ public void ExecuteAfterRollback(string callerName) { inAfterRollback = true; if (afterRollbackObj != null) - ClassLoader.ExecuteVoidRef(afterRollbackObj, "execute", new Object[] { callerName }); + ClassLoader.ExecuteVoidRef(afterRollbackObj, "execute", new Object[] { callerName }, "After Rollback"); inAfterRollback = false; } } @@ -3305,7 +3305,7 @@ public bool ExecuteBeforeConnect(IGxDataStore datastore) if (beforeConnectObj != null) { GXLogging.Debug(Logger, "ExecuteBeforeConnect"); - ClassLoader.ExecuteVoidRef(beforeConnectObj, "execute", new Object[] { datastore }); + ClassLoader.ExecuteVoidRef(beforeConnectObj, "execute", new Object[] { datastore }, "Before Connect"); return true; } else @@ -3319,7 +3319,7 @@ public bool ExecuteAfterConnect(String datastoreName) if (afterConnectObj != null) { GXLogging.Debug(Logger, "ExecuteAfterConnect"); - ClassLoader.ExecuteVoidRef(afterConnectObj, "execute", new Object[] { datastoreName }); + ClassLoader.ExecuteVoidRef(afterConnectObj, "execute", new Object[] { datastoreName }, "After Connect"); return true; } else diff --git a/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs b/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs index b3b76cb43..e50e50906 100644 --- a/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs +++ b/dotnet/src/dotnetframework/GxClasses/Helpers/GXMetadata.cs @@ -14,6 +14,7 @@ namespace GeneXus.Metadata #endif using GeneXus.Application; using System.Collections.Concurrent; + using System.Text; public class ClassLoader { @@ -233,8 +234,11 @@ internal static string ConstructorArgsString(Object[] constructorArgs) } return argsConstr; } - static public void ExecuteVoidRef(object o, string mthd, Object[] args) + { + ExecuteVoidRef(o, mthd, args, null); + } + internal static void ExecuteVoidRef(object o, string mthd, Object[] args, string propertyName) { try { @@ -269,7 +273,10 @@ static public void ExecuteVoidRef(object o, string mthd, Object[] args) pm[0] = ((pi.Attributes & ParameterAttributes.In) != ParameterAttributes.None); pm[1] = ((pi.Attributes & ParameterAttributes.Out) != ParameterAttributes.None); pm[2] = pi.ParameterType.IsByRef; - pms[i] = pm; + if (i < pms.Length) + { + pms[i] = pm; + } } try { @@ -277,7 +284,7 @@ static public void ExecuteVoidRef(object o, string mthd, Object[] args) }catch(MissingMethodException) { - throw new GxClassLoaderException("Method " + mi.DeclaringType.FullName + "." + mi.Name + " for " + args.Length + " parameters ("+ String.Join(",", args) + ") not found"); + throw new GxClassLoaderException(BuildParameterMismatchErrorMessage(mi.DeclaringType.FullName, pis, args, propertyName)); } } else @@ -294,6 +301,110 @@ static public void ExecuteVoidRef(object o, string mthd, Object[] args) throw GxClassLoaderException.ProcessException(e); } } + static string BuildParameterMismatchErrorMessage(string objectName, ParameterInfo[] methodParameters, object[] runtimeMethodParameters, string propertyName) + { + string parmInfo = GetParameterTypesString(methodParameters); + string runtimeParms = GetParameterValuesString(runtimeMethodParameters); + StringBuilder errorMessage = new StringBuilder(); + + errorMessage.Append($"Program {objectName} "); + if (!string.IsNullOrEmpty(propertyName)) + { + errorMessage.Append($"referenced in {propertyName} property "); + } + errorMessage.Append($"does not have the expected parameter definition."); + if (methodParameters.Length == 0) + { + errorMessage.Append($"It does not have any parameters."); + } + else + { + errorMessage.Append($"Program parameter definition: {parmInfo}."); + } + + if (runtimeMethodParameters.Length == 0) + { + errorMessage.Append($"No parameter values were provided at runtime. "); + } + else if (runtimeMethodParameters.Length == 1) + { + errorMessage.Append($"Parameter value provided at runtime: {runtimeParms}. "); + } + else + { + errorMessage.Append($"Parameter values provided at runtime: {runtimeParms}. "); + } + errorMessage.Append($"Please check the parm rule of the {objectName}."); + + return errorMessage.ToString(); + } + + static string GetParameterValuesString(object[] runtimeMethodParameters) + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + + for (int i = 0; i < runtimeMethodParameters.Length; i++) + { + object parmValue = runtimeMethodParameters[i]; + string parmStringValue = (parmValue is string) ? $"\"{parmValue}\"" : parmValue.ToString(); + sb.Append(parmStringValue); + if (i < runtimeMethodParameters.Length - 1) + { + sb.Append(", "); + } + } + sb.Append(')'); + return sb.ToString(); + } + + static string GetParameterTypesString(ParameterInfo[] parameters) + { + StringBuilder sb = new StringBuilder(); + sb.Append('('); + + for (int i = 0; i < parameters.Length; i++) + { + string parmTypeName = ParameterTypeName(parameters[i].ParameterType); + sb.Append(parmTypeName); + if (i < parameters.Length - 1) + { + sb.Append(", "); + } + } + sb.Append(')'); + return sb.ToString(); + } + + static string ParameterTypeName(Type parameterType) + { + Type innerType = (parameterType.IsByRef && parameterType.GetElementType()!=null) ? parameterType.GetElementType() : parameterType; + if (IsNumericType(innerType)) + return "Numeric"; + return innerType.Name; + } + + static bool IsNumericType(Type type) + { + if (type == typeof(byte) || + type == typeof(sbyte) || + type == typeof(short) || + type == typeof(ushort) || + type == typeof(int) || + type == typeof(uint) || + type == typeof(long) || + type == typeof(ulong) || + type == typeof(float) || + type == typeof(double) || + type == typeof(decimal)) + { + return true; + } + else + { + return false; + } + } static public void ExecuteRef(object o, string mthd, Object[] args) { GXLogging.Debug(log, "ExecuteRef '" + "class '" + o + "' mthd '" + mthd + "'");