代码之家  ›  专栏  ›  技术社区  ›  Alex

将接口方法作为参数传递

  •  2
  • Alex  · 技术社区  · 8 年前

    注释 这很可能是 C# 特定语言问题,与 WCF web services 完全。

    有三个派对 ASMX Web服务,用于数据检索。我创建了一个名为 ExecuteCommand() 它用于针对Web服务的每个请求。此方法的目的是处理cookie会话/异常和其他常见逻辑。对于每个请求,应使用新的通道,以简化未使用资源的处理。

    问题是使用 执行命令() 方法-我必须每次初始化一个通道,以便能够传递要作为参数执行的方法。如果这听起来太复杂,对不起。下面是一个用法示例:

    string color = "blue";
    var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
    var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color));
    // channel is null here. Channel was closed/aborted, depending on Exception type.
    

    执行命令() 被调用- channel 已释放。为什么 通道 对象是完全需要的,是能够提供一个方法来作为参数执行的!即 () => channel.GetCars() 。为了进一步支持这些话,这里是 WcfHelper 类内部构件:

    public static class WcfHelper
    {
        public static Cookie Cookie { get; set; }
    
        public static T ExecuteCommand<T>(IClientChannel channel, Expression<Func<T>> method)
        {
            T result = default(T);
    
            try
            {
                // init operation context
                using (new OperationContextScope(channel))
                {
                    // set the session cookie to header
                    if (Cookie != null) {
                        HttpRequestMessageProperty request = new HttpRequestMessageProperty();
                        request.Headers["Cookie"] = cookie.ToString();
                        OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;  
                    }
    
                    // execute method
                    var compiledMethod = method.Compile();
                    result = compiledMethod.Invoke();
                }
            }
            // do different logic for FaultException, CommunicationException, TimeoutException
            catch (Exception)
            {
                throw;
            }
            finally
            {
                CloseOrAbortServiceChannel(channel);
                channel = null;
            }
    
            return result;
        }
    
        private static void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
        {
            bool isClosed = false;
    
            if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
                return;
    
            try
            {
                if (communicationObject.State != CommunicationState.Faulted)
                {
                    communicationObject.Close();
                    isClosed = true;
                }
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (!isClosed)
                    AbortServiceChannel(communicationObject);
            }
        }
    
        private static void AbortServiceChannel(ICommunicationObject communicationObject)
        {
            try
            {
                communicationObject.Abort();
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
    

    所以这个简短的问题-可以初始化 通道 变量内部 ExecuteCommand 方法本身,虽然有可能定义,但应在内部执行哪种方法 执行命令 对于给定的频道?

    我正试图完成这样的事情:

    string color = "blue";
    var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));
    

    甚至

    string color = "blue";
    var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
    

    当然,任何其他代码改进建议都是受欢迎的,但不是强制性的。

    附笔。 自动交换存储器 添加为 Service reference 在里面 Visual Studio 。因此,有些实体会自动为“汽车服务”生成,例如- CarServiceSoapChannel 接口, CarServiceSoapClient 当然还有 CarService 包含Web服务方法的接口。在上面的示例中,a ChannelFactory 用于为 汽车服务频道 因此,下面是问题名称的来源: Passing an interface method as a parameter . 这可能有点误导,但我希望从描述本身就可以清楚地了解我试图完成的工作。

    更新日期:2018年5月25日 我听从了@nvoigt的建议,能够达到我想要的结果:

    public static TResult ExecuteCommand<TInterface, TResult>(Func<TInterface, TResult> method)
        where TInterface : IClientChannel
    {
        TResult result = default(TResult);
        IClientChannel channel = null;
    
        try
        {
            channel = StrategyFactory.CreateChannel<TInterface>();
    
            // init operation context
            using (new OperationContextScope(channel))
            {
                // set the session cookie to header
                if (Cookie != null)
                    Cookie.SetCookieForSession();
    
                // execute method
                result = method((TInterface)channel);
            }
        }
        catch (Exception)
        {
            throw;
        }
        finally
        {
            CloseOrAbortServiceChannel(channel);
            channel = null;
        }
    
        return result;
    }
    

    这已经达到了最初的目标。然而,这种方法有一个问题。要调用该方法,必须显式指定该方法的返回参数。

    所以说,如果你想调用这个方法-写这是不够的:

    var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.IsBlue())
    

    您必须具体说明,返回类型应为 boolean

    var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel, bool>(channel => channel.IsBlue())
    

    我个人并不介意编写额外的代码,因为它仍然比我的初始方法实现有很大的优势,但是,我想知道新版本是否可以改进?

    例如,当我只有一个普通的 TResult 键入方法-返回类型 <TResult> 可以省略。这不再是2个泛型的情况。无论如何,谢谢@ 诺沃格特 !

    1 回复  |  直到 8 年前
        1
  •  1
  •   nvoigt    8 年前

    您的方法签名应该是:

    public static TResult ExecuteCommand<TInterface>(Func<TInterface, TResult> method)
    

    然后在你的wcfhelper(可能不应该是 static 因为它需要一个成员 _strategyFactory )如前所述创建通道:

    {
        var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
    
        return method(channel);
    }
    

    显然,您需要再次添加所有花哨的“尝试/最终”内容。


    因为您现在应该有实例,并且工厂作为成员在类中,所以您可以将服务契约generic放入类中,以方便用户:

    public class ConnectionToService<TInterface> : where TInterface : class
    {
        public TResult ExecuteCommand<TResult>(Func<TInterface, TResult> method)
        {
            var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
    
            return method(channel);
        }
    }
    

    用途:

    var service = new ConnectionToService<ICarService>();
    
    var color = service.ExecuteCommand(s => s.GetColor());
    
    推荐文章