Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle multiple identical classes in C#

Tags:

c#

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?

like image 934
zacaj Avatar asked May 23 '18 15:05

zacaj


3 Answers

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); }
    
like image 52
InBetween Avatar answered Nov 17 '22 13:11

InBetween


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);
like image 7
p e p Avatar answered Nov 17 '22 13:11

p e p


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);
like image 4
Optional Option Avatar answered Nov 17 '22 12:11

Optional Option