Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

External Object DynamicCall #578

Merged
merged 11 commits into from
Sep 13, 2022
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,4 @@ build
/dotnet/src/dotnetcore/GxDataInitialization/net6.0/GXDataInitialization.deps.json
/dotnet/src/dotnetcore/GxNetCoreStartup/net6.0/GxNetCoreStartup.deps.json
/dotnet/src/dotnetcore/Reor/net6.0/Reor.deps.json
/dotnet/.editorconfig
claudiamurialdo marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions dotnet/src/dotnetcore/GxClasses.Web/GxClasses.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup>
<Compile Include="..\..\dotnetframework\GxClasses\Domain\GXGeolocation.cs" Link="Domain\GXGeolocation.cs" />
<Compile Include="..\..\dotnetframework\GxClasses\Helpers\GxDynamicCall.cs" Link="Helpers\GxDynamicCall.cs" />
<Compile Include="..\..\dotnetframework\GxClasses\Middleware\GXHttp.cs" Link="Middleware\GXHttp.cs" />
<Compile Include="..\..\dotnetframework\GxClasses\Middleware\GXHttpServices.cs" Link="Middleware\GXHttpServices.cs" />
<Compile Include="..\..\dotnetframework\GxClasses\Domain\GXRuntime.cs" Link="Domain\GXRuntime.cs" />
Expand Down
193 changes: 193 additions & 0 deletions dotnet/src/dotnetframework/GxClasses/Helpers/GxDynamicCall.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
using GeneXus.Application;
using GeneXus.Metadata;
using GeneXus.Utils;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace GeneXus.DynamicCall
{
public class GxDynamicCall
{
private const string defaultMethod = "execute";
private Assembly _assembly;
private GxDynCallProperties _properties;
private object _object;
public string ObjectName { get; set; }
public GxDynCallProperties Properties
{
get => _properties;
set
{
_properties = Properties;
}
}

public GxDynamicCall()
{
_assembly= null;
_properties = new GxDynCallProperties();
_object = null;
}

private void VerifyDefaultProperties() {
_properties.NameSpace = string.IsNullOrEmpty(_properties.NameSpace) ? "GeneXus.Programs" : _properties.NameSpace;

if (_assembly is null)
{
if (string.IsNullOrEmpty(_properties.AssemblyName))
{
_assembly = Assembly.GetCallingAssembly();
}
else
{
try
{
_assembly = Assembly.LoadFrom(_properties.AssemblyName);
}
catch
{
throw;
}
}
}
}

public void Execute(ref IList<object> parameters, out IList<SdtMessages_Message> errors)
{
Create(null, out errors);
if (errors.Count == 0)
{
GxDynCallMethodConf methodConf = new GxDynCallMethodConf();
Execute(ref parameters, methodConf, out errors);
}
}

public void Create( IList<object> constructParms, out IList<SdtMessages_Message> errors)
{
errors = new GXBaseCollection<SdtMessages_Message>();
string objectNameToInvoke;
try
{
VerifyDefaultProperties();
if (constructParms is null)
{
objectNameToInvoke = ObjectName;
}
else
{
objectNameToInvoke = _properties.ExternalName;
}
try
{
Type objType = ClassLoader.FindType(objectNameToInvoke, _properties.NameSpace, objectNameToInvoke.ToLower().Trim(), _assembly);
object[] constructorParameters;
if (constructParms != null && constructParms.Count > 0)
{
constructorParameters = new object[constructParms.Count];
constructParms.CopyTo(constructorParameters, 0);
}
else
{
constructorParameters = new object[] { new GxContext() };
}
_object = Activator.CreateInstance(objType, constructorParameters);
}
catch (Exception e)
{
GXUtil.ErrorToMessages("CreateInstance Error", e.Message, (GXBaseCollection<SdtMessages_Message>) errors);
}
}
catch (Exception e)
{
GXUtil.ErrorToMessages("VerifyProperties Error", e.Message, (GXBaseCollection<SdtMessages_Message>)errors);
}
}
public object Execute(ref IList<object> parameters, GxDynCallMethodConf methodconfiguration , out IList<SdtMessages_Message> errors)
{
object result;
errors = new GXBaseCollection<SdtMessages_Message>();
IList<object> outParms= new List<object>();

string methodName = methodconfiguration.MethodName;
bool isStatic = methodconfiguration.IsStatic;

if (!isStatic)
{
if (_object != null)
{
try
{
outParms = ReflectionHelper.CallMethod(_object, (string.IsNullOrEmpty(methodName) ? defaultMethod : methodName), parameters);
}
catch (Exception e)
{
GXUtil.ErrorToMessages("CallMethod Error", e.Message, (GXBaseCollection<SdtMessages_Message>)errors);
}
}
else
{
GXUtil.ErrorToMessages("NullInstance Error", "You must invoke create method before executing a non-static one", (GXBaseCollection<SdtMessages_Message>)errors);
}
}
else
{
VerifyDefaultProperties();
Type objType = ClassLoader.FindType(_properties.ExternalName, _properties.NameSpace, _properties.ExternalName.ToLower().Trim(), _assembly);
outParms = ReflectionHelper.CallMethod(objType, (string.IsNullOrEmpty(methodName) ? defaultMethod : methodName), parameters, isStatic);
}
if (outParms.Count > parameters.Count)
{
result = outParms[parameters.Count];
outParms.RemoveAt(parameters.Count);

}
else
{
result = null;
}
parameters = outParms;
return result;
}

}
public class GxDynCallMethodConf
{
public bool IsStatic
{
get; set;
}
public string MethodName
{
get; set;
}

public GxDynCallMethodConf()
{
IsStatic = false;
MethodName = "execute";
}

}

public class GxDynCallProperties
{
public string ExternalName
{
get;
set;
}
public string AssemblyName
{
get;
set;
}
public string NameSpace
{
get;
set;
}

}
}

