Как узнать, помечен ли текущий метод с помощью IsOneWay?
Есть ли способ узнать, является ли выполняемый в настоящее время метод WCF методом OneWay?
Я использую httpBinding, и вопрос касается серверной части.
Я искал в свойствах для OperationContext на MSDN и не мог найти его.
РЕДАКТИРОВАТЬ:
I used the following check:
HttpContext.Current.Response.StatusCode !=
(int)System.Net.HttpStatusCode.Accepted;
В случае вызовов OneWay код состояния будет 202, но это не очень хороший способ.
Есть ли лучшие способы?
2 ответа
Способ WCF решить это состоит в том, чтобы:
- Создайте пользовательский объект контекста, который содержит данные, которые вы хотите
- Создать пользовательское поведение, которое заполняет данные
- Применить поведение к вашему сервису
Это требует подключения к нескольким точкам расширения 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);
}
}
Он использует пользовательский контекстный объект для хранения необходимой вам информации. В этом случае bool это правда, если операция IsOneWay. Обратите внимание, что, поскольку вы упаковываете InstanceContext WCF, возможно модульное тестирование без фактического размещения службы. Метод для создания пользовательского контекста взят из этого блога:
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, которое применяет поведение операции. Вы также можете применить поведение на уровне операций.
Обратите внимание, что 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();
}
}
}
}
Как предложил Тим, используйте рефлексию. Следующий фрагмент должен работать на вас
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 для получения текущего имени метода, но я все время использовал отражение.