Object reference not set to an instance of an obje

2019-07-10 15:50发布

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!

1条回答
太酷不给撩
2楼-- · 2019-07-10 16:40

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):

  1. Try to do the scraping in the service a UI-less way, e.g. by using WebClient or something similar to get the raw HTML document and analyze its content.
  2. If you need to have a user interact with the WebBrowser in order to be able to get to the document you want to scrap (e.g. for authentication), use a separate application as you do now and store the result in a database. But use the application independently from the service. The service can return only the data that is stored in the database (maybe with a timestamp so that users are informed when the data was retrieved and can react if the data is too old).
  3. Self host the WCF service in the application. By that you can combine the application and the service in a way you want. Downside is that you can access the service only as long as the application runs - but you will be able to control and sync the lifecycle of the application and the service in the way you want.
  4. If neither of the first approaches work, turn around your dependencies so that the service starts the application and not the other way round. The WCF service will be the central spot that runs only once and controls the application. If you try to have the application register itself with the server, you run in trouble if a user starts the application several times.

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.

查看更多
登录 后发表回答