89 changes: 75 additions & 14 deletions dotnet/src/dotnetframework/GxClasses/Services/ReflectionHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
namespace GeneXus.Application
{
public class ReflectionHelper
{
const string ISO_8601_TIME_SEPARATOR= "T";
{
const string ISO_8601_TIME_SEPARATOR = "T";
const string ISO_8601_TIME_SEPARATOR_1 = ":";
public static void CallBCMethod(object instance, String methodName, IList<string> inParametersValues)
{
Expand Down Expand Up @@ -43,17 +43,42 @@ public static Dictionary<string, object> CallMethodPattern(object instance, Stri
MethodInfo methodInfo = instanceType.GetMethod(memberInfo.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
return CallMethodImpl(instance, methodInfo, parameters, context);
}
public static Dictionary<string, object> CallMethod(object instance, String methodName, IDictionary<string, object> parameters, IGxContext context=null)

public static Dictionary<string, object> CallMethod(object instance, String methodName, IDictionary<string, object> parameters, IGxContext context = null)
{
MethodInfo methodInfo = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
return CallMethodImpl(instance, methodInfo, parameters, context);
}
public static IList<object> CallMethod(object instanceOrType, String methodName, IList<object> parameters, bool isStatic = false, IGxContext context = null)
{
MethodInfo methodInfo;
object instance = null;
if (isStatic)
{
methodInfo = ((Type)instanceOrType).GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.IgnoreCase);
}
else
{

methodInfo = instanceOrType.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
instance = instanceOrType;
}
return CallMethodImpl(instance, methodInfo, parameters, context);
}
static Dictionary<string, object> CallMethodImpl(object instance, MethodInfo methodInfo, IDictionary<string, object> parameters, IGxContext context)
{
object[] parametersForInvocation = ProcessParametersForInvoke(methodInfo, parameters, context);
object returnParm = methodInfo.Invoke(instance, parametersForInvocation);
return ProcessParametersAfterInvoke(methodInfo, parametersForInvocation, returnParm);
}
static IList<object> CallMethodImpl(object instance, MethodInfo methodInfo, IList<object> parameters, IGxContext context)
{
object[] parametersForInvocation = ProcessParametersForInvoke(methodInfo, parameters, context);
object returnParm = methodInfo.Invoke(instance, parametersForInvocation);
IList<object> parametersAfterInvoke = parametersForInvocation.ToList<object>();
parametersAfterInvoke.Add(returnParm);
return parametersAfterInvoke;
}
public static bool MethodHasInputParameters(object instance, String methodName)
{
MethodInfo methodInfo = instance.GetType().GetMethod(methodName);
Expand Down Expand Up @@ -97,16 +122,16 @@ public static Dictionary<string, object> GetWrappedParameter(object instance, St

private static object ConvertSingleJsonItem(object value, Type newType, IGxContext context)
{
if (value!= null && value.GetType() == newType)
if (value != null && value.GetType() == newType)
{
return value;
}
else if (typeof(IGxJSONAble).IsAssignableFrom(newType))
{
object TObject;
if (typeof(GxSilentTrnSdt).IsAssignableFrom(newType) && context!=null)
if (typeof(GxSilentTrnSdt).IsAssignableFrom(newType) && context != null)
{
TObject = Activator.CreateInstance(newType, new object[] { context});
TObject = Activator.CreateInstance(newType, new object[] { context });
}
else
{
Expand Down Expand Up @@ -145,9 +170,9 @@ private static object ConvertSingleJsonItem(object value, Type newType, IGxConte
}
}

private static object ConvertStringToNewNonNullableType(object value, Type newType, IGxContext context=null)
private static object ConvertStringToNewNonNullableType(object value, Type newType, IGxContext context = null)
{

if (newType.IsArray)
{
// For comma separated list
Expand All @@ -164,7 +189,7 @@ private static object ConvertStringToNewNonNullableType(object value, Type newTy
return ConvertSingleJsonItem(value, newType, context);
}

internal static object ConvertStringToNewType(object value, Type newType, IGxContext context=null)
internal static object ConvertStringToNewType(object value, Type newType, IGxContext context = null)
{
// If it's not a nullable type, just pass through the parameters to Convert.ChangeType
if (newType.GetTypeInfo().IsGenericType && newType.GetGenericTypeDefinition() != null && newType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
Expand All @@ -181,9 +206,9 @@ internal static object ConvertStringToNewType(object value, Type newType, IGxCon
public static Dictionary<string, string> ParametersFormat(object instance, string methodName)
{
MethodInfo methodInfo = instance.GetType().GetMethod(methodName);

Dictionary<string, string> formatList = new Dictionary<string, string>();
var methodParameters = methodInfo.GetParameters();
var methodParameters = methodInfo.GetParameters();
foreach (var methodParameter in methodParameters)
{
var gxParameterName = GxParameterName(methodParameter.Name);
Expand All @@ -200,6 +225,7 @@ public static Dictionary<string, string> ParametersFormat(object instance, strin
return formatList;
}


private static Dictionary<string, object> ProcessParametersAfterInvoke(MethodInfo methodInfo, object[] parametersForInvocation, object returnParm)
{
Dictionary<string, object> outputParameters = new Dictionary<string, object>();
Expand All @@ -220,22 +246,22 @@ private static Dictionary<string, object> ProcessParametersAfterInvoke(MethodInf
}


internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDictionary<string, object> parameters, IGxContext context=null)
internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDictionary<string, object> parameters, IGxContext context = null)
{
var methodParameters = methodInfo.GetParameters();
object[] parametersForInvocation = new object[methodParameters.Length];
var idx = 0;
foreach (var methodParameter in methodParameters)
{
object value;

var gxParameterName = GxParameterName(methodParameter.Name).ToLower();
Type parmType = methodParameter.ParameterType;
if (IsByRefParameter(methodParameter))
{
parmType = parmType.GetElementType();
}
if (parameters!=null && parameters.TryGetValue(gxParameterName, out value))
if (parameters != null && parameters.TryGetValue(gxParameterName, out value))
{
var convertedValue = ConvertStringToNewType(value, parmType, context);
parametersForInvocation[idx] = convertedValue;
Expand All @@ -249,6 +275,41 @@ internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IDict
}
return parametersForInvocation;
}

internal static object[] ProcessParametersForInvoke(MethodInfo methodInfo, IList<object> parameters, IGxContext context = null)
{
var methodParameters = methodInfo.GetParameters();
object[] parametersForInvocation = new object[methodParameters.Length];
var idx = 0;
foreach (var methodParameter in methodParameters)
{
Type parmType = methodParameter.ParameterType;
if (IsByRefParameter(methodParameter))
{
parmType = parmType.GetElementType();
}
object value = parameters.ElementAt(idx);
if (!value.GetType().Equals(parmType))
{
//To avoid convertion from string type
if (value.GetType() != typeof(string))
{
var convertedValue = ConvertStringToNewType(value, parmType, context);
parametersForInvocation[idx] = convertedValue;
}
else
{
throw new ArgumentException("Type does not match", methodParameter.Name);
}
}
else
{
parametersForInvocation[idx] = value;
}
idx++;
}
return parametersForInvocation;
}
private static Regex attVar = new Regex(@"^AV?\d{1,}", RegexOptions.Compiled);
private static string GxParameterName(string methodParameterName)
{
Expand Down