Текущий OperationContext является нулевым в службе Windows WCF
Я пытаюсь настроить систему публикации / подписки с помощью WCF и где сервер WCF находится в службе Windows. Привязка нет. TCP. Служба предоставляет клиенту метод "Подписаться", чтобы клиент мог зарегистрировать обработчик обратного вызова для события, которое будет вызвано из библиотеки DLL, связанной с сервером. В методе Subscribe я пытаюсь получить канал обратного вызова, используя метод OperationContext.Current.GetCallbackChannel. Когда я пытаюсь это сделать, свойство OperationContext.Current возвращает NULL.
Может кто-нибудь сказать мне, при каких обстоятельствах это свойство будет возвращать ноль?? Я что-то пропустил? Я включу сервисный код и код интерфейса ниже. Я использую C# в Visual Studio 2012 и ориентации платформы 4.5.
Обслуживание:
namespace WService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class WcfPublisherService : IWcfPublisherContract
{
IOALogic logic = new OAControlExample();
IWcfSubscriberContract _callback = null;
public void Subscribe()
{
_callback = OperationContext.Current.GetCallbackChannel<IWcfSubscriberContract>();
logic.BarriersChanged += logic_BarriersChanged;
}
public void UnSubscribe()
{
logic.BarriersChanged -= logic_BarriersChanged;
}
void logic_BarriersChanged(object sender, BarriersChangedEventArgs e)
{
_callback.BarriersChanged(e.BarrierLines);
}
}
}
Интерфейс:
namespace WService
{
[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IWcfSubscriberContract))]
public interface IWcfPublisherContract
{
[OperationContract(IsOneWay=false, IsInitiating=true)]
void Subscribe();
[OperationContract(IsOneWay = false, IsTerminating=true)]
void UnSubscribe();
}
public interface IWcfSubscriberContract
{
[OperationContract(IsOneWay = true)]
void BarriersChanged(BarrierLines barrierLines);
}
}
Клиент:
namespace TestClient
{
public partial class Form1 : Form
{
WcfPublisherService myService
= new WcfPublisherService();
ServiceCallback serviceCallback = new ServiceCallback();
public Form1()
{
InitializeComponent();
serviceCallback.NewMessage += serviceCallback_NewMessage;
}
private delegate void serviceCallback_NewMessageDelegate(object sender, NewMessageEventArgs e);
void serviceCallback_NewMessage(object sender, NewMessageEventArgs e)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new serviceCallback_NewMessageDelegate(serviceCallback_NewMessage), new object[] {sender, e});
}
else
{
if (textBox1.Text.Trim().Length > 1)
{
textBox1.Text += Environment.NewLine;
}
textBox1.Text += e.Msg;
}
}
private void button1_Click(object sender, EventArgs e)
{
myService.Subscribe();
}
private void button2_Click(object sender, EventArgs e)
{
myService.UnSubscribe();
}
}
[CallbackBehaviorAttribute(UseSynchronizationContext = false)]
class ServiceCallback : IWcfSubscriberContract
{
public delegate void NewMessageEventHandler(object sender, NewMessageEventArgs e);
public event NewMessageEventHandler NewMessage;
protected virtual void OnNewMessage(string msg)
{
if (NewMessage != null)
{
NewMessage(this, new NewMessageEventArgs(msg));
}
}
public void BarriersChanged(OA.BarrierLines barrierLines)
{
OnNewMessage("new barrier lines");
}
}
public class NewMessageEventArgs : EventArgs
{
public NewMessageEventArgs(string msg)
{
this.Msg = msg;
}
public string Msg { get; set; }
}
}
** * ** * ** * Новое редактирование * ** * ** * ** * ** * ** Благодаря предложениям SalientBrain, я внес значительные изменения в свой проект, потому что понял, что служба должна работать долго и постоянно работает, даже если клиенты не подключены, поэтому я изменил его на одиночный. Несмотря на это, моя первоначальная проблема все еще сохраняется. SalientBrain попросил посмотреть мой конфигурационный файл, поэтому я включу его ниже вместе со всеми остальными соответствующими файлами. Я убрал это, чтобы сохранить пространство, но я не думаю, что убрал что-то важное. Ошибка возникает в методе Subscribe класса PulisherService. Я надеюсь, что это что-то глупое, что я сделал в файле конфигурации. Ну, вот оно:
Config:
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="WService.WCFPublisherServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="WService.WCFPublisherServiceBehavior"
name="WService.WcfPublisherService">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
name="NetTcpBindingEndpoint" contract="WService.IWcfPublisherContract">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
name="MexTcpBindingEndpoint" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8523/Publisher" />
</baseAddresses>
</host>
</service>
</services>
</system.serviceModel>
</configuration>
WcfContracts:
namespace WService
{
[ServiceContract(SessionMode = SessionMode.Allowed, CallbackContract = typeof(IWcfSubscriberContract))]
public interface IWcfPublisherContract
{
[OperationContract(IsOneWay=false)]
void Subscribe(string key);
[OperationContract(IsOneWay = false)]
void UnSubscribe(string key);
}
public interface IWcfSubscriberContract
{
[OperationContract(IsOneWay = true)]
void BarriersChanged(BarrierLines barrierLines);
}
}
WcfService:
namespace WService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WcfPublisherService : IWcfPublisherContract
{
private static WcfPublisherService _instance = null;
private IOALogic _logic = null;
private Dictionary<string, IWcfSubscriberContract> _callbacks
= new Dictionary<string, IWcfSubscriberContract>();
private ReaderWriterLock _callbacksLock = new ReaderWriterLock();
private WcfPublisherService() { }
public static WcfPublisherService TheInstance()
{
if (_instance == null)
{
_instance = new WcfPublisherService();
}
return _instance;
}
public void StopWcf()
{
_logic.StopRequest();
}
public void StartWcf(IOALogic logic)
{
_logic = logic;
_logic.BarriersChanged += logic_BarriersChanged;
ThreadPool.QueueUserWorkItem(new WaitCallback(StartWork), null);
}
public void StartWork(object state)
{
_logic.Run();
}
public void Subscribe(string key)
{
OperationContext context = OperationContext.Current;
// The above line returns null ***********************************************
_callbacksLock.AcquireWriterLock(2000);
if (_callbacksLock.IsWriterLockHeld)
{
_callbacks.Add(key, context.GetCallbackChannel<IWcfSubscriberContract>());
// The above line throws a null execption because context is null ********
_callbacksLock.ReleaseWriterLock();
}
}
public void UnSubscribe(string key)
{
_callbacksLock.AcquireWriterLock(2000);
if (_callbacksLock.IsWriterLockHeld)
{
_callbacks.Remove(key);
_callbacksLock.ReleaseWriterLock();
}
}
void logic_BarriersChanged(object sender, BarriersChangedEventArgs e)
{
_callbacksLock.AcquireReaderLock(1000);
if (_callbacksLock.IsReaderLockHeld)
{
try
{
foreach (IWcfSubscriberContract callback in _callbacks.Values)
{
callback.BarriersChanged(e.BarrierLines);
}
}
finally
{
_callbacksLock.ReleaseReaderLock();
}
}
}
}
}
WindowsService:
namespace WService
{
public partial class WService : ServiceBase
{
internal static ServiceHost _serviceHost = null;
internal static IOALogic _logic = new OAControlExample();
public WService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
if (_serviceHost != null)
{
_serviceHost.Close();
}
_serviceHost = new ServiceHost(WcfPublisherService.TheInstance());
WcfPublisherService.TheInstance().StartWcf(_logic);
_serviceHost.Open();
}
protected override void OnStop()
{
if (WcfPublisherService.TheInstance() != null)
{
WcfPublisherService.TheInstance().StopWcf();
}
if (_serviceHost != null)
{
_serviceHost.Close();
_serviceHost = null;
}
}
}
}
TestForm:
namespace TestClient
{
public partial class Form1 : Form
{
ServiceCallback serviceCallback = new ServiceCallback();
public Form1()
{
InitializeComponent();
serviceCallback.NewMessage += serviceCallback_NewMessage;
}
private delegate void serviceCallback_NewMessageDelegate(object sender, NewMessageEventArgs e);
void serviceCallback_NewMessage(object sender, NewMessageEventArgs e)
{
if (textBox1.InvokeRequired)
{
textBox1.Invoke(new serviceCallback_NewMessageDelegate(serviceCallback_NewMessage), new object[] {sender, e});
}
else
{
if (textBox1.Text.Trim().Length > 1)
{
textBox1.Text += Environment.NewLine;
}
textBox1.Text += e.Msg;
}
}
private void button1_Click(object sender, EventArgs e)
{
serviceCallback.Subscribe();
}
private void button2_Click(object sender, EventArgs e)
{
serviceCallback.Unsubscribe();
}
}
}
TestCallbackClass:
namespace TestClient
{
[CallbackBehaviorAttribute(UseSynchronizationContext = true)]
class ServiceCallback : IWcfSubscriberContract
{
WcfPublisherService myService
= WcfPublisherService.TheInstance();
string callbackKey = Guid.NewGuid().ToString();
public delegate void NewMessageEventHandler(object sender, NewMessageEventArgs e);
public event NewMessageEventHandler NewMessage;
protected virtual void OnNewMessage(string msg)
{
if (NewMessage != null)
{
NewMessage(this, new NewMessageEventArgs(msg));
}
}
public void Subscribe()
{
try
{
myService.Subscribe(callbackKey);
}
catch (Exception ex)
{
OnNewMessage("exception: " + ex.Message);
}
}
public void Unsubscribe()
{
try
{
myService.UnSubscribe(callbackKey);
}
catch (Exception ex)
{
OnNewMessage("exception: " + ex.Message);
}
}
public void BarriersChanged(OAInterface.BarrierLines barrierLines)
{
OnNewMessage("new barrier lines");
}
}
public class NewMessageEventArgs : EventArgs
{
public NewMessageEventArgs(string msg)
{
this.Msg = msg;
}
public string Msg { get; set; }
}
}
4 ответа
Как обсуждалось в комментариях, если вы непосредственно создаете экземпляр типа службы - в отличие от прокси / клиентского канала WCF - и затем вызываете метод для него, OperationContext не существует. WCF предоставляет экземпляр OperationContext, когда ваша операция выполняется внутри службы.
Я столкнулся с этой проблемой, и ни одно из решений не сработало, и самое главное, если вы используете
async await
OperationContext.Current; will be null
Я использую, чтобы получить Ip, поэтому использовал его, как это до любого ожидаемого вызова
var clientIpAddress = System.Web.HttpContext.Current?.Request?.UserHostAddress;
После первого оператора await в вашей асинхронной операции службы OperationContext.Current может иметь значение null, поскольку остальная часть тела метода может выполняться в другом потоке (и OperationContext не передается между потоками.
Таким образом, чтобы получить его, вы можете написать свой код перед любым ожидаемым действием
Может быть, это кому-то поможет:)
В коде клиента ни прокси не создан, ни канал фабрики. Экземпляр класса обслуживания создается как библиотека классов.
Вы должны использовать сервис, как показано ниже
ServiceCallback serviceCallback = new ServiceCallback();
InstanceContext instanceContext = new InstanceContext(serviceCallback);
var pubsubProxy = new PubSubProxy.WcfPublisherContractClient(instanceContext);
pubsubProxy.Subscribe();
И когда служба работает, OperationContext создается, и вы можете получить доступ к OperationContext.Current
В моем случае это был я глупый...
Я пытался установить
callback = OperationContext.Current.GetCallbackChannel<IWcfSubscriberContract>();
В функции CALLBACK вместо функции на стороне сервера... Когда в функции обратного вызова - очевидно, нет текущего контекста.
У меня была похожая проблема: в моем случае, когда InstanceContextMode был установлен в Single, WebOperationContext.Current был нулевым в конструкторе. Однако это было доступно в методах services/classes.