Reputation: 1258
Okay, to show what my code looks like (and this works, but isn't necessarily pretty):
public delegate Response Func<R1>(ref R1 out1);
public delegate Response Func<T1, R1>(T1 in1, ref R1 out1);
public delegate Response Func<T1, T2, R1>(T1 in1, T2 in2, ref R1 out1);
public delegate Response Func<T1, T2, T3, R1>(T1 in1, T2 in2, T3 in3, ref R1 out1);
public delegate Response Func<T1, T2, T3, T4, R1>(T1 in1, T2 in2, T3 in3, T4 in4, ref R1 out1);
public delegate Response Func<T1, T2, T3, T4, T5, R1>(T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, ref R1 out1);
public delegate Response Func<T1, T2, T3, T4, T5, T6, R1>(T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1);
public static Response Query<R1>(Func<R1> method, ref R1 out1)
{
return QueryAll<object, object, object, object, object, object, R1>(method, null, null, null, null, null, null, ref out1);
}
public static Response Query<T1, R1>(Func<T1, R1> method, T1 in1, ref R1 out1)
{
return QueryAll<T1, object, object, object, object, object, R1>(method, in1, null, null, null, null, null, ref out1);
}
public static Response Query<T1, T2, R1>(Func<T1, T2, R1> method, T1 in1, T2 in2, ref R1 out1)
{
return QueryAll<T1, T2, object, object, object, object, R1>(method, in1, in2, null, null, null, null, ref out1);
}
public static Response Query<T1, T2, T3, R1>(Func<T1, T2, T3, R1> method, T1 in1, T2 in2, T3 in3, ref R1 out1)
{
return QueryAll<T1, T2, T3, object, object, object, R1>(method, in1, in2, in3, null, null, null, ref out1);
}
public static Response Query<T1, T2, T3, T4, R1>(Func<T1, T2, T3, T4, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, ref R1 out1)
{
return QueryAll<T1, T2, T3, T4, object, object, R1>(method, in1, in2, in3, in4, null, null, ref out1);
}
public static Response Query<T1, T2, T3, T4, T5, R1>(Func<T1, T2, T3, T4, T5, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, ref R1 out1)
{
return QueryAll<T1, T2, T3, T4, T5, object, R1>(method, in1, in2, in3, in4, in5, null, ref out1);
}
public static Response Query<T1, T2, T3, T4, T5, T6, R1>(Func<T1, T2, T3, T4, T5, T6, R1> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
{
return QueryAll<T1, T2, T3, T4, T5, T6, R1>(method, in1, in2, in3, in4, in5, in6, ref out1);
}
private static Response QueryAll<T1, T2, T3, T4, T5, T6, R1>(Delegate method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
{
try
{
Response response = null;
// Test if the method's class implements ICacheable
if (method.GetType() is ICacheable)
{
// Try to get the value from the cache if available
out1 = ((ICacheable)method.Target).Get<R1>(out1);
// If not null, return the value and exit
if (out1 != null)
return null;
else
{
// Value is null, but should be cached, so attempt to load to cache and return it
if (in6 != null)
response = ((Func<T1, T2, T3, T4, T5, T6, R1>)method)(in1, in2, in3, in4, in5, in6, ref out1);
else if (in5 != null)
response = ((Func<T1, T2, T3, T4, T5, R1>)method)(in1, in2, in3, in4, in5, ref out1);
else if (in4 != null)
response = ((Func<T1, T2, T3, T4, R1>)method)(in1, in2, in3, in4, ref out1);
else if (in3 != null)
response = ((Func<T1, T2, T3, R1>)method)(in1, in2, in3, ref out1);
else if (in2 != null)
response = ((Func<T1, T2, R1>)method)(in1, in2, ref out1);
else if (in1 != null)
response = ((Func<T1, R1>)method)(in1, ref out1);
else
response = ((Func<R1>)method)(ref out1);
// If value from database is not null, save it in cache
if (out1 != null)
((ICacheable)method.Target).Set<R1>(out1);
return response;
}
}
else
{
// Get data from database
if (in6 != null)
response = ((Func<T1, T2, T3, T4, T5, T6, R1>)method)(in1, in2, in3, in4, in5, in6, ref out1);
else if (in5 != null)
response = ((Func<T1, T2, T3, T4, T5, R1>)method)(in1, in2, in3, in4, in5, ref out1);
else if (in4 != null)
response = ((Func<T1, T2, T3, T4, R1>)method)(in1, in2, in3, in4, ref out1);
else if (in3 != null)
response = ((Func<T1, T2, T3, R1>)method)(in1, in2, in3, ref out1);
else if (in2 != null)
response = ((Func<T1, T2, R1>)method)(in1, in2, ref out1);
else if (in1 != null)
response = ((Func<T1, R1>)method)(in1, ref out1);
else
response = ((Func<R1>)method)(ref out1);
return response;
}
}
catch (Exception exc)
{
CustomException exception = exc.ToCustomException();
exception.Code = ResponseCodes.UnknownError;
throw exception;
}
}
This is the data abstraction layer. Again, my question is I want to allow a developer to pass in a method and up to 6 parameters. But, I only want one main method to contain all of my logic so that it's easier to maintain. Then, based on certain conditions (is the object in cache or not), call a method on the data layer to read in the object from the repository, store in cache, then return object to the controller.
Is there a better way to do this than multiple if/else statements like below?
Upvotes: 1
Views: 128
Reputation: 1258
Thanks @ohmusama! I tweaked your code a bit because I needed to still use the returned reference parameter in my Generic query method, but you helped a ton!
Below is my final answer with one overloaded method:
public static Response Query<T1, T2, T3, T4, T5, T6, R1>(Func<T1, T2, T3, T4, T5, T6, Tuple<Response, R1>> method, T1 in1, T2 in2, T3 in3, T4 in4, T5 in5, T6 in6, ref R1 out1)
{
return QueryAll(() => method(in1, in2, in3, in4, in5, in6), ref out1);
}
private static Response QueryAll<R1>(Func<Tuple<Response, R1>> method, ref R1 out1)
{
try
{
Tuple<Response, R1> result;
// Test if the method's class implements ICacheable
if (method.GetType() is ICacheable)
{
// Try to get the value from the cache if available
out1 = ((ICacheable)method.Target).Get<R1>(out1);
// If not null, return the value and exit
if (out1 != null)
return null;
else
{
// Value is null, but should be cached, so attempt to load to cache and return it
result = method();
out1 = result.Item2;
// If value from database is not null, save it in cache
if (out1 != null)
((ICacheable)method.Target).Set<R1>(out1);
return result.Item1;
}
}
else
{
// Get data from database
result = method();
out1 = result.Item2;
return result.Item1;
}
}
catch (Exception exc)
{
CustomException exception = exc.ToCustomException();
exception.Code = ResponseCodes.UnknownError;
throw exception;
}
}
Upvotes: 0
Reputation: 4215
You could have your business logic contained in another method, that has an argument for the method you want to call
public static Response Query<R1>(Func<Tuple<Result, R1>> method, ref R1 @out)
{
Tuple<Result, R1> result = Logic(() => method());
@out = result.Item2;
return result.Item1;
}
public static Response Query<T1, R1>(Func<T1, Tuple<Result, R1>> method, T1 a, ref R1 @out)
{
Tuple<Result, R1> result = Logic(() => method(a));
@out = result.Item2;
return result.Item1;
}
public static Response Query<T1, T2, R1>(Func<T1, T2, Tuple<Result, R1>> method, T1 a, T2 b, ref R1 @out)
{
Tuple<Result, R1> result = Logic(() => method(a, b));
@out = result.Item2;
return result.Item1;
}
...
public static Tuple<Result, R1> Logic<R1>(Func<Tuple<Result, R1>> doMethod)
{
Tuple<Result, R1> result;
// logic
if(true) { result = doMethod(); }
...
// watch out if this doesn't get assigned, can cause problems downstream
return result;
}
Upvotes: 2
Reputation: 473
What if instead putting T1, T2, and the others as separate parameters you would put them in a list? List<object>
You would get something similar to this:
public static Response Query<L1,R1>(L1 in1, ref R1 out1)
{
// The SAME business logic with the same if/then statements
if (true)
out1 = method(in1, in2);
else
// Some other business logic
}
There would be no need to pass method
as a parameter as it would always take the same list and the same number of parameters.
Then you'd perform the same operations in method
by checking in1.Count
and the type of each individual item: if (typeof(in1[0]) == T1) { ... }
Upvotes: 0