C# WCF Windows 服务中的当前 OperationContext 为 null
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15205337/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
current OperationContext is null in WCF Windows Service
提问by dtaylor
I am trying to set up a Publish/Subscribe system using WCF and where the WCF server is in a Windows service. The binding is net.TCP. The service is providing a "Subscribe" method to the client so the client can register a callback handler to an event that will be raised from a DLL linked to the server. In the Subscribe method I attempt to get the callback channel using the OperationContext.Current.GetCallbackChannel method. When I attempt this the OperationContext.Current property returns NULL.
我正在尝试使用 WCF 以及 WCF 服务器在 Windows 服务中的位置设置发布/订阅系统。绑定是 net.TCP。该服务正在向客户端提供“订阅”方法,以便客户端可以将回调处理程序注册到将从链接到服务器的 DLL 引发的事件。在 Subscribe 方法中,我尝试使用 OperationContext.Current.GetCallbackChannel 方法获取回调通道。当我尝试此操作时,OperationContext.Current 属性返回 NULL。
Can anyone tell me under what circumstances this property would return null?? Have I missed setting something up? I will include the service code and the interface code below. I am using c# in Visual Studio 2012 and targeting framework 4.5.
谁能告诉我在什么情况下这个属性会返回null?我错过了设置吗?我将在下面包含服务代码和接口代码。我在 Visual Studio 2012 和目标框架 4.5 中使用 c#。
Service:
服务:
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);
}
}
}
Interface:
界面:
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);
}
}
Client:
客户:
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; }
}
}
*********New Edit ***************Thanks to SalientBrain's suggestions, I have made considerable changes to my project because I realized the service had to be long running and continually running even if no clients are connected so I changed it to a singleton. Even so, my original problem still persists. SalientBrain has asked to see my config file so I will include it below along with all the other pertinent files. I've stripped it out to conserve space, but I don't think I removed anything important. The error occurs in Subscribe method of the PulisherService class. I hope it is something stupid I did in the config file. Well, here it is:
** *** *** *新编辑 *** *** *** *** ***感谢 SalientBrain 的建议,我对我的项目进行了相当大的更改,因为我意识到即使没有客户端连接,服务也必须长时间运行并持续运行,因此我将其更改为单例。即便如此,我原来的问题仍然存在。SalientBrain 要求查看我的配置文件,因此我会将它与所有其他相关文件一起包含在下面。我已将其删除以节省空间,但我认为我没有删除任何重要的内容。该错误发生在 PulisherService 类的 Subscribe 方法中。我希望这是我在配置文件中所做的一些愚蠢的事情。嗯,这里是:
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:
Wcf合同:
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:
Wcf服务:
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; }
}
}
采纳答案by Ian Gilroy
As discussed in the comments, if you directly create an instance of the service type - as opposed to a WCF proxy/clientchannel - and then you call a method on it, there is no OperationContext. WCF provides an OperationContext instance when your operation is running within a service.
正如评论中所讨论的,如果您直接创建服务类型的实例(而不是 WCF 代理/客户端通道),然后在其上调用方法,则没有 OperationContext。当您的操作在服务中运行时,WCF 提供一个 OperationContext 实例。
回答by Milan Raval
In the client code neither proxy created not channel factory. Service class instance is created as a class library.
在客户端代码中,既没有创建代理也没有创建通道工厂。服务类实例被创建为类库。
You should consume service as below code
您应该按照以下代码使用服务
ServiceCallback serviceCallback = new ServiceCallback();
InstanceContext instanceContext = new InstanceContext(serviceCallback);
var pubsubProxy = new PubSubProxy.WcfPublisherContractClient(instanceContext);
pubsubProxy.Subscribe();
And when the service is running, OperationContext is created and you can access OperationContext.Current
当服务运行时,会创建 OperationContext 并且您可以访问 OperationContext.Current
回答by John Meyer
I had a similar issue: in my case when the InstanceContextMode
was set to Single
the WebOperationContext.Current
was null
in the constructor. However it was available within the services/classes methods.
我有一个类似的问题:在我的情况下,当InstanceContextMode
被设定为Single
在WebOperationContext.Current
是null
在构造函数中。但是,它在服务/类方法中可用。
回答by ephraim
In my case it was me being stupid...
就我而言,这是我愚蠢...
I tried to set
我试图设置
callback = OperationContext.Current.GetCallbackChannel<IWcfSubscriberContract>();
In the CALLBACK function, instead of the server side funciton... When in callback function - obviously there's no current context.
在 CALLBACK 函数中,而不是服务器端函数...在回调函数中时 - 显然没有当前上下文。
回答by dnxit
I've faced this issue and non of the solutions worked and Most Important thing is if you're using
我遇到过这个问题,但没有一个解决方案有效,最重要的是你是否正在使用
async await
OperationContext.Current; will be null
My usage is to get Ip so used it like this before any awaitable call
我的用法是让 Ip 在任何等待调用之前像这样使用它
var clientIpAddress = System.Web.HttpContext.Current?.Request?.UserHostAddress;
After the first await statement in your async service operation, OperationContext.Current could be null because the rest of the method body may be running on a different thread (and OperationContext does not flow between threads
在异步服务操作中的第一个 await 语句之后,OperationContext.Current 可能为 null,因为方法主体的其余部分可能在不同的线程上运行(并且 OperationContext 不会在线程之间流动
So to get it you can write your code before any awaitable action
因此,为了获得它,您可以在任何等待操作之前编写代码
May be it'll help someone :)
可能会帮助某人:)