Skip to content

Commit

Permalink
Dynamic RFC Function Result
Browse files Browse the repository at this point in the history
  • Loading branch information
campersau committed Dec 30, 2021
1 parent d33e865 commit 8c02ac1
Show file tree
Hide file tree
Showing 10 changed files with 940 additions and 2 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,18 @@ var result = someFunction.Invoke<SomeFunctionResult>(new SomeFunctionParameters
// Do something with result.Abc
```

### Call function with dynamic input and output parameters

```csharp
using var someFunction = connection.CreateFunction("BAPI_SOME_FUNCTION_NAME");
var result = someFunction.Invoke<dynamic>(new
{
SOME_FIELD = "Some value",
});

string abc = result.RES_ABC;
```

### Define models with a nested structure

```csharp
Expand Down
97 changes: 97 additions & 0 deletions src/SapNwRfc/Internal/Dynamic/DynamicRfc.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
using System;
using SapNwRfc.Internal.Fields;
using SapNwRfc.Internal.Interop;

namespace SapNwRfc.Internal.Dynamic
{
internal static class DynamicRfc
{
internal static bool TryGetRfcValue(RfcInterop interop, IntPtr dataHandle, string name, SapRfcType type, Func<ISapTypeMetadata> typeMetadata, Type returnType, out object result)
{
result = GetRfcValue(interop, dataHandle, name, type, typeMetadata);

if (returnType == typeof(object))
{
return true;
}

try
{
result = Convert.ChangeType(result, returnType);
return true;
}
catch { }

result = null;
return false;
}

private static object GetRfcValue(RfcInterop interop, IntPtr dataHandle, string name, SapRfcType type, Func<ISapTypeMetadata> typeMetadata)
{
switch (type)
{
case SapRfcType.RFCTYPE_CHAR:
case SapRfcType.RFCTYPE_NUM:
case SapRfcType.RFCTYPE_BCD:
case SapRfcType.RFCTYPE_STRING:
return StringField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_INT:
case SapRfcType.RFCTYPE_INT1:
case SapRfcType.RFCTYPE_INT2:
return IntField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_INT8:
return LongField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_FLOAT:
return DoubleField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_DECF16:
case SapRfcType.RFCTYPE_DECF34:
return DecimalField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_DATE:
return DateField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_TIME:
return TimeField.Extract(interop, dataHandle, name).Value;

case SapRfcType.RFCTYPE_BYTE:
case SapRfcType.RFCTYPE_XSTRING:
return BytesField.Extract(interop, dataHandle, name, bufferLength: 0).Value;

case SapRfcType.RFCTYPE_TABLE:
{
RfcResultCode resultCode = interop.GetTable(
dataHandle: dataHandle,
name: name,
tableHandle: out IntPtr tableHandle,
errorInfo: out RfcErrorInfo errorInfo);

resultCode.ThrowOnError(errorInfo);

return new DynamicRfcTable(interop, tableHandle, typeMetadata());
}

case SapRfcType.RFCTYPE_STRUCTURE:
{
RfcResultCode resultCode = interop.GetStructure(
dataHandle: dataHandle,
name: name,
structHandle: out IntPtr structHandle,
errorInfo: out RfcErrorInfo errorInfo);

resultCode.ThrowOnError(errorInfo);

return new DynamicRfcStructure(interop, structHandle, typeMetadata());
}

case SapRfcType.RFCTYPE_NULL:
return null;
}

throw new NotSupportedException($"Parameter type {type} is not supported");
}
}
}
158 changes: 158 additions & 0 deletions src/SapNwRfc/Internal/Dynamic/DynamicRfcFunction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using SapNwRfc.Internal.Interop;

namespace SapNwRfc.Internal.Dynamic
{
internal sealed class DynamicRfcFunction : DynamicObject, IReadOnlyDictionary<string, object>
{
private readonly RfcInterop _interop;
private readonly IntPtr _functionHandle;
private readonly ISapFunctionMetadata _functionMetadata;

internal DynamicRfcFunction(RfcInterop interop, IntPtr functionHandle, ISapFunctionMetadata functionMetadata)
{
_interop = interop;
_functionHandle = functionHandle;
_functionMetadata = functionMetadata;
}

private bool TryGetByName(string name, Type returnType, out object result)
{
if (_functionMetadata.Parameters.TryGetValue(name, out ISapParameterMetadata parameter))
{
if (DynamicRfc.TryGetRfcValue(_interop, _functionHandle, parameter.Name, parameter.Type, parameter.GetTypeMetadata, returnType, out result))
{
return true;
}
}

result = null;
return false;
}

private bool TryGetByIndex(int index, Type returnType, out object result)
{
if (index >= 0 && index < _functionMetadata.Parameters.Count)
{
ISapParameterMetadata parameter = _functionMetadata.Parameters[index];

if (DynamicRfc.TryGetRfcValue(_interop, _functionHandle, parameter.Name, parameter.Type, parameter.GetTypeMetadata, returnType, out result))
{
return true;
}
}

result = null;
return false;
}

public override IEnumerable<string> GetDynamicMemberNames()
{
return Keys;
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return TryGetByName(binder.Name, binder.ReturnType, out result);
}

public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length != 1)
throw new ArgumentOutOfRangeException(nameof(indexes));

var index = indexes[0];

if (index is string name)
return TryGetByName(name, binder.ReturnType, out result);

if (index is int i)
return TryGetByIndex(i, binder.ReturnType, out result);

if (index is uint u)
return TryGetByIndex((int)u, binder.ReturnType, out result);

throw new ArgumentException(nameof(indexes));
}

public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if (binder.Name == "GetMetadata" && binder.CallInfo.ArgumentCount == 0)
{
result = _functionMetadata;
return true;
}

return base.TryInvokeMember(binder, args, out result);
}

public IEnumerable<string> Keys
{
get
{
foreach (var parameter in _functionMetadata.Parameters)
{
yield return parameter.Name;
}
}
}

public IEnumerable<object> Values
{
get
{
foreach (var parameter in _functionMetadata.Parameters)
{
if (TryGetByName(parameter.Name, typeof(object), out object result))
{
yield return result;
}
}
}
}

public int Count => _functionMetadata.Parameters.Count;

public object this[string key]
{
get
{
if (TryGetByName(key, typeof(object), out var result))
{
return result;
}

throw new KeyNotFoundException();
}
}

public bool ContainsKey(string key)
{
return _functionMetadata.Parameters.TryGetValue(key, out _);
}

public bool TryGetValue(string key, out object value)
{
return TryGetByName(key, typeof(object), out value);
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
foreach (var parameter in _functionMetadata.Parameters)
{
if (TryGetByName(parameter.Name, typeof(object), out object result))
{
yield return new KeyValuePair<string, object>(parameter.Name, result);
}
}
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Loading

0 comments on commit 8c02ac1

Please sign in to comment.