-
I've checked samples in project and SAPHub project too. Maybe I've missed something, but I cant get how to generate conditional input parameters My example works on two conditions, but crashing (program crash in debug run and even try-catch didn't help to get exception) on more than two. Expression<Func<Either<RfcErrorInfo, IFunction>, Either<RfcErrorInfo, IFunction>>> inputQueryExpr = null;
Func<Either<RfcErrorInfo, IFunction>, Either<RfcErrorInfo, IFunction>> compiled = null;
if (query.Some1)
{
if (inputQueryExpr != null)
compiled = inputQueryExpr.Compile();
inputQueryExpr = compiled != null ?
x => compiled(x).SetField("Some1", 1) :
x => x.SetField("Some1", 1);
}
...
if (query.SomeN)
{
if (inputQueryExpr != null)
compiled = inputQueryExpr.Compile();
inputQueryExpr = compiled != null ?
x => compiled(x).SetField("SomeN", 1) :
x => x.SetField("SomeN", 1);
}
var calling = await rfc.CallFunction("FUNC_1",
Input: inputQueryExpr.Compile(),
Output: f => ...
)
.IfLeftAsync(l => throw new Exception(l.Message)); |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
Hi, Could you perhaps help me understand your use case by giving me some more details? Why are you trying to call a function with a compiled expression? Best Regards, |
Beta Was this translation helpful? Give feedback.
-
Just trying to reduce transfer and make more readable input parameters. Now it looks like var calling = await rfc.CallFunction("FUNC_1",
Input: x => x.SetField("Some1", query.Some1 ? 1 : 0)
...
.SetField("SomeN", query.SomeN ? 1 : 0).
Output: f => ...
)
.IfLeftAsync(l => throw new Exception(l.Message)); In real situation there are complex tables and complex conditions, not just .SetFields. Trying to make some kind of query builder. |
Beta Was this translation helpful? Give feedback.
-
Ok, understood. Many functions (and all BAPIs) in SAP handles a empty value like a unset value. So normally you just have to set a value to a empty string if it is unset. But sometimes values depend on each other - then in most cases simple conditions could be applied. return _rfcContext.CallFunction("BAPI_QUALNOT_CREATE",
f => f
.SetField("NOTIF_TYPE", message.NotificationType)
// header
.SetStructure("NOTIFHEADER", s =>
from sCodeGroup in s.SetField("CODE_GROUP", message.CodeGroup ?? "")
from sCode in s.SetField("CODE", message.Code ?? "")
from sTxt in s.SetField("SHORT_TEXT", message.Text ?? "")
from sPriority in s.SetField("PRIORITY", message.PriorityKey ?? ' ')
from sStartDate in message.StartDateTime.HasValue
? s.SetField("STARTDATE", message.StartDateTime.GetValueOrDefault())
.SetField("DESSTTIME", message.StartDateTime.GetValueOrDefault())
: s
from sEndDate in message.EndDateTime.HasValue
? s.SetField("ENDDATE", message.EndDateTime.GetValueOrDefault())
.SetField("DESENDTM", message.EndDateTime.GetValueOrDefault())
: s
select s) To make this method a more readable you can extract the SetStructure call and the date conditions like this: private static Either<RfcErrorInfo, T> SetDateTime<T>(Either<RfcErrorInfo, T> dc,
string dateField, string timeField, DateTime? optionalDateTime)
where T: IDataContainer
{
return optionalDateTime.HasValue
? dc.SetField(dateField, optionalDateTime.GetValueOrDefault())
.Bind(_ => dc.SetField(timeField, optionalDateTime.GetValueOrDefault()))
: dc;
}
private Either<RfcErrorInfo, IFunction> SetNotificationHeader(Either<RfcErrorInfo, IFunction> function, CreateSAPNotification message)
{
return function.SetStructure("NOTIFHEADER", s =>
from sCodeGroup in s.SetField("CODE_GROUP", message.CodeGroup ?? "")
from sCode in s.SetField("CODE", message.Code ?? "")
from sTxt in s.SetField("SHORT_TEXT", message.Text ?? "")
from sPriority in s.SetField("PRIORITY", message.PriorityKey ?? ' ')
// conditionally set the date and time
from sStartDate in SetDateTime(s, "STARTDATE", "DESSTTIME", message.StartDateTime)
from sEndDate in SetDateTime(s, "ENDDATE", "DESENDTM", message.EndDateTime)
select s);
}
return _rfcContext.CallFunction("BAPI_QUALNOT_CREATE",
f => f
.SetField("NOTIF_TYPE", message.NotificationType)
// set headers using Apply - that just passes the input to a function for chaining
.Apply(func=> SetNotificationHeader(func, message)) There are many more helpers in Language.Ext to ease this kind of function chaining and functional programming in C#. For your query builder you can also define a complete generic method to set values, e.g. from a dictionary. static class DataContainerExtensions
{
public static Either<RfcErrorInfo, TDC> SetFromDictionary<TDC>(this Either<RfcErrorInfo, TDC> dataContainer,
IDictionary<string, string> dictionary)
where TDC : IDataContainer
{
return dictionary.Map(queryField => dataContainer.SetField(queryField.Key, queryField.Value))
.Traverse(t => t)
.Bind(_ => dataContainer);
}
} As this is generic on IDataContainer you can use this extension on functions and structures: var dictionary = new Dictionary<string, string>();
return _rfcContext.CallFunction("FUNC", f => f
.SetFromDictionary(dictionary) Hope this gives you some general ideas. Please note that we also offer commercial support for this kind of topics. If you are interested, please drop us a message at [email protected] for details and a quote. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the detailed answer, sensible examples and your time. I'll see how to implement all of this ideas. Unfortunately, it is currently unrealistic to receive any additional funding for all SAP-related projects. |
Beta Was this translation helpful? Give feedback.
Ok, understood.
Many functions (and all BAPIs) in SAP handles a empty value like a unset value. So normally you just have to set a value to a empty string if it is unset. But sometimes values depend on each other - then in most cases simple conditions could be applied.
Here a copy of some production code of us to create a notification in SAP with the BAPI BAPI_QUALNOT_CREATE. StartDateTime is set conditional in this sample and for other optional fields fields we just set the default: