I have multiple services with a lot of methods. Every method returns an int
as a result code. All methods have 2 types of signatures:
- Only with
IN
params - n of
IN
params and mOUT
params in the end (in most cases 1OUT
param)
Up until now I came up with this solution:
#region Decorators
#region [Getters]
#region [Void]
protected delegate int KeeperAPIDelegateVoid<T1>(string sessionID, T1 obj1);
protected delegate int KeeperAPIDelegateVoid<T1, T2>(string sessionID, T1 obj1, T2 obj2);
protected void DataGrabber<T1>(KeeperAPIDelegateVoid<T1> action, params object[] args)
{
int result = action.Invoke(SessionID, (T1)args[0]);
if (result != 0) throw HandleError(result);
}
protected void DataGrabber<T1, T2>(KeeperAPIDelegateVoid<T1, T2> action, params object[] args)
{
int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1]);
if (result != 0) throw HandleError(result);
}
#endregion
#region [Typed]
protected delegate int KeeperAPIDelegate<TReturn>(string sessionID, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, TReturn>(string sessionID, T1 obj1, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, TReturn>(string sessionID, T1 obj1, T2 obj2, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, TReturn>(string sessionID, T1 obj1, T2 obj2, T3 obj3, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, T4, T5, TReturn>(string sessionID, T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, out TReturn rObj);
protected delegate int KeeperAPIDelegate<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3>(string sessionID, T1 obj1, T2 obj2, T3 obj3, T4 obj4, T5 obj5, T6 obj6, out TReturn1 rObj1, out TReturn2 rObj2, out TReturn3 rObj3);
protected TReturn DataGrabber<TReturn>(KeeperAPIDelegate<TReturn> action)
{
TReturn data;
int result = action.Invoke(SessionID, out data);
if (result == 0) return data;
throw HandleError(result);
}
protected TReturn DataGrabber<T1, TReturn>(KeeperAPIDelegate<T1, TReturn> action, params object[] args)
{
TReturn data;
int result = action.Invoke(SessionID, (T1)args[0], out data);
if (result == 0) return data;
throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, TReturn>(KeeperAPIDelegate<T1, T2, TReturn> action, params object[] args)
{
TReturn data;
int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], out data);
if (result == 0) return data;
throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, T3, TReturn>(KeeperAPIDelegate<T1, T2, T3, TReturn> action, params object[] args)
{
TReturn data;
int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], out data);
if (result == 0) return data;
throw HandleError(result);
}
protected TReturn DataGrabber<T1, T2, T3, T4, T5, TReturn>(KeeperAPIDelegate<T1, T2, T3, T4, T5, TReturn> action, params object[] args)
{
TReturn data;
int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], out data);
if (result == 0) return data;
throw HandleError(result);
}
protected Tuple<TReturn1, TReturn2, TReturn3> DataGrabber<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3>(KeeperAPIDelegate<T1, T2, T3, T4, T5, T6, TReturn1, TReturn2, TReturn3> action, params object[] args)
{
TReturn1 data1;
TReturn2 data2;
TReturn3 data3;
int result = action.Invoke(SessionID, (T1)args[0], (T2)args[1], (T3)args[2], (T4)args[3], (T5)args[4], (T6)args[5], out data1, out data2, out data3);
if (result == 0) return new Tuple<TReturn1, TReturn2, TReturn3>(data1, data2, data3);
throw HandleError(result);
}
#endregion
#endregion
I call my methods like:
public SelectOptionDesc[] GetContractorFieldOptions(long contractorId, long fieldId)
{
return DataGrabber<long, long, SelectOptionDesc[]>(TelepayClient.GetTelepayContractorFieldSelectOptions, new object[] { contractorId, fieldId });
}
Is it an OK solution or is there a better way? Modifying services is not an option, because it's a 3rd party service and I don't have access.
-
1\$\begingroup\$ You probably should have a look at Aspect Oriented Programming. There are a bunch of AOP frameworks (such as postsharp) around which let you do this kind of decoration with a lot less coding work and in a much more generic fashion. \$\endgroup\$ChrisWue– ChrisWue2016年08月10日 08:39:02 +00:00Commented Aug 10, 2016 at 8:39
-
\$\begingroup\$ Thx. I thought about this. But mostly about T4 templates. PostSharp - interesting idea. Thx! \$\endgroup\$Kindzoku– Kindzoku2016年08月10日 08:40:13 +00:00Commented Aug 10, 2016 at 8:40
-
\$\begingroup\$ Is it correct, that the decorator only checks the error code and throws the corresponding exception if needed or is it just a simplified example? \$\endgroup\$JanDotNet– JanDotNet2016年08月10日 09:10:12 +00:00Commented Aug 10, 2016 at 9:10
-
\$\begingroup\$ @JanDotNet, it's checking and processing the error (ok) code, but it should return value as well. It's Ctrl+C, Ctrl+V code. \$\endgroup\$Kindzoku– Kindzoku2016年08月10日 09:14:39 +00:00Commented Aug 10, 2016 at 9:14
-
\$\begingroup\$ @Kindzoku: Ok, then it should be possible to throw the exception direct in the service method (instead of returning an error code). That way, you could call the service methods directly and remove the whole decorator code, right? That would simplify the code dramatically. \$\endgroup\$JanDotNet– JanDotNet2016年08月10日 10:02:09 +00:00Commented Aug 10, 2016 at 10:02
1 Answer 1
If the service methods are not changeable, I don't see a solution that is fundamentally better then yours...
- If there are only one or a few methods with 3 return values, consider to create more descriptive custom types for the return values (instead of using
Tuple<T1, T2, T3>
). - The check for
(result == 0)
is implemented for each method. You could do that within the methodHandleError
. However, that requires, thatHandleError
does not return the exception but throws it directly if result is != 0.
-
\$\begingroup\$ As far as I know,
out
parameter in Func<> is return value, not actual out property. So no, unfortunately I can't use Func. And Action is void, so no use for me too. \$\endgroup\$Kindzoku– Kindzoku2016年08月10日 13:23:11 +00:00Commented Aug 10, 2016 at 13:23 -
\$\begingroup\$ right....I'll remove that point from my answer ;). \$\endgroup\$JanDotNet– JanDotNet2016年08月10日 13:26:29 +00:00Commented Aug 10, 2016 at 13:26
-
\$\begingroup\$ 1st point - not sure if I need them more descriptive + I haven't seen all methods yet ;D. 2nd point - a good one. Thx. \$\endgroup\$Kindzoku– Kindzoku2016年08月10日 13:28:15 +00:00Commented Aug 10, 2016 at 13:28