代码之家  ›  专栏  ›  技术社区  ›  Sergey Litvinov Oetawan Arya Chandra

如何查找当前方法是否使用IsOneWay进行标记

  •  2
  • Sergey Litvinov Oetawan Arya Chandra  · 技术社区  · 12 年前

    有没有办法知道当前执行的WCF方法是否是单向方法?

    我使用的是httpBinding,这个问题与服务器端有关。

    我在MSDN的属性中搜索OperationContext,但找不到它。

    编辑 :

    I used the following check:
    HttpContext.Current.Response.StatusCode != 
            (int)System.Net.HttpStatusCode.Accepted;
    

    在OneWay调用的情况下,状态代码将是202,但这不是一个好方法。

    有什么更好的方法吗?

    2 回复  |  直到 12 年前
        1
  •  2
  •   ErnieL    12 年前

    WCF解决此问题的方法是:

    1. 创建包含所需数据的自定义上下文对象
    2. 创建填充数据的自定义行为
    3. 将行为应用于您的服务

    这需要插入多个WCF扩展点。一旦掌握了窍门,这并不难,但由于需要实现所有接口(即使方法实现是空的),这需要大量的输入。这是一个例子。

    首先定义一个简单服务:

    [ServiceContract]
    public interface ISimple
    {
        [OperationContract(IsOneWay = true)]
        void OneWay();
    
        [OperationContract]
        void Default();
    }
    
    [OneWayContract]
    public class SimpleService : ISimple
    {
        //[OneWayOperation]     // uncomment to Add context data on the operation level instead on contract.
        public void OneWay()
        {
            Console.WriteLine("OneWay() is marked IsOneWay:" + OneWayContext.Current.IsOneWay);
        }
    
        public void Default()
        {
            Console.WriteLine("Default() is marked IsOneWay:" + OneWayContext.Current.IsOneWay);
        }
    }
    

    它使用自定义上下文对象来存储您需要的信息。在这种情况下,如果操作是单向的,则为true的bool。请注意,由于您正在包装WCF InstanceContext,因此可以在不实际托管服务的情况下进行单元测试。方法创建自定义上下文,该上下文取自 this blog :

    public class OneWayContext : IExtension<InstanceContext>
    {
        public OneWayContext()
        {
            // if not set, default to false.
            IsOneWay = false;
        }
    
        public bool IsOneWay { get; set; }
    
        public static OneWayContext Current
        {
            get
            {
                OneWayContext context = OperationContext.Current.InstanceContext.Extensions.Find<OneWayContext>();
                if (context == null)
                {
                    context = new OneWayContext();
                    OperationContext.Current.InstanceContext.Extensions.Add(context);
                }
                return context;
            }
        }
    
        public void Attach(InstanceContext owner) { }
        public void Detach(InstanceContext owner) { }
    }
    

    创建一个OperationInvoker,将自定义上下文添加到OperationContext中。请注意,插入WCF OperationInvoker意味着将其放入调用堆栈中。因此,它需要将所有不处理的调用传递给框架的“内部”OperationInvoker。

    public class OneWayBehavior : IOperationInvoker
    {
        IOperationInvoker innerOperationInvoker;
        public readonly bool isOneWay;
    
        public OneWayBehavior(IOperationInvoker innerOperationInvoker, bool isOneWay)
        {
            this.isOneWay = isOneWay;
            this.innerOperationInvoker = innerOperationInvoker;
        }
    
        public object[] AllocateInputs()
        {
            return innerOperationInvoker.AllocateInputs();
        }
    
        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            // Everytime the operation is invoked, add IsOneWay information to the context.
            OneWayContext.Current.IsOneWay = this.isOneWay;
    
            return innerOperationInvoker.Invoke(instance, inputs, out outputs);
        }
    
        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            return innerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
        }
    
        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            return innerOperationInvoker.InvokeEnd(instance, out outputs, result);
        }
    
        public bool IsSynchronous
        {
            get { return innerOperationInvoker.IsSynchronous; }
        }
    }
    

    现在将新行为应用于合同。[OneWayContract]属性应用WCF Contract行为,该行为应用操作行为。您也可以在操作级别应用行为。

    请注意,OperationDescription提供了已经填充的WCF结构所需的所有信息。不需要反思。不依赖于绑定。

    public class OneWayOperationAttribute : Attribute, IOperationBehavior
    {
        public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }
    
        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }
    
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            // grab "IsOneWay" from the operation description and pass on the behavior's constructor.
            dispatchOperation.Invoker = new OneWayBehavior(dispatchOperation.Invoker, operationDescription.IsOneWay);
        }
    
        public void Validate(OperationDescription operationDescription)
        {
        }
    }
    
    public class OneWayContractAttribute : Attribute, IContractBehavior
    {
        public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }
    
        public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }
    
        public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
        {
            foreach (OperationDescription operation in contractDescription.Operations)
            {
                operation.OperationBehaviors.Add(new OneWayOperationAttribute());
            }
        }
    
        public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
        {
        }
    }
    

    现在运行一个快速测试。

    public static class Program
    {
        static void Main(string[] args)
        {
            ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
            simpleHost.Open();
    
            ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
            ISimple proxy = factory.CreateChannel();
    
            proxy.OneWay();
    
            proxy.Default();
    
            Console.WriteLine("Press ENTER to close the host once you see 'ALL DONE'.");
            Console.ReadLine();
    
            ((ICommunicationObject)proxy).Shutdown();
    
            simpleHost.Shutdown();
        }
    }
    

    输出应为:

    Default() is marked IsOneWay:False
    OneWay() is marked IsOneWay:True
    Press ENTER to close the host once you see 'ALL DONE'.
    

    请注意,我们所有的抽象都得到了维护。服务只依赖于行为提供的上下文对象,该行为被属性清楚地标记为对服务的依赖。

    这个例子表明 [OneWayContract] 在服务类上。但您也应该能够将其应用于 [ServiceContract] .


    为了完整起见,这是整个代码示例的副本,作为一个控制台应用程序,您可以粘贴并运行。

    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    
    namespace ConsoleWCF
    {
        [ServiceContract]
        public interface ISimple
        {
            [OperationContract(IsOneWay = true)]
            void OneWay();
    
            [OperationContract]
            void Default();
        }
    
        [OneWayContract]
        public class SimpleService : ISimple
        {
            //[OneWayOperation]     // uncomment to Add context data on the operation level instead on contract.
            public void OneWay()
            {
                Console.WriteLine("OneWay() is marked IsOneWay:" + OneWayContext.Current.IsOneWay);
            }
    
            public void Default()
            {
                Console.WriteLine("Default() is marked IsOneWay:" + OneWayContext.Current.IsOneWay);
            }
        }
    
        public class OneWayBehavior : IOperationInvoker
        {
            IOperationInvoker innerOperationInvoker;
            public readonly bool isOneWay;
    
            public OneWayBehavior(IOperationInvoker innerOperationInvoker, bool isOneWay)
            {
                this.isOneWay = isOneWay;
                this.innerOperationInvoker = innerOperationInvoker;
            }
    
            public object[] AllocateInputs()
            {
                return innerOperationInvoker.AllocateInputs();
            }
    
            public object Invoke(object instance, object[] inputs, out object[] outputs)
            {
                // Everytime the operation is invoked, add IsOneWay information to the context.
                OneWayContext.Current.IsOneWay = this.isOneWay;
    
                return innerOperationInvoker.Invoke(instance, inputs, out outputs);
            }
    
            public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
            {
                return innerOperationInvoker.InvokeBegin(instance, inputs, callback, state);
            }
    
            public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
            {
                return innerOperationInvoker.InvokeEnd(instance, out outputs, result);
            }
    
            public bool IsSynchronous
            {
                get { return innerOperationInvoker.IsSynchronous; }
            }
        }
    
        public class OneWayOperationAttribute : Attribute, IOperationBehavior
        {
            public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
            {
            }
    
            public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
            {
            }
    
            public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
            {
                // grab "IsOneWay" from the operation description and pass on the behavior's constructor.
                dispatchOperation.Invoker = new OneWayBehavior(dispatchOperation.Invoker, operationDescription.IsOneWay);
            }
    
            public void Validate(OperationDescription operationDescription)
            {
            }
        }
    
        public class OneWayContractAttribute : Attribute, IContractBehavior
        {
            public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
            {
            }
    
            public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
            }
    
            public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
            {
                foreach (OperationDescription operation in contractDescription.Operations)
                {
                    operation.OperationBehaviors.Add(new OneWayOperationAttribute());
                }
            }
    
            public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
            {
            }
        }
    
        public class OneWayContext : IExtension<InstanceContext>
        {
            public OneWayContext()
            {
                // if not set, default to false.
                IsOneWay = false;
            }
    
            public bool IsOneWay { get; set; }
    
            public static OneWayContext Current
            {
                get
                {
                    OneWayContext context = OperationContext.Current.InstanceContext.Extensions.Find<OneWayContext>();
                    if (context == null)
                    {
                        context = new OneWayContext();
                        OperationContext.Current.InstanceContext.Extensions.Add(context);
                    }
                    return context;
                }
            }
    
            public void Attach(InstanceContext owner) { }
            public void Detach(InstanceContext owner) { }
        }
    
    
    
        public static class Program
        {
            static void Main(string[] args)
            {
                ServiceHost simpleHost = new ServiceHost(typeof(SimpleService), new Uri("http://localhost/Simple"));
                simpleHost.Open();
    
                ChannelFactory<ISimple> factory = new ChannelFactory<ISimple>(simpleHost.Description.Endpoints[0]);
                ISimple proxy = factory.CreateChannel();
    
                proxy.OneWay();
    
                proxy.Default();
    
                Console.WriteLine("Press ENTER to close the host once you see 'ALL DONE'.");
                Console.ReadLine();
    
                ((ICommunicationObject)proxy).Shutdown();
    
                simpleHost.Shutdown();
            }
        }
    
        public static class Extensions
        {
            static public void Shutdown(this ICommunicationObject obj)
            {
                try
                {
                    obj.Close();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Shutdown exception: {0}", ex.Message);
                    obj.Abort();
                }
            }
        }
    }
    
        2
  •  2
  •   Dhawalk    12 年前

    正如蒂姆所建议的那样,使用反思。下面的片段应该对你有用

     Type serviceInterface = typeof(IService1);
     MethodInfo mi =  serviceInterface.GetMethod((System.Reflection.MethodBase.GetCurrentMethod().Name);
     Attribute attr = mi.GetCustomAttribute(typeof(OperationContractAttribute));
     Console.WriteLine(((OperationContractAttribute)attr).IsOneWay);
    

    您也可以使用stackframe来获取当前的方法名称,但我一直使用反射。