I am using delegates to allow my service to grab a string that is stored in my form. When I run the code my service log states the following:
System.NullReferenceException: Object reference not set to an instance of an object.
Pointing towards diData.DIDataCompressed = GetDIData();
I tried setting a breakpoint at DataServer = new ModelDataService();
in my form's constructor. And noticed that my WCF service was started (I got the popup from WcfSvcHost in tray and also in the WCF Service Host window it says ModelDataService has been started) before my form's constructor's code was run, so the delegate had obviously not been instantiated. Update: The Service is being started before the Main() method in my Program.cs is called (the method that starts the form). Also, my solution's only start up project is my form!
How can I get my WCF Service to only start when my form loads (so I can set the delegate correctly)?
Here's my WCF Service Code:
[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;
}
And my form code that should start the service:
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
}
My 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>
Thanks for your help!
UPDATE
I added a load event to my form. I am still getting the same error. I tried setting a breakpoint on the Main() method in my Program.cs for my form (the method that starts the form) and the service is started before the main method in Program.cs is even called!
Could the problem be that a new instance of the service is created every time my client calls the service? I have set it up to be a singleton but have I done it incorrectly?
UPDATE 2
I thought that I may have accidentally made my project a WCF Service Application, and not a WCF Service Library (which I could host inside a form). The bin for my project contains a .dll in the project's name. I believe that this means that it is indeed a Library. Please correct me if I'm wrong.
Thanks again!
My answer is based upon the additional information provided in the comments.
It is very unusual to have a WCF service access a user interface window or its components as a service is supposed to run in the background on a server and must not interfere with the user interface (there are lots of stories of programmers who used message boxes when debugging a service and forgot to remove them before deploying the solution. As it was supposed to, this ended in message boxes blocking the server).
For that reason I'd suggest the following approaches (the order is by intent):
One more thing: you mention that you want to use delegates to allow the service to access the information in the application. If you want to have the service interact with the application in this way, delegates won't do the trick as the application and the service live in different processes. You could use a Duplex WCF service if you really need this. But I think the probability is high that you won't need to access the application from the service after you restructured your design as written above.