1

I have a .NET Service Reference that I'd like to encapsulate into a single, reusable class.

I typical call looks something like this:

// instantiate the api and set credentials
ApiClient api = new ApiClient();
api.Credentials.UserName.UserName = "blah";
api.Credentials.UserName.Password = "blahblah";

// operation-specific search parameter object
SomethingSearch search = new SomethingSearch();
search.Key = "blah";

Result[] result = api.GetSomething(search);

api.Close();

Other calls vary in both the operation called and the search parameter object.

The thing is, I don't know how to pass into the class both the name of the API operation (i.e. GetSomething() and the operation-specific search object (SomethingSearch).

How might I accomplish this? I'm not asking for the work to be done for me, but I'm not sure where to begin. I believe it has something to do with Func<T> and delegates but I'm embarrassingly inexperienced with them.

Chad Levy
  • 10,032
  • 7
  • 41
  • 69
  • 1
    There are many patterns available for handling this all over the internet, you just have to make sure that you appropriately close your services and don't make the [mistake of utilizing the using statement](http://msdn.microsoft.com/en-us/library/aa355056.aspx). – Jaime Torres Jul 10 '12 at 22:37
  • This [Encapsulating a service](http://stackoverflow.com/a/6020625/1257607) may help as well. But is more to encapsulate the whole thing. – DanielV Sep 09 '16 at 12:24

2 Answers2

3

A colleague of mine developed this solution:

/// <summary>
/// Proxy for executing generic service methods
/// </summary>
public class ServiceProxy
{
    /// <summary>
    /// Execute service method and get return value
    /// </summary>
    /// <typeparam name="C">Type of service</typeparam>
    /// <typeparam name="T">Type of return value</typeparam>
    /// <param name="action">Delegate for implementing the service method</param>
    /// <returns>Object of type T</returns>
    public static T Execute<C, T>(Func<C, T> action) where C : class, ICommunicationObject, new()
    {
        C svc = null;

        T result = default(T);

        try
        {
            svc = new C();

            result = action.Invoke(svc);

            svc.Close();
        }
        catch (FaultException ex)
        {
            // Logging goes here
            // Service Name: svc.GetType().Name
            // Method Name: action.Method.Name
            // Duration: You could note the time before/after the service call and calculate the difference
            // Exception: ex.Reason.ToString()

            if (svc != null)
            {
                svc.Abort();
            }

            throw;
        }
        catch (Exception ex)
        {
            // Logging goes here

            if (svc != null)
            {
                svc.Abort();
            }

            throw;
        }

        return result;
    }
}

And an example of its use:

public class SecurityServiceProxy
{

    public static UserInformation GetUserInformation(Guid userId)
    {
        var result = ServiceProxy.Execute<MySecurityService, UserInformation>
        (
            svc => svc.GetUserInformation(userId)
        );

        return result;
    }

    public static bool IsUserAuthorized(UserCredentials creds, ActionInformation actionInfo)
    {
        var result = ServiceProxy.Execute<MySecurityService, bool>
        (
            svc => svc.IsUserAuthorized(creds, actionInfo)
        );

        return result;
    }
 }

In this fake case, we are using two methods fromMySecurityService, GetUserInformation and IsUserAuthorized. GetUserInformation takes a Guid as an argument and returns a UserInformation object. IsUserAuthorized takes a UserCredentials and ActionInformation object, and returns a bool whether or not the user is authorized.

This proxy is also a perfect place to cache cacheable service call results.

If you need to send parameters to the server, there may be a more generic way of doing so, but I think you'd need to create a specific proxy for it. Example:

public interface ISecuredService
{
   public UserCredentials Credentials { get; set; }
}

/// <summary>
/// Proxy for executing generic UserCredentials  secured service methods
/// </summary>
public class SecuredServiceProxy
{
    /// <summary>
    /// Execute service method and get return value
    /// </summary>
    /// <typeparam name="C">Type of service</typeparam>
    /// <typeparam name="T">Type of return value</typeparam>
    /// <param name="credentials">Service credentials</param>
    /// <param name="action">Delegate for implementing the service method</param>
    /// <returns>Object of type T</returns>
    public static T Execute<C, T>(UserCredentials credentials, Func<C, T> action) where C : class, ICommunicationObject, ISecuredService, new()
    {
        C svc = null;

        T result = default(T);

        try
        {
            svc = new C();
            svc.Credentials = credentials;

            result = action.Invoke(svc);

            svc.Close();
        }
        catch (FaultException ex)
        {
            // Logging goes here
            // Service Name: svc.GetType().Name
            // Method Name: action.Method.Name
            // Duration: You could note the time before/after the service call and calculate the difference
            // Exception: ex.Reason.ToString()

            if (svc != null)
            {
                svc.Abort();
            }

            throw;
        }
        catch (Exception ex)
        {
            // Logging goes here

            if (svc != null)
            {
                svc.Abort();
            }

            throw;
        }

        return result;
    }
}
Jaime Torres
  • 10,365
  • 1
  • 48
  • 56
  • Curiously, how would you pass a parameter into the proxy? In my specific case, `svc` requires an object that contains Username and Password. – Chad Levy Sep 14 '12 at 00:00
  • @Paperjam I've updated to include a more concrete example of how to pass various arguments to service method calls. Please let me know if you would like further clarification. – Jaime Torres Sep 14 '12 at 01:20
  • In your example, the `UserCredentials` are passed as a parameter for `IsUserAuthorized`. In my specific case, `UserCredentials` are a parameter for `svc`. – Chad Levy Sep 14 '12 at 01:36
  • @Paperjam In the last update I made, I included a "SecuredServiceProxy". If your service inherits from a base class, you could define C to be that base class and instead of passing in credentials as a property, you could pass it in with the constructor (assuming the base class required that constructor). – Jaime Torres Sep 14 '12 at 01:38
  • I'm getting an error: `The type 'ApiClient' cannot be used as type parameter 'C' in the generic type or method 'SecuredServiceProxy.Execute(System.Func, System.ServiceModel.Description.ClientCredentials)'. There is no implicit reference conversion from 'StandardApiClient' to 'ISecuredService'` – Chad Levy Sep 14 '12 at 02:02
  • @Paperjam Without seeing your code that is very hard to diagnose. Based on a LOT of assumptions (the major one being that ApiClient is an abstract/base class for your services), replace ISecuredService from the constraints list with ApiClient and give it ago. – Jaime Torres Sep 14 '12 at 02:22
1

You can take a simmilar approach to most WCF implementations & create an interface defining the API functionality & hiding the implementation behind that interface. Here's a quick example using your code sample:

    class APIEngine :IApiProvider
    {
        //...Private stuff & other methods
        T[] Search<T>(SearchArgs args)
        {
           //Error handling ommitted
           T[] result;

           switch(args.SearchType)
           {
               case(SearchType.GetSomething)
                    result = GetSomethingSearch(args.Key);
                    break;
               // and so on
           }     


           api.Close();
          return result;
       }
       Result[] GetSomethingSearch(Key searchKey)
       {   
           ApiClient api = new ApiClient(); 
           api.Credentials.UserName.UserName = "blah";
           api.Credentials.UserName.Password = "blahblah";   

           object SomethingSearch search = new SomethingSearch(); 
           search.Key = searchKey;

           result = api.GetSomething(search);  
       }
    }


class SearchArgs
{
    SearchType SearchType {get; set;} //Enum of search types
    SearchKey Key {get; set;} //SearchKey would be parent class for different key types
{

You would call this just like any other interface:

IApiProvider.Search(keyValue);

Everything else can be set during construction or re-set later on via dedicated methods. Let me know if this doesn't actually answer your question.

EDIT:

Using a wrapper class for the arguments allows you to have a single friendly Search method that can take any number of Search types by falling through the case to determine the correct type based on your SearchType.

Chris
  • 2,885
  • 18
  • 25
  • The thing is, I need to be able to specify which api operation and search objects to use. `SomethingSearch` could be `SomethingElseSearch`, and `GetSomething` could be `GetSomethingElse`, for example. The different search objects have different numbers of parameters as well. – Chad Levy Jul 10 '12 at 21:50