2013-03-27 7 views
2

Есть ли способ узнать, является ли текущий метод WCF методом OneWay?Как узнать, помечен ли текущий метод IsOneWay

Я использую httpBinding, и вопрос относится к серверной стороне.

Я искал в свойствах для OperationContext на MSDN и не смог его найти.

EDIT:

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

В случае OneWay называет код состояния будет 202, но это не лучший способ.

Есть ли лучшие способы?

+1

Возможно отражение и проверка атрибута? Это также может быть в метаданных для службы. Это просто с головы. – Tim

+0

Да, отражение - это путь. Но с точки зрения производительности это не так хорошо. Проверка StatusCode будет быстрее в этом месте. На самом деле я думал, что для текущего OperationContext есть свойство, которое говорит, что это OneWay или нет, но, похоже, нет нормального способа получить эту информацию. Но в любом случае спасибо. –

ответ

2

ФОС способ решить эту проблему заключается в следующем:

  1. Создать пользовательский объект контекста, который содержит данные, которые вы хотите
  2. Создание пользовательского поведения, заполняющий данные
  3. Применить поведение к службе

Для этого необходимо подключить несколько точек расширения WCF. Его не сложно, как только вы его повесите, но это много набирает из-за всех интерфейсов, которые вам нужно реализовать (даже когда реализация метода пуста). Вот пример.

Сначала определим 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); 
    } 
} 

Он использует пользовательский объект контекста для хранения необходимой информации. В этом случае bool, это правда, если операция IsOneWay. Обратите внимание, что, поскольку вы завершаете экземпляр 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, которое применяет поведение операции. Вы также можете применить поведение на уровне операции.

Обратите внимание, что 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(); 
      } 
     } 
    } 
} 
+0

Да, это определенно то, что мне нужно. Жаль, что WCF не обладает такой функциональностью из коробки, но мне нравится ваш вариант. Большое спасибо за это :) –

2

as Tim предлагается, использование отражение. Следующий фрагмент кода должен работать для вас

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, чтобы получить текущее имя метода, но я использовал отражение все вместе.

 Смежные вопросы

  • Нет связанных вопросов^_^