将多个服务引用操作封装在可重用类中



我有一个.NET服务引用,我想将它封装到一个可重用的类中。

我通常的电话看起来是这样的:

// 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();

其他调用在调用的操作和搜索参数对象中都有所不同。

问题是,我不知道如何将API操作的名称(即GetSomething()和特定于操作的搜索对象(SomethingSearch)传递到类中。

我该如何做到这一点?我不是要求为我完成这项工作,但我不确定从哪里开始。我相信这与Func<T>和代表们有关,但令人尴尬的是,我对他们缺乏经验。

我的一位同事开发了这个解决方案:

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

还有一个使用的例子:

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

在这个伪案例中,我们使用了MySecurityServiceGetUserInformationIsUserAuthorized中的两种方法。GetUserInformationGuid作为参数,并返回一个UserInformation对象。IsUserAuthorized获取UserCredentialsActionInformation对象,并返回bool,无论用户是否被授权。

此代理也是缓存可缓存服务调用结果的理想场所。

如果你需要向服务器发送参数,可能有一种更通用的方法,但我认为你需要为它创建一个特定的代理。例如:

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

您可以对大多数WCF实现采取类似的方法&创建定义API功能的接口&将实现隐藏在该接口后面。下面是一个使用代码示例的快速示例:

    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
{

你可以像其他任何接口一样调用它:

IApiProvider.Search(keyValue);

其他一切都可以在施工期间设置,也可以稍后通过专用方法重新设置。如果这并不能真正回答你的问题,请告诉我。

编辑:

使用包装类作为参数可以使用一个友好的Search方法,该方法可以通过区分大小写来根据SearchType确定正确的类型,从而采用任意数量的Search类型。

最新更新