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