如何找出IsOneWay是否标记了当前方法 [英] How to find out whether current method is flagged with IsOneWay
问题描述
有没有办法知道当前正在执行的WCF方法是否是OneWay方法?
Is there a way to know whether the currently executing WCF method is a OneWay method?
我正在使用httpBinding,这个问题与服务器端有关.
I'm using httpBinding and the question relates to the server side.
我在MSDN上的OperationContext中搜索属性,但找不到它.
I searched in properties for OperationContext on MSDN and couldn't find it.
编辑:
I used the following check:
HttpContext.Current.Response.StatusCode !=
(int)System.Net.HttpStatusCode.Accepted;
对于OneWay呼叫,状态代码将为202,但这不是一个好方法.
In case of OneWay calls the status code will be 202, but it's not a good way.
还有更好的方法吗?
推荐答案
WCF解决此问题的方法是:
The WCF way to solve this is to:
- 创建一个包含所需数据的自定义上下文对象
- 创建填充数据的自定义行为
- 将行为应用于您的服务
这需要插入多个WCF扩展点.一旦掌握了它就不难了,但是由于需要实现所有接口(即使方法实现为空),它也需要大量输入.这是一个例子.
This requires plugging into multiple WCF extension points. Its not hard once you get the hang of it, but it is a lot of typing because of all the interfaces you need to implement (even when the method implementations are empty). Here's an example.
首先定义一个简单服务:
First define a Simple Service:
[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);
}
}
它使用自定义上下文对象来存储所需的信息.在这种情况下,如果操作IsOneWay,则布尔值为true.请注意,由于要包装WCF InstanceContext,因此可以在不实际托管服务的情况下进行单元测试.创建来自此博客的方法的自定义上下文的方法:
It uses a custom context object to store the information you need. In this case a bool that is true if the operation IsOneWay. Notice that since you are wrapping the WCF InstanceContext, unit testing without actually hosting the service is possible. Method to create a custom context taken from 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.
Create an OperationInvoker to add the custom context to the OperationContext. Note that inserting a WCF OperationInvoker means putting it in the call stack. So all calls that it doesn't handle, it needs to pass on to the framework's "inner" 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合同行为,该行为应用操作行为.您也可以在操作级别应用行为.
Now apply the new behavior to the contract. The [OneWayContract] attribute applies a WCF Contract behavior, which applies an operation behavior. You could also apply the behavior at the operation level.
请注意,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'.
请注意,我们所有的抽象都已维护.服务仅取决于行为提供的上下文对象,该行为被属性明确标记为对服务的依赖.
Notice that all our abstractions are maintained. The service only depends on the context object provided by a behavior, which is clearly marked as a dependency on the service by the attribute.
该示例将[OneWayContract]
放在服务类上.但您也应该可以将其应用于[ServiceContract]
.
The example puts [OneWayContract]
on the service class. But you should also be able to apply it to the [ServiceContract]
.
为了完整起见,这是作为一个控制台应用程序复制的整个代码示例的副本,可以粘贴并运行.
For completeness sake this is a copy of the entire code sample as one console app that you can paste and run.
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();
}
}
}
}
这篇关于如何找出IsOneWay是否标记了当前方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!