我正在使用委托(delegate)来允许我的服务获取存储在我的表单中的字符串。 当我运行代码时,我的服务日志说明如下:
System.NullReferenceException:对象引用未设置为对象的实例。
指向 diData.DIDataCompressed = GetDIData();
我尝试在表单的构造函数中的 DataServer = new ModelDataService();
处设置断点。并注意到我的 WCF 服务在我的窗体的构造函数代码运行之前已经启动(我从托盘中的 WcfSvcHost 获得弹出窗口,并且在 WCF 服务主机窗口中它说 ModelDataService 已经启动),所以委托(delegate)显然没有被实例化。 更新:在调用 Program.cs 中的 Main() 方法(启动表单的方法)之前启动服务。此外,我的解决方案的唯一启动项目是我的表单!
如何让我的 WCF 服务仅在我的表单加载时启动(以便我可以正确设置委托(delegate))?
这是我的 WCF 服务代码:
[ServiceBehavior(UseSynchronizationContext = false, InstanceContextMode = InstanceContextMode.Single)]
public class ModelDataService : IModelData
{
public delegate string GetData();
public GetData GetDIData { get; set; }
public ModelDataService()
{
}
public DIData GetDData()
{
DIData diData = new DIData();
diData.DIDataCompressed = GetDIData(); // **** error points here
return diData;
}
}
[DataContract]
public class DIData
{
[DataMember]
public string DIDataCompressed;
}
以及应该启动服务的表单代码:
public partial class ScraperForm : Form
{
ServiceHost Host;
ModelDataService DataServer;
string DIData;
public ScraperForm()
{
InitializeComponent();
#region Start Data Server
DataServer = new ModelDataService(); // I set breakpoint here
DataServer.GetDIData = new ModelDataService.GetData(this.GetDIData);
Host = new ServiceHost(DataServer, new Uri[]
{
new Uri("http://localhost:8000")
});
Host.AddServiceEndpoint(typeof(IModelData),
new BasicHttpBinding(),
"ModelData");
Host.Open();
#endregion
DIData = "";
}
public string GetDIData()
{
return DIData; // This is updated on a timer
}
我的 App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
</appSettings>
<system.web>
<compilation debug="true" />
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service name="SoccerModelService.ModelDataService">
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding"
contract="SoccerModelService.IModelData"
name ="BasicHttpBinding_IModelData">
<identity>
<dns value="localhost"/>
</identity>
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8000/ModelData/"/>
<!--//localhost:8733/Design_Time_Addresses/ModelDataService/Service1/" /> -->
</baseAddresses>
</host>
</service>
<!--><service name="SoccerModelService.ModelDataService" behaviorConfiguration="debug">
</service> -->
</services>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="32" maxStringContentLength="8388608" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8000/ModelData/" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding" contract="SoccerModelService.IModelData"
name="EndPoint" behaviorConfiguration="EndpointBehaviour" />
<!--<endpoint address="http://localhost:8000/ModelData" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding" name="EndPoint" behaviorConfiguration="EndpointBehaviour" /> -->
</client>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information,
set the values below to false before deployment -->
<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True"/>
<!-- To receive exception details in faults for debugging purposes,
set the value below to true. Set to false before deployment
to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="False" />
</behavior>
<behavior name="debug">
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="EndpointBehaviour">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
<!-- Trace-->
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Warning" propagateActivity="true" >
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="myUserTraceSource" switchValue="Warning, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml"
type="System.Diagnostics.XmlWriterTraceListener"
initializeData="TraceLog.svclog" />
</sharedListeners>
<trace autoflush="true" />
</system.diagnostics>
</configuration>
感谢您的帮助!
更新
我在我的表单中添加了一个加载事件。我仍然遇到同样的错误。我尝试在我的 Program.cs 中的 Main() 方法上为我的表单(启动表单的方法)设置一个断点,并且服务在 Program.cs 中的 main 方法被调用之前启动!
问题可能是每次我的客户调用服务时都会创建一个新的服务实例吗?我已将其设置为单例,但我设置有误吗?
更新 2
我认为我可能不小心将我的项目设为 WCF 服务应用程序,而不是 WCF 服务库(我可以将其托管在表单中)。我项目的 bin 在项目名称中包含一个 .dll。我相信这意味着它确实是一个图书馆。如果我错了,请纠正我。
再次感谢!
最佳答案
我的回答基于评论中提供的附加信息。
让 WCF 服务访问用户界面窗口或其组件是非常不寻常的,因为服务应该在服务器的后台运行并且不能干扰用户界面(有很多使用消息框的程序员的故事在调试服务时忘记在部署解决方案之前删除它们。正如预期的那样,这最终导致消息框阻塞服务器)。
出于这个原因,我建议采用以下方法(顺序是有意的):
- 尝试以无用户界面的方式在服务中进行抓取,例如通过使用 WebClient 或类似工具获取原始 HTML 文档并分析其内容。
- 如果您需要让用户与 WebBrowser 交互以便能够访问您要删除的文档(例如用于身份验证),请像现在一样使用单独的应用程序并将结果存储在数据库中。但是独立于服务使用应用程序。该服务可以仅返回存储在数据库中的数据(可能带有时间戳,以便用户在检索到数据时得到通知,并且可以在数据太旧时使用react)。
- 在应用程序中自行托管 WCF 服务。通过这种方式,您可以按照您想要的方式组合应用程序和服务。缺点是您只能在应用程序运行时访问该服务 - 但您将能够以您想要的方式控制和同步应用程序和服务的生命周期。
- 如果第一种方法都不起作用,请改变您的依赖关系,以便服务启动应用程序,而不是相反。 WCF 服务将成为仅运行一次并控制应用程序的中心点。如果您尝试让应用程序在服务器上自行注册,那么如果用户多次启动该应用程序,您就会遇到麻烦。
还有一件事:您提到要使用委托(delegate)来允许服务访问应用程序中的信息。如果您想让服务以这种方式与应用程序交互,委托(delegate)将无法解决问题,因为应用程序和服务位于不同的进程中。如果确实需要,可以使用 Duplex WCF 服务。但我认为,在您按上述方式重构设计后,您不需要从服务访问应用程序的可能性很高。
关于c# - 未将对象引用设置为对象的实例 - WCF 服务和委托(delegate)(WCF 在委托(delegate)实例化之前托管),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20715604/