I'm working with a client's API that has multiple different 'services' (around 10 so far), each one imports as its own namespace. Part of their standard API call pattern involves returning an array of error messages:
public class Error {
public String ErrorMessage {get;set}
public int errorNumber {get;set}
..etc
}
I've been trying to clean up and unify our handling of those messages. I tried to make a single function to handle them, eg:
void CheckErrors(List<Error> errors) {
if(errors != null && errors.Count() > 0)
throw new Exception(errors.First().ErrorMessage);
}
(the actual function is more complex but that gives the general idea)
However, it turns out that every one of their services has its own (identical) definition of this Error class. In C++ I could just template this function and it would work fine, or in a dynamic language it'd just work, but in C# I haven't been able to find a way to do this without making 10+ copies of the same function, each with a different namespace on the Error type.
In my own code I could just add an interface to these classes, but since it's not my code I don't think you can do that in C#? I could make a wrapper class that inherits from each of these and implements the interface, but I'm still stuck with duplicate code for every namespace/service.
Is there any cleaner way to handle this?
You could consider using a late binding solution either using reflection or dynamic
. Both have the same drawback: you loose compile time type safety but if its a very isolated and contained piece of code it should be tolerable:
Reflection
void CheckErrors(List<object> errors) {
if(errors != null && errors.Count() > 0)
{
var firstError = errors.First();
throw new Exception(
firstError.GetType()
.GetProperty("ErrorMessage")
.GetValue(firstError)
.ToString()); }
Dynamic
void CheckErrors(List<dynamic> errors) {
if(errors != null && errors.Count() > 0)
throw new Exception(errors.First().ErrorMessage); }
Bear with me... you may need yet another Error
class that is identical in properties to their Error
class, but that you define in your namespace.
Your method CheckErrors
uses your definition of Error
.
Finally, you can use AutoMapper to map between their Error
types and yours. This is pretty much exactly what AutoMapper is designed for. Since all your contracts are identical, the AutoMapper configuration should be trivial. Of course, you incur some runtime expense of mapping, but I think this would lead to the cleanest statically typed solution given that you can't change their interfaces.
The AutoMapper config + usage will look something like this:
//See AutoMapper docs for where to put config, it shouldn't happen on every call
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TheirApi.Error, MyNamespace.MyErrorDefinition>();
}
var mapper = config.CreateMapper();
MyErrorDefinition myErrors = mapper.Map<List<MyErrorDefinition>>(listOfTheirErrorObjects);
CheckErrors(myErrors);
Another way is to use lambdas:
void CheckErrors<T>(IEnumerable<T> errors, Func<T,string> getMessage)
{
if (errors?.Count() > 0) throw new Exception(getMessage(errors.First()));
}
Then call it like this:
CheckErrors(errors, (e) => e.ErrorMessage);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With