diff --git a/YaNco.sln.DotSettings b/YaNco.sln.DotSettings
new file mode 100644
index 00000000..7ce9f154
--- /dev/null
+++ b/YaNco.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/IRfcRuntime.cs b/src/YaNco.Abstractions/IRfcRuntime.cs
index 03d51ab3..17fe180f 100644
--- a/src/YaNco.Abstractions/IRfcRuntime.cs
+++ b/src/YaNco.Abstractions/IRfcRuntime.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Dbosoft.YaNco;
using LanguageExt;
@@ -46,5 +47,10 @@ public interface IRfcRuntime
Either GetTimeString(IDataContainerHandle containerHandle, string name);
Option Logger { get; }
+
+ Either SetFieldValue(IDataContainerHandle handle, T value, Func> func);
+ Either GetFieldValue(IDataContainerHandle handle, Func> func);
}
+
+
}
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs b/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs
new file mode 100644
index 00000000..3ffbbe2a
--- /dev/null
+++ b/src/YaNco.Abstractions/TypeMapping/FieldMappingContext.cs
@@ -0,0 +1,16 @@
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class FieldMappingContext
+ {
+ public readonly IRfcRuntime RfcRuntime;
+ public readonly IDataContainerHandle Handle;
+ public readonly RfcFieldInfo FieldInfo;
+
+ public FieldMappingContext(IRfcRuntime rfcRuntime, IDataContainerHandle handle, RfcFieldInfo fieldInfo)
+ {
+ RfcRuntime = rfcRuntime;
+ Handle = handle;
+ FieldInfo = fieldInfo;
+ }
+ }
+}
diff --git a/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs b/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs
new file mode 100644
index 00000000..b65e3e3f
--- /dev/null
+++ b/src/YaNco.Abstractions/TypeMapping/IFieldMapper.cs
@@ -0,0 +1,11 @@
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public interface IFieldMapper
+ {
+ Either SetField(T value, FieldMappingContext context);
+ Either GetField(FieldMappingContext context);
+
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs b/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs
new file mode 100644
index 00000000..b64030f7
--- /dev/null
+++ b/src/YaNco.Abstractions/TypeMapping/IFromAbapValueConverter.cs
@@ -0,0 +1,11 @@
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public interface IFromAbapValueConverter
+ {
+ Try ConvertTo(AbapValue abapValue);
+ bool CanConvertTo(RfcType rfcType);
+
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs b/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs
new file mode 100644
index 00000000..3176a24a
--- /dev/null
+++ b/src/YaNco.Abstractions/TypeMapping/IRfcConverterResolver.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public interface IRfcConverterResolver
+ {
+ IEnumerable> GetToRfcConverters(RfcType rfcType);
+ IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType);
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs b/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs
new file mode 100644
index 00000000..0d8f6669
--- /dev/null
+++ b/src/YaNco.Abstractions/TypeMapping/IToAbapValueConverter.cs
@@ -0,0 +1,11 @@
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public interface IToAbapValueConverter
+ {
+ Try ConvertFrom(T value, RfcFieldInfo fieldInfo);
+ bool CanConvertFrom(RfcType rfcType);
+
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Abstractions/YaNco.Abstractions.csproj b/src/YaNco.Abstractions/YaNco.Abstractions.csproj
index 0ec99b05..ac39ff23 100644
--- a/src/YaNco.Abstractions/YaNco.Abstractions.csproj
+++ b/src/YaNco.Abstractions/YaNco.Abstractions.csproj
@@ -20,7 +20,7 @@ This package contains abstraction definitions.
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/src/YaNco.Core/DataContainer.cs b/src/YaNco.Core/DataContainer.cs
index c263f213..d9ed3cd2 100644
--- a/src/YaNco.Core/DataContainer.cs
+++ b/src/YaNco.Core/DataContainer.cs
@@ -17,162 +17,14 @@ protected DataContainer(IDataContainerHandle handle, IRfcRuntime rfcRuntime)
public Either SetField(string name, T value)
{
- switch (value)
- {
- case int intValue:
- return _rfcRuntime.SetInt(_handle, name, intValue);
- case long longValue:
- return _rfcRuntime.SetLong(_handle, name, longValue);
- case DateTime dateTime:
- return GetFieldInfo(name)
- .Bind(typeDesc =>
- {
- switch (typeDesc.Type)
- {
- case RfcType.DATE:
- return SetFieldAsDate(name, dateTime);
- case RfcType.TIME:
- return SetFieldAsTime(name, dateTime);
- default:
- return _rfcRuntime.SetString(_handle, name,
- (string)Convert.ChangeType(value, typeof(string)));
- }
- });
-
- default:
- {
- return _rfcRuntime.SetString(_handle, name,
- (string)Convert.ChangeType(value, typeof(string)));
-
- }
- }
+ return _rfcRuntime.SetFieldValue(_handle, value, () => GetFieldInfo(name));
}
protected abstract Either GetFieldInfo(string name);
public Either GetField(string name)
{
- return GetFieldInfo(name)
- .Bind(typeDesc =>
- {
- switch (typeDesc.Type)
- {
- case RfcType.BYTE:
- return GetFieldAsInt(name);
- case RfcType.NUM:
- return GetFieldAsInt(name);
- case RfcType.INT:
- return GetFieldAsInt(name);
- case RfcType.INT2:
- return GetFieldAsInt(name);
- case RfcType.INT1:
- return GetFieldAsInt(name);
- case RfcType.INT8:
- return GetFieldAsLong(name);
- case RfcType.DATE:
- return GetFieldAsDate(name);
- case RfcType.TIME:
- return GetFieldAsTime(name);
- default:
- return GetFieldAsString(name);
- }
- });
- }
-
- private Either GetFieldAsString(string name)
- {
- return _rfcRuntime.GetString(_handle, name).Map(r =>
- {
- object value = r;
-
- if (typeof(T) == typeof(bool))
- {
- value = !string.IsNullOrWhiteSpace(r);
- }
-
- return (T)Convert.ChangeType(value, typeof(T));
- });
- }
-
- private Either SetFieldAsDate(string name, DateTime value)
- {
- var dateString = value.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
- return _rfcRuntime.SetDateString(_handle, name, dateString);
- }
-
- private Either SetFieldAsTime(string name, DateTime value)
- {
- var dateString = value.ToString("HHmmss", CultureInfo.InvariantCulture);
- return _rfcRuntime.SetDateString(_handle, name, dateString);
- }
-
- private Either GetFieldAsInt(string name)
- {
- return _rfcRuntime.GetInt(_handle, name).Map(r =>
- {
- object value = r;
-
- if (typeof(T) == typeof(bool))
- {
- value = r != 0;
- }
-
- return (T)Convert.ChangeType(value, typeof(T));
- });
- }
-
- private Either GetFieldAsLong(string name)
- {
- return _rfcRuntime.GetLong(_handle, name).Map(r =>
- {
- object value = r;
-
- if (typeof(T) == typeof(bool))
- {
- value = r != 0;
- }
-
- return (T)Convert.ChangeType(value, typeof(T));
- });
- }
-
- private Either GetFieldAsDate(string name)
- {
- return _rfcRuntime.GetDateString(_handle, name).Map(r =>
- {
- object value;
- if (typeof(T) == typeof(string))
- value = r;
- else
- {
- if (r == "00000000" || r == string.Empty)
- value = DateTime.MinValue;
- else
- value = DateTime.ParseExact(r, "yyyyMMdd", CultureInfo.InvariantCulture);
- }
-
-
- return (T)Convert.ChangeType(value, typeof(T));
- });
- }
-
- private Either GetFieldAsTime(string name)
- {
- return _rfcRuntime.GetDateString(_handle, name).Map(r =>
- {
- object value;
- if (typeof(T) == typeof(string))
- value = r;
- else
- {
- if (r == "000000" || r == string.Empty)
- value = DateTime.MinValue;
- else
- value = DateTime.ParseExact(r, "HHmmss", CultureInfo.InvariantCulture);
- }
-
- return (T)Convert.ChangeType(value, typeof(T));
- });
+ return _rfcRuntime.GetFieldValue(_handle,() => GetFieldInfo(name));
}
public Either SetFieldBytes(string name, byte[] buffer, long bufferLength)
@@ -195,8 +47,7 @@ public Either GetTable(string name)
{
return _rfcRuntime.GetTable(_handle, name).Map(handle => (ITable) new Table(handle, _rfcRuntime));
}
-
-
+
protected virtual void Dispose(bool disposing)
{
if (disposing)
diff --git a/src/YaNco.Core/Internal/Api.cs b/src/YaNco.Core/Internal/Api.cs
index 1c98991b..e1a3972e 100644
--- a/src/YaNco.Core/Internal/Api.cs
+++ b/src/YaNco.Core/Internal/Api.cs
@@ -319,13 +319,13 @@ public static RfcRc GetBytes(IDataContainerHandle containerHandle, string name,
if (rc != RfcRc.RFC_BUFFER_TOO_SMALL)
{
buffer = new byte[bufferLength];
- tempBuffer.CopyTo(buffer, 0);
+ Array.Copy(tempBuffer, buffer, bufferLength);
return rc;
}
tempBuffer = new byte[bufferLength];
- rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, 255, out _, out errorInfo);
+ rc = Interopt.RfcGetXString(containerHandle.Ptr, name, tempBuffer, bufferLength, out _, out errorInfo);
buffer = new byte[bufferLength];
tempBuffer.CopyTo(buffer, 0);
diff --git a/src/YaNco.Core/RfcRuntime.cs b/src/YaNco.Core/RfcRuntime.cs
index da186157..5a76b415 100644
--- a/src/YaNco.Core/RfcRuntime.cs
+++ b/src/YaNco.Core/RfcRuntime.cs
@@ -1,15 +1,26 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using Dbosoft.YaNco.Internal;
+using Dbosoft.YaNco.TypeMapping;
using LanguageExt;
+using FieldMappingContext = Dbosoft.YaNco.TypeMapping.FieldMappingContext;
+
// ReSharper disable UnusedMember.Global
namespace Dbosoft.YaNco
{
public class RfcRuntime : IRfcRuntime
{
- public RfcRuntime(ILogger logger = null)
+ private readonly IFieldMapper _fieldMapper;
+
+ public RfcRuntime(ILogger logger = null, IFieldMapper fieldMapper = null)
{
Logger = logger == null ? Option.None : Option.Some(logger);
+ _fieldMapper = fieldMapper ??
+ new DefaultFieldMapper(
+ new CachingConverterResolver(
+ DefaultConverterResolver.CreateWithBuildInConverters()));
+
}
private Either ResultOrError(TResult result, RfcErrorInfo errorInfo, bool logAsError = false)
@@ -292,6 +303,27 @@ public Either GetTimeString(IDataContainerHandle container
public Option Logger { get; }
+ public Either SetFieldValue(IDataContainerHandle handle, T value, Func> func)
+ {
+ return func().Bind(fieldInfo =>
+ {
+ Logger.IfSome(l => l.LogTrace("setting field value", new { handle, fieldInfo, SourceType= typeof(T) }));
+ return _fieldMapper.SetField(value, new FieldMappingContext(this, handle, fieldInfo));
+ });
+
+ }
+
+ public Either GetFieldValue(IDataContainerHandle handle, Func> func)
+ {
+ return func().Bind(fieldInfo =>
+ {
+ Logger.IfSome(l => l.LogTrace("reading field value", new { handle, fieldInfo, TargetType = typeof(T) }));
+ return _fieldMapper.GetField(new FieldMappingContext(this, handle, fieldInfo));
+ });
+
+
+ }
+
public Either SetInt(IDataContainerHandle containerHandle, string name, int value)
{
Logger.IfSome(l => l.LogTrace("setting int value by name", new { containerHandle, name, value }));
diff --git a/src/YaNco.Core/TypeMapping/ByteValueConverter.cs b/src/YaNco.Core/TypeMapping/ByteValueConverter.cs
new file mode 100644
index 00000000..771c3ee2
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/ByteValueConverter.cs
@@ -0,0 +1,49 @@
+using System;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class ByteValueConverter: IToAbapValueConverter, IFromAbapValueConverter
+ {
+ public Try ConvertFrom(byte[] value, RfcFieldInfo fieldInfo)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!IsSupportedRfcType(fieldInfo.Type))
+ throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to byte array.");
+
+ return new AbapByteValue(fieldInfo, value);
+ });
+
+ }
+
+ public bool CanConvertFrom(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ public Try ConvertTo(AbapValue abapValue)
+ {
+ return Prelude.Try(() => (abapValue as AbapByteValue)?.Value);
+ }
+
+ public bool CanConvertTo(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ private bool IsSupportedRfcType(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.BYTE:
+ case RfcType.XSTRING:
+ return true;
+ default:
+ return false;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs b/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs
new file mode 100644
index 00000000..b5ea0da9
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/CachingConverterResolver.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class CachingConverterResolver : IRfcConverterResolver
+ {
+ private readonly IRfcConverterResolver _decoratedResolver;
+
+ public CachingConverterResolver(IRfcConverterResolver decoratedResolver)
+ {
+ _decoratedResolver = decoratedResolver;
+ }
+
+ private readonly IDictionary _fromRfcConverters = new ConcurrentDictionary();
+ private readonly IDictionary _toRfcConverters = new ConcurrentDictionary();
+
+ public IEnumerable> GetToRfcConverters(RfcType rfcType)
+ {
+ var sourceType = typeof(T);
+ var key = $"{rfcType}_{sourceType}";
+
+ if (!_toRfcConverters.ContainsKey(key))
+ {
+ var converters = _decoratedResolver.GetToRfcConverters(rfcType).ToArray();
+ _toRfcConverters.Add(key, converters.Length == 0 ? null : converters);
+
+ }
+
+ var entry = _toRfcConverters[key];
+
+ if (entry != null)
+ return (IEnumerable>)entry;
+ return new IToAbapValueConverter[0];
+
+
+ }
+
+ public IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType)
+ {
+ var targetType = typeof(T);
+ var key = $"{rfcType}_{targetType}";
+
+ if (!_fromRfcConverters.ContainsKey(key))
+ {
+ var converters = _decoratedResolver.GetFromRfcConverters(rfcType, abapValueType).ToArray();
+ _fromRfcConverters.Add(key, converters.Length == 0 ? null : converters);
+ }
+
+ var entry = _fromRfcConverters[key];
+
+ if (entry != null)
+ return (IEnumerable>)entry;
+ return new IFromAbapValueConverter[0];
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs b/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs
new file mode 100644
index 00000000..929cc709
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/DateTimeValueConverter.cs
@@ -0,0 +1,99 @@
+using System;
+using System.Globalization;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class DateTimeValueConverter: IToAbapValueConverter, IFromAbapValueConverter
+ {
+ public Try ConvertFrom(DateTime value, RfcFieldInfo fieldInfo)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!IsSupportedRfcType(fieldInfo.Type))
+ throw new NotSupportedException($"Cannot convert DateTime to RfcType {fieldInfo.Type} .");
+
+ string stringValue;
+
+ var dateTime = (DateTime)Convert.ChangeType(value, typeof(DateTime), CultureInfo.InvariantCulture);
+
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (fieldInfo.Type)
+ {
+ case RfcType.DATE:
+ stringValue = dateTime.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
+ break;
+ case RfcType.TIME:
+ stringValue = dateTime.ToString("HHmmss", CultureInfo.InvariantCulture);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(fieldInfo), fieldInfo.Type,
+ $"not supported Type field in {nameof(fieldInfo)}.");
+ }
+
+ return new AbapStringValue(fieldInfo, stringValue);
+ });
+
+ }
+
+ public bool CanConvertFrom(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ private bool IsSupportedRfcType(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.DATE:
+ case RfcType.TIME:
+ return true;
+ default:
+ return false;
+ }
+
+ }
+
+ public Try ConvertTo(AbapValue abapValue)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!(abapValue is AbapStringValue stringValue))
+ throw new ArgumentException($"DateTimeConverter cannot convert type {abapValue.GetType()}",
+ nameof(abapValue));
+
+ DateTime dateTime;
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (stringValue.FieldInfo.Type)
+ {
+ case RfcType.DATE:
+ if (stringValue.Value == "00000000" || stringValue.Value == string.Empty)
+ dateTime = DateTime.MinValue;
+ else
+ dateTime = DateTime.ParseExact(stringValue.Value, "yyyyMMdd", CultureInfo.InvariantCulture);
+ break;
+ case RfcType.TIME:
+ if (stringValue.Value == "000000" || stringValue.Value == string.Empty)
+ dateTime = DateTime.MinValue;
+ else
+ dateTime = default(DateTime).Add(
+ DateTime.ParseExact(stringValue.Value, "HHmmss", CultureInfo.InvariantCulture).TimeOfDay);
+ break;
+ default:
+ throw new NotSupportedException(
+ $"It is not supported to convert RfcType {abapValue.FieldInfo.Type} to DateTime");
+ }
+
+ return dateTime;
+
+ });
+
+ }
+
+ public bool CanConvertTo(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs b/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs
new file mode 100644
index 00000000..58112b54
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/DefaultConverterResolver.cs
@@ -0,0 +1,86 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class DefaultConverterResolver : IRfcConverterResolver
+ {
+ private readonly IEnumerable _toRfcConverters;
+ private readonly IEnumerable _fromRfcConverters;
+
+ public static DefaultConverterResolver CreateWithBuildInConverters(
+ IEnumerable fromRfcConverters = null, IEnumerable toRfcConverters = null)
+ {
+ fromRfcConverters = (fromRfcConverters ?? new Type[0]).Append(new []
+ {
+ typeof(DateTimeValueConverter),
+ typeof(ByteValueConverter),
+ typeof(DefaultFromAbapValueConverter<>),
+ });
+
+ toRfcConverters = (toRfcConverters ?? new Type[0]).Append(new[]
+ {
+ typeof(IntValueConverter<>),
+ typeof(LongValueConverter<>),
+ typeof(StringValueConverter<>),
+ typeof(ByteValueConverter),
+ typeof(DateTimeValueConverter)
+ });
+
+ return new DefaultConverterResolver(
+ fromRfcConverters, toRfcConverters);
+ }
+
+ public DefaultConverterResolver(IEnumerable fromRfcConverters, IEnumerable toRfcConverters)
+ {
+ _fromRfcConverters = fromRfcConverters;
+ _toRfcConverters = toRfcConverters;
+ }
+
+ protected virtual object CreateConverter(Type type, Type targetType = null, Type abapType = null)
+ {
+ if (!type.IsGenericTypeDefinition)
+ return Activator.CreateInstance(type);
+
+ var typeArguments = new List();
+ foreach (var argument in type.GetGenericArguments())
+ {
+ if (!(targetType is null) && !argument.IsSubclassOf(typeof(AbapValue)))
+ {
+ typeArguments.Add(targetType);
+ }
+ else
+ {
+ if (abapType == null) continue;
+
+ if (abapType.IsSubclassOf(typeof(AbapValue)))
+ typeArguments.Add(abapType);
+ }
+ }
+
+ return Activator.CreateInstance(type.MakeGenericType(typeArguments.ToArray()));
+ }
+
+ public IEnumerable> GetToRfcConverters(RfcType rfcType)
+ {
+ return _toRfcConverters
+ .Map(type => CreateConverter(type, typeof(T)) as IToAbapValueConverter)
+ .Where(c => c != null)
+ .Where(c => c.CanConvertFrom(rfcType));
+
+ }
+
+ public IEnumerable> GetFromRfcConverters(RfcType rfcType, Type abapValueType)
+ {
+ return _fromRfcConverters
+ .Map(type =>
+ (CreateConverter(type, typeof(T), abapValueType)) as
+ IFromAbapValueConverter)
+ .Where(c => c != null)
+ .Where(c => c.CanConvertTo(rfcType));
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs b/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs
new file mode 100644
index 00000000..c5a6bbf0
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/DefaultFieldMapper.cs
@@ -0,0 +1,122 @@
+using System;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class DefaultFieldMapper : IFieldMapper
+ {
+ private readonly IRfcConverterResolver _converterResolver;
+
+ public DefaultFieldMapper(IRfcConverterResolver converterResolver)
+ {
+ _converterResolver = converterResolver;
+ }
+
+ public Either SetField(T value, FieldMappingContext context)
+ {
+ AbapValue abapValue = null;
+
+ foreach (var converter in _converterResolver.GetToRfcConverters(context.FieldInfo.Type))
+ {
+ var result = converter.ConvertFrom(value, context.FieldInfo)();
+ if(result.IsFaulted)
+ continue;
+ result.IfSucc(v => abapValue = v);
+ break;
+
+ }
+
+ if (abapValue == null)
+ return new RfcErrorInfo(RfcRc.RFC_CONVERSION_FAILURE, RfcErrorGroup.EXTERNAL_APPLICATION_FAILURE, "",
+ $"Converting from type {typeof(T)} to abap type {context.FieldInfo.Type} is not supported.",
+ "", "E", "", "", "", "", "");
+
+ switch (abapValue)
+ {
+ case AbapIntValue abapIntValue:
+ return context.RfcRuntime.SetInt(context.Handle, context.FieldInfo.Name, abapIntValue.Value);
+ case AbapLongValue abapLongValue:
+ return context.RfcRuntime.SetLong(context.Handle, context.FieldInfo.Name, abapLongValue.Value);
+ case AbapByteValue abapByteValue:
+ return context.RfcRuntime.SetBytes(context.Handle, context.FieldInfo.Name, abapByteValue.Value,
+ abapByteValue.Value.LongLength);
+ case AbapStringValue abapStringValue:
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (context.FieldInfo.Type)
+ {
+ case RfcType.DATE:
+ return context.RfcRuntime.SetDateString(context.Handle, context.FieldInfo.Name,
+ abapStringValue.Value);
+ case RfcType.TIME:
+ return context.RfcRuntime.SetTimeString(context.Handle, context.FieldInfo.Name,
+ abapStringValue.Value);
+ default:
+ return context.RfcRuntime.SetString(context.Handle, context.FieldInfo.Name, abapStringValue.Value);
+ }
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(abapValue));
+
+ }
+ }
+
+ public Either GetField(FieldMappingContext context)
+ {
+ return context.Apply(c =>
+ {
+ switch (context.FieldInfo.Type)
+ {
+ case RfcType.DATE:
+ return context.RfcRuntime.GetDateString(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapStringValue(context.FieldInfo, v));
+ case RfcType.TIME:
+ return context.RfcRuntime.GetTimeString(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapStringValue(context.FieldInfo, v));
+ case RfcType.CHAR:
+ case RfcType.NUM:
+ case RfcType.STRING:
+ case RfcType.BCD:
+ case RfcType.FLOAT:
+ case RfcType.DECF16:
+ case RfcType.DECF34:
+ return context.RfcRuntime.GetString(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapStringValue(context.FieldInfo, v));
+ case RfcType.INT:
+ case RfcType.INT2:
+ case RfcType.INT1:
+ return context.RfcRuntime.GetInt(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapIntValue(context.FieldInfo, v));
+ case RfcType.INT8:
+ return context.RfcRuntime.GetLong(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapLongValue(context.FieldInfo, v));
+ case RfcType.BYTE:
+ case RfcType.XSTRING:
+ return context.RfcRuntime.GetBytes(context.Handle, context.FieldInfo.Name).Map(v =>
+ (AbapValue) new AbapByteValue(context.FieldInfo, v));
+
+ default:
+ throw new NotSupportedException(
+ $"Reading a field of RfcType {context.FieldInfo.Type} is not supported for this method.");
+ }
+ }).Bind(abapValue =>
+ {
+ T value = default;
+ foreach (var converter in _converterResolver.GetFromRfcConverters(context.FieldInfo.Type, abapValue.GetType()))
+ {
+ var result = converter.ConvertTo(abapValue)();
+ if (result.IsFaulted)
+ continue;
+ result.IfSucc(v => value = v);
+ break;
+ }
+
+ if (value == null)
+ return new RfcErrorInfo(RfcRc.RFC_CONVERSION_FAILURE, RfcErrorGroup.EXTERNAL_APPLICATION_FAILURE, "",
+ $"Converting from abap type {context.FieldInfo.Type} to type {typeof(T)} is not supported.",
+ "", "E", "", "", "", "", "");
+
+ return Prelude.Right(value);
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs b/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs
new file mode 100644
index 00000000..2731f948
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/DefaultFromAbapValueConverter.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Globalization;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class DefaultFromAbapValueConverter : IFromAbapValueConverter
+ {
+ public Try ConvertTo(AbapValue abapValue)
+ {
+ return Prelude.Try( () => (T) Convert.ChangeType(abapValue, typeof(T), CultureInfo.InvariantCulture));
+ }
+
+ public bool CanConvertTo(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.CHAR:
+ case RfcType.DATE:
+ case RfcType.BCD:
+ case RfcType.TIME:
+ case RfcType.BYTE:
+ case RfcType.NUM:
+ case RfcType.FLOAT:
+ case RfcType.INT:
+ case RfcType.INT2:
+ case RfcType.INT1:
+ case RfcType.DECF16:
+ case RfcType.DECF34:
+ case RfcType.STRING:
+ case RfcType.INT8:
+ return true;
+ default:
+ return false;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/IntValueConverter.cs b/src/YaNco.Core/TypeMapping/IntValueConverter.cs
new file mode 100644
index 00000000..743385f7
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/IntValueConverter.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Globalization;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class IntValueConverter: IToAbapValueConverter
+ {
+ public Try ConvertFrom(T value, RfcFieldInfo fieldInfo)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!IsSupportedRfcType(fieldInfo.Type))
+ throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to integer value.");
+
+ return new AbapIntValue(fieldInfo, (int)Convert.ChangeType(value, typeof(int), CultureInfo.InvariantCulture));
+
+ });
+
+ }
+
+ public bool CanConvertFrom(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ private static bool IsSupportedRfcType(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.INT:
+ case RfcType.INT2:
+ case RfcType.INT1:
+ return true;
+ default:
+ return false;
+ }
+
+ }
+ }
+}
diff --git a/src/YaNco.Core/TypeMapping/LongValueConverter.cs b/src/YaNco.Core/TypeMapping/LongValueConverter.cs
new file mode 100644
index 00000000..3df9e38e
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/LongValueConverter.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Globalization;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class LongValueConverter : IToAbapValueConverter
+ {
+ public Try ConvertFrom(T value, RfcFieldInfo fieldInfo)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!IsSupportedRfcType(fieldInfo.Type))
+ throw new NotSupportedException($"Cannot convert from RfcType {fieldInfo.Type} to long value.");
+
+ return new AbapLongValue(fieldInfo, (long)Convert.ChangeType(value, typeof(long), CultureInfo.InvariantCulture));
+
+ });
+ }
+
+ public bool CanConvertFrom(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ private static bool IsSupportedRfcType(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.INT8:
+ return true;
+ default:
+ return false;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/TypeMapping/StringValueConverter.cs b/src/YaNco.Core/TypeMapping/StringValueConverter.cs
new file mode 100644
index 00000000..a107f1a3
--- /dev/null
+++ b/src/YaNco.Core/TypeMapping/StringValueConverter.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Globalization;
+using LanguageExt;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class StringValueConverter : IToAbapValueConverter
+ {
+ public Try ConvertFrom(T value, RfcFieldInfo fieldInfo)
+ {
+ return Prelude.Try(() =>
+ {
+ if (!IsSupportedRfcType(fieldInfo.Type))
+ throw new NotSupportedException($"Cannot convert string to RfcType {fieldInfo.Type} .");
+
+ string stringValue = null;
+
+ if (value is IConvertible convertible)
+ {
+ switch (convertible.GetTypeCode())
+ {
+ case TypeCode.Boolean:
+ stringValue = Convert.ToBoolean(value, CultureInfo.InvariantCulture) ? "X" : "";
+ break;
+ default:
+ stringValue = (string)Convert.ChangeType(value, typeof(string), CultureInfo.InvariantCulture);
+ break;
+ }
+ }
+
+ return new AbapStringValue(fieldInfo, stringValue);
+
+ });
+ }
+
+ public bool CanConvertFrom(RfcType rfcType)
+ {
+ return IsSupportedRfcType(rfcType);
+ }
+
+ private static bool IsSupportedRfcType(RfcType rfcType)
+ {
+ // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault
+ switch (rfcType)
+ {
+ case RfcType.CHAR:
+ case RfcType.NUM:
+ case RfcType.BCD:
+ case RfcType.FLOAT:
+ case RfcType.DECF16:
+ case RfcType.DECF34:
+ case RfcType.STRING:
+ return true;
+ default:
+ return false;
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Core/YaNco.Core.csproj b/src/YaNco.Core/YaNco.Core.csproj
index e48ff7cd..84724a65 100644
--- a/src/YaNco.Core/YaNco.Core.csproj
+++ b/src/YaNco.Core/YaNco.Core.csproj
@@ -22,7 +22,7 @@ This package contains the core implementation.
-
+
all
runtime; build; native; contentfiles; analyzers
diff --git a/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs b/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs
new file mode 100644
index 00000000..d4a5b835
--- /dev/null
+++ b/src/YaNco.Primitives/TypeMapping/AbapByteValue.cs
@@ -0,0 +1,13 @@
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class AbapByteValue : AbapValue
+ {
+ public readonly byte[] Value;
+
+ public AbapByteValue(RfcFieldInfo fieldInfo, byte[] value) :
+ base(fieldInfo)
+ {
+ Value = value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs b/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs
new file mode 100644
index 00000000..6275abd7
--- /dev/null
+++ b/src/YaNco.Primitives/TypeMapping/AbapIntValue.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class AbapIntValue : AbapValue, IConvertible
+ {
+ public readonly int Value;
+
+ public AbapIntValue(RfcFieldInfo fieldInfo, int value) :
+ base(fieldInfo)
+ {
+ Value = value;
+
+ }
+
+ public TypeCode GetTypeCode()
+ {
+ return Value.GetTypeCode();
+ }
+
+ public bool ToBoolean(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToBoolean(provider);
+ }
+
+ public byte ToByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToByte(provider);
+ }
+
+ public char ToChar(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToChar(provider);
+ }
+
+ public DateTime ToDateTime(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDateTime(provider);
+ }
+
+ public decimal ToDecimal(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDecimal(provider);
+ }
+
+ public double ToDouble(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDouble(provider);
+ }
+
+ public short ToInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt16(provider);
+ }
+
+ public int ToInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt32(provider);
+ }
+
+ public long ToInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt64(provider);
+ }
+
+ public sbyte ToSByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSByte(provider);
+ }
+
+ public float ToSingle(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSingle(provider);
+ }
+
+ public string ToString(IFormatProvider provider)
+ {
+ return Value.ToString(provider);
+ }
+
+ public object ToType(Type conversionType, IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToType(conversionType, provider);
+ }
+
+ public ushort ToUInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt16(provider);
+ }
+
+ public uint ToUInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt32(provider);
+ }
+
+ public ulong ToUInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt64(provider);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs b/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs
new file mode 100644
index 00000000..88de4562
--- /dev/null
+++ b/src/YaNco.Primitives/TypeMapping/AbapLongValue.cs
@@ -0,0 +1,101 @@
+using System;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class AbapLongValue : AbapValue, IConvertible
+ {
+ public readonly long Value;
+
+ public AbapLongValue(RfcFieldInfo fieldInfo, long value) :
+ base(fieldInfo)
+ {
+ Value = value;
+
+ }
+
+ public TypeCode GetTypeCode()
+ {
+ return Value.GetTypeCode();
+ }
+
+ public bool ToBoolean(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToBoolean(provider);
+ }
+
+ public byte ToByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToByte(provider);
+ }
+
+ public char ToChar(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToChar(provider);
+ }
+
+ public DateTime ToDateTime(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDateTime(provider);
+ }
+
+ public decimal ToDecimal(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDecimal(provider);
+ }
+
+ public double ToDouble(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDouble(provider);
+ }
+
+ public short ToInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt16(provider);
+ }
+
+ public int ToInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt32(provider);
+ }
+
+ public long ToInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt64(provider);
+ }
+
+ public sbyte ToSByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSByte(provider);
+ }
+
+ public float ToSingle(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSingle(provider);
+ }
+
+ public string ToString(IFormatProvider provider)
+ {
+ return Value.ToString(provider);
+ }
+
+ public object ToType(Type conversionType, IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToType(conversionType, provider);
+ }
+
+ public ushort ToUInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt16(provider);
+ }
+
+ public uint ToUInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt32(provider);
+ }
+
+ public ulong ToUInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt64(provider);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs b/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs
new file mode 100644
index 00000000..2c600005
--- /dev/null
+++ b/src/YaNco.Primitives/TypeMapping/AbapStringValue.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Globalization;
+
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class AbapStringValue : AbapValue, IConvertible
+ {
+ public readonly string Value;
+
+ public AbapStringValue(RfcFieldInfo fieldInfo, string value) :
+ base(fieldInfo)
+ {
+ Value = value;
+ }
+
+ public TypeCode GetTypeCode()
+ {
+ return Value.GetTypeCode();
+ }
+
+ public bool ToBoolean(IFormatProvider provider)
+ {
+ return !string.IsNullOrWhiteSpace(Value);
+ }
+
+ public byte ToByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToByte(CultureInfo.InvariantCulture);
+ }
+
+ public char ToChar(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToChar(CultureInfo.InvariantCulture);
+ }
+
+ public DateTime ToDateTime(IFormatProvider provider)
+ {
+ return ((IConvertible)Value).ToDateTime(CultureInfo.InvariantCulture);
+ }
+
+ public decimal ToDecimal(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDecimal(CultureInfo.InvariantCulture);
+ }
+
+ public double ToDouble(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToDouble(CultureInfo.InvariantCulture);
+ }
+
+ public short ToInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt16(CultureInfo.InvariantCulture);
+ }
+
+ public int ToInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt32(CultureInfo.InvariantCulture);
+ }
+
+ public long ToInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToInt64(CultureInfo.InvariantCulture);
+ }
+
+ public sbyte ToSByte(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSByte(CultureInfo.InvariantCulture);
+ }
+
+ public float ToSingle(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToSingle(CultureInfo.InvariantCulture);
+ }
+
+ public string ToString(IFormatProvider provider)
+ {
+ return Value.ToString(CultureInfo.InvariantCulture);
+ }
+
+ public object ToType(Type conversionType, IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToType(conversionType, CultureInfo.InvariantCulture);
+ }
+
+ public ushort ToUInt16(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt16(CultureInfo.InvariantCulture);
+ }
+
+ public uint ToUInt32(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt32(CultureInfo.InvariantCulture);
+ }
+
+ public ulong ToUInt64(IFormatProvider provider)
+ {
+ return ((IConvertible) Value).ToUInt64(CultureInfo.InvariantCulture);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/YaNco.Primitives/TypeMapping/AbapValue.cs b/src/YaNco.Primitives/TypeMapping/AbapValue.cs
new file mode 100644
index 00000000..0ebab41b
--- /dev/null
+++ b/src/YaNco.Primitives/TypeMapping/AbapValue.cs
@@ -0,0 +1,12 @@
+namespace Dbosoft.YaNco.TypeMapping
+{
+ public class AbapValue
+ {
+ public readonly RfcFieldInfo FieldInfo;
+
+ protected AbapValue(RfcFieldInfo fieldInfo)
+ {
+ FieldInfo = fieldInfo;
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/SAPSystemTests/Program.cs b/test/SAPSystemTests/Program.cs
index dcee38c8..7e3bbb12 100644
--- a/test/SAPSystemTests/Program.cs
+++ b/test/SAPSystemTests/Program.cs
@@ -5,6 +5,7 @@
using System.Linq;
using System.Threading.Tasks;
using Dbosoft.YaNco;
+using KellermanSoftware.CompareNetObjects;
using LanguageExt;
using Microsoft.Extensions.Configuration;
@@ -41,7 +42,6 @@ static async Task Main(string[] args)
var rows = Convert.ToInt32(config["tests:rows"]);
var repeats = Convert.ToInt32(config["tests:repeats"]);
-
Console.WriteLine($"Test rows: {rows}");
Console.WriteLine($"Test repeats: {repeats}");
@@ -59,14 +59,17 @@ static async Task Main(string[] args)
using (var context = new RfcContext(settings, new SimpleConsoleLogger()))
{
await context.PingAsync();
+
+ await RunIntegrationTests(context);
+
long totalTest1 = 0;
long totalTest2 = 0;
for (var run = 0; run < repeats; run++)
{
- Console.WriteLine($"starting Test Run {run+1} of {repeats}\tTest 01");
+ Console.WriteLine($"starting Test Run {run + 1} of {repeats}\tTest 01");
totalTest1 += await RunPerformanceTest01(context, rows);
- Console.WriteLine($"starting Test Run {run+1} of {repeats}\tTest 02");
+ Console.WriteLine($"starting Test Run {run + 1} of {repeats}\tTest 02");
totalTest2 += await RunPerformanceTest02(context, rows);
GC.Collect();
@@ -81,6 +84,143 @@ static async Task Main(string[] args)
}
}
+ private static async Task RunIntegrationTests(IRfcContext context)
+ {
+ Console.WriteLine("*** BEGIN OF Integration Tests ***");
+
+ await RunIntegrationTest01(context);
+
+ Console.WriteLine("*** END OF Integration Tests ***");
+ }
+ private static async Task RunIntegrationTest01(IRfcContext context)
+ {
+ Console.WriteLine("Integration Tests 01 (I/O field)");
+
+ // ReSharper disable StringLiteralTypo
+ var inputData = new TypesTestData
+ {
+ ACCP = "123456",
+ CHAR = "AB",
+ CLNT = "ABC",
+ CUKY = "ABCDE",
+ CURR = 9.12,
+ DATS = DateTime.MaxValue.Date,
+ DEC = 1.999M,
+ FLTP = +1E-6143,
+ INT1 = 1,
+ INT2 = 32767,
+ INT4 = 2147483647,
+ LANG = "A",
+ LCHR = RandomString(256),
+ LRAW = RandomByteArray(256),
+ NUMC = "123",
+ PREC = 99,
+ QUAN = 99.123,
+ RAW = RandomByteArray(10),
+ SSTRING = RandomString(10),
+ TIMS = default(DateTime).Add(new TimeSpan(23, 59,59)),
+ STRING = "ABCDE",
+ RAWSTRING = RandomByteArray(300),
+ UNIT = "ABC"
+ };
+
+ var outputData = await context.CallFunction("ZYANCO_IT_1",
+ Input: f => f.SetStructure("IS_IN",
+ s => s
+ .SetField("FIELD_ACCP", inputData.ACCP)
+ .SetField("FIELD_CHAR", inputData.CHAR)
+ .SetField("FIELD_CLNT", inputData.CLNT)
+ .SetField("FIELD_CUKY", inputData.CUKY)
+ .SetField("FIELD_CURR", inputData.CURR)
+ .SetField("FIELD_DATS", inputData.DATS)
+ .SetField("FIELD_DEC", inputData.DEC)
+ .SetField("FIELD_FLTP", inputData.FLTP)
+ .SetField("FIELD_INT1", inputData.INT1)
+ .SetField("FIELD_INT2", inputData.INT2)
+ .SetField("FIELD_INT4", inputData.INT4)
+ .SetField("FIELD_LANG", inputData.LANG)
+ .SetField("FIELD_LCHR", inputData.LCHR)
+ .SetField("FIELD_LRAW", inputData.LRAW)
+ .SetField("FIELD_NUMC", inputData.NUMC)
+ .SetField("FIELD_PREC", inputData.PREC)
+ .SetField("FIELD_QUAN", inputData.QUAN)
+ .SetField("FIELD_RAW", inputData.RAW)
+ .SetField("FIELD_SSTRING", inputData.SSTRING)
+ .SetField("FIELD_TIMS", inputData.TIMS)
+ .SetField("FIELD_STRING", inputData.STRING)
+ .SetField("FIELD_RAWSTRING", inputData.RAWSTRING)
+ .SetField("FIELD_UNIT", inputData.UNIT)
+ ),
+ Output: f => f.MapStructure("ES_ECHO", s =>
+ // ReSharper disable InconsistentNaming
+ // ReSharper disable IdentifierTypo
+ from fieldACCP in s.GetField("FIELD_ACCP")
+ from fieldCHAR in s.GetField("FIELD_CHAR")
+ from fieldCLNT in s.GetField("FIELD_CLNT")
+ from fieldCUKY in s.GetField("FIELD_CUKY")
+ from fieldCURR in s.GetField("FIELD_CURR")
+ from fieldDATS in s.GetField("FIELD_DATS")
+ from fieldDEC in s.GetField("FIELD_DEC")
+ from fieldFLTP in s.GetField("FIELD_FLTP")
+ from fieldINT1 in s.GetField("FIELD_INT1")
+ from fieldINT2 in s.GetField("FIELD_INT2")
+ from fieldINT4 in s.GetField("FIELD_INT4")
+ from fieldLANG in s.GetField("FIELD_LANG")
+ from fieldLCHR in s.GetField("FIELD_LCHR")
+ from fieldLRAW in s.GetField("FIELD_LRAW")
+ from fieldNUMC in s.GetField("FIELD_NUMC")
+ from fieldPREC in s.GetField("FIELD_PREC")
+ from fieldQUAN in s.GetField("FIELD_QUAN")
+ from fieldRAW in s.GetField("FIELD_RAW")
+ from fieldSSTRING in s.GetField("FIELD_SSTRING")
+ from fieldTIMS in s.GetField("FIELD_TIMS")
+ from fieldSTRING in s.GetField("FIELD_STRING")
+ from fieldRAWSTRING in s.GetField("FIELD_RAWSTRING")
+ from fieldUNIT in s.GetField("FIELD_UNIT")
+ // ReSharper restore InconsistentNaming
+ // ReSharper restore IdentifierTypo
+
+ select new TypesTestData
+ {
+ ACCP = fieldACCP,
+ CHAR = fieldCHAR,
+ CLNT = fieldCLNT,
+ CUKY = fieldCUKY,
+ CURR = fieldCURR,
+ DATS = fieldDATS,
+ DEC = fieldDEC,
+ FLTP = fieldFLTP,
+ INT1 = fieldINT1,
+ INT2 = fieldINT2,
+ INT4 = fieldINT4,
+ LANG = fieldLANG,
+ LCHR = fieldLCHR,
+ LRAW = fieldLRAW,
+ NUMC = fieldNUMC,
+ PREC = fieldPREC,
+ QUAN = fieldQUAN,
+ RAW = fieldRAW,
+ SSTRING = fieldSSTRING,
+ TIMS = fieldTIMS,
+ STRING = fieldSTRING,
+ RAWSTRING = fieldRAWSTRING,
+ UNIT = fieldUNIT
+
+ })).Match(
+ r => r,
+ l =>
+ {
+ Console.WriteLine(l.Message);
+ return new TypesTestData();
+ });
+ // ReSharper restore StringLiteralTypo
+
+
+ CompareLogic compareLogic = new CompareLogic();
+ var result = compareLogic.Compare(inputData, outputData);
+
+ Console.WriteLine(!result.AreEqual ? result.DifferencesString : "Test succeed");
+ }
private static async Task RunPerformanceTest01(IRfcContext context, int rows=0)
{
var watch = Stopwatch.StartNew();
@@ -135,8 +275,26 @@ private static Either SetRows(Either s[random.Next(s.Length)]).ToArray());
+ }
+
+ private static byte[] RandomByteArray(int size)
+ {
+ var rnd = new Random();
+ var b = new byte[size]; // convert kb to byte
+ rnd.NextBytes(b);
+ return b;
+ }
}
+
+
public class TestData
{
public string Char40 { get; set; }
@@ -145,5 +303,39 @@ public class TestData
public string String { get; set; }
+ }
+
+ public class TypesTestData
+ {
+ // ReSharper disable InconsistentNaming
+ public string ACCP { get; set; }
+ public string CHAR { get; set; }
+ public string CLNT { get; set; }
+ public string CUKY { get; set; }
+ public double CURR { get; set; }
+ public DateTime DATS { get; set; }
+ public decimal DEC { get; set; }
+ public double FLTP { get; set; }
+ public int INT1 { get; set; }
+ public short INT2 { get; set; }
+ public int INT4 { get; set; }
+ public string LANG { get; set; }
+ public string LCHR { get; set; }
+ public byte[] LRAW { get; set; }
+ public string NUMC { get; set; }
+ public int PREC { get; set; }
+ public double QUAN { get; set; }
+
+ public byte[] RAW { get; set; }
+ public string SSTRING { get; set; }
+ public DateTime TIMS { get; set; }
+ public string STRING { get; set; }
+ public byte[] RAWSTRING { get; set; }
+ public string UNIT { get; set; }
+
+ // ReSharper restore InconsistentNaming
+
+
+
}
}
\ No newline at end of file
diff --git a/test/SAPSystemTests/SAPSystemTests.csproj b/test/SAPSystemTests/SAPSystemTests.csproj
index feddb9ff..d31ffbf7 100644
--- a/test/SAPSystemTests/SAPSystemTests.csproj
+++ b/test/SAPSystemTests/SAPSystemTests.csproj
@@ -10,6 +10,7 @@
+