Search code examples
c#.netweb-servicesencapsulationservice-reference

Encapsulating multiple service reference operations in a reusable class


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.


Solution

  • 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;
        }
    }