Home > Mobile >  C# Repeating Logic for Multiple Object Types, candidate for generics?
C# Repeating Logic for Multiple Object Types, candidate for generics?

Time:12-30

I have multiple GetResponse functions in my code, each differs by the return type, single parameter passed and a single line of code calling out to the SOAP interface. Currently using object overload design.

I'm not sure how to make into generic method or if it makes sense. I'm going for the DRY principle here.

I cannot alter the object types as they are auto built with the connected service types (WSDL).

I suspect that I'll need to somehow flag which SOAP call to use when calling the method.

Any help here would be much appreciated.

Here are two sample definitions:

    private GetAccountInfoResponse GetResponse(GetAccountInfoRequest req) {...}

    private CleanseAddressResponse GetResponse(CleanseAddressRequest req) {...}

Here is the full code for GetAccountInfos:

    private GetAccountInfoResponse GetResponse(GetAccountInfoRequest req)
    {
      MethodBase m = MethodBase.GetCurrentMethod();
      string source = $"{m.ReflectedType.Name}.{m.Name}";

      SwsimV111Soap swsim = swsimFactory.CreateChannel();
      GetAccountInfoResponse resp = null;
      int retry = 0;

      if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information)
        ReportStatus(LogLevels.Debug, source, $"SWSIM req: {SupportClasses.XmlConvert.SerializeObject(req)}");

      while (resp == null && retry < 1)
      {
        try
        {
          resp = swsim.GetAccountInfo(req); //<= call changes with each (req) type
          _authToken = resp.Authenticator;
        }
        catch (FaultException ex)
        {
          if (ex.Message.ToLower().Contains("converstion out-of-sync"))
          {
            ClearAuthState();
            req.Item = GetCredsType();
          }
          else
          {
            msg = $"Call Fault: {Misc.ParseException(ex)}";
            break; //Force end of loop
          }
        } // catch (FaultException ex)
        catch (Exception ex)
        {
          msg = $"Call Error: {Misc.ParseException(ex)}";
          break; //Force end of loop
        }
        retry  ;
      } //while (resp == null && retry < 2)

      if (resp != null)
      {
        if (Logging.GetLevelFromString(ConfigSettings.LogLevel) > LogLevels.Information)
          ReportStatus(LogLevels.Debug, source, $"SWSIM resp: {SupportClasses.XmlConvert.SerializeObject(resp)}");
      }
      else ReportStatus(LogLevels.Error, source, $"Fault Exception: {msg}");
      return resp;
    }

CodePudding user response:

The typical solution is to instead define an interface and have each of these object types implement that interface.

CodePudding user response:

You can use a combination of generics and using a callback to do the actual method call.

Given the following type declarations:

public class Request {}
public class Response {}

public class GetAccountInfoResponse : Response { }
public class CleanseAddressResponse : Response { }
public class GetAccountInfoRequest : Request { }
public class CleanseAddressRequest: Request { }

public class SwsimV111Soap
{
    public GetAccountInfoResponse GetAccountInfo(GetAccountInfoRequest req) => new GetAccountInfoResponse();

    public CleanseAddressResponse GetCleanseAddress(CleanseAddressRequest req) => new CleanseAddressResponse();
}

This is how you could write your method, leaving out all the irrelevant stuff that you can add in:

private TResp GetResponse<TReq, TResp>(TReq req, Func<SwsimV111Soap, TReq, TResp> func) where TReq : Request where TResp : Response
{
    SwsimV111Soap swsim = new SwsimV111Soap(); // Use your factory here
    // Do the actual call using the passed callback function
    TResp resp = func(swsim, req);

    return resp;
}

We use generic types for the request type and the response type. Then we also expect as an argument a function that operates on the service type and also uses the request and response types.

Inside the method we invoke the function with the service instance and the request instance and it will return the response instance.

Usage like so:

private void button1_Click(object sender, EventArgs e)
{
    GetAccountInfoResponse resp1 = GetResponse(new GetAccountInfoRequest(), (s, r) => s.GetAccountInfo(r));
    CleanseAddressResponse resp2 = GetResponse(new CleanseAddressRequest(), (s, r) => s.GetCleanseAddress(r));
}

We can use the lambda expression syntax to easily create the expected function that performs the actual call.

  • Related