2013-12-21 66 views
2

我使用委託來允許我的服務獲取存儲在表單中的字符串。 當我運行代碼,我的服務日誌規定如下:對象引用未設置爲對象的實例 - WCF服務和委託(實例化委託之前託管的WCF)

System.NullReferenceException: Object reference not set to an instance of an object.

diData.DIDataCompressed = GetDIData();

我想在我的窗體的構造在DataServer = new ModelDataService();設置斷點指點。並注意到我的WCF服務在我的窗體的構造函數的代碼運行之前已經啓動(我從托盤中的WcfSvcHost彈出,並且在WCF服務主機窗口中它已經啓動了ModelDataService),所以委託顯然未被實例化。 更新:在我的Program.cs中的Main()方法被調用(啓動表單的方法)之前,服務正在啓動。另外,我的解決方案的唯一啓動項目就是我的表單!

如何讓我的WCF服務只在我的表單加載時啓動(這樣我才能正確設置代理)?

這裏是我的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> 

感謝您的幫助!

UPDATE

我加了負載事件到我的形式。我仍然收到同樣的錯誤。我嘗試在我的表單(啓動表單的方法)的Program.cs中的Main()方法上設置一個斷點,然後在調用Program.cs中的main方法之前啓動服務!

問題是,每次我的客戶端調用服務時都會創建服務的新實例嗎?我已經把它設置成了一個單身人士,但是我做錯了嗎?

更新2

我認爲我可能不小心讓自己的項目中WCF服務應用程序,而不是一個WCF服務庫(我可能擁有一個表單內)。我的項目的bin包含項目名稱中的.dll。我相信這意味着它確實是一個圖書館。如果我錯了,請糾正我。

再次感謝!

+0

你試圖完成什麼目標?有一個WCF服務的原因是什麼?服務通常獨立於應用程序運行,以便多個應用程序可以訪問它。基於這種n:1關係,我不明白爲什麼你希望在應用程序和服務之間有如此緊密的耦合關於啓動和初始化。如果其他用戶啓動應用程序會怎樣?你確定你需要WCF還是一個類庫? – Markus

+0

WCF服務從網站中抓取數據並需要將其發送到多個應用程序。必須將它附加到帶有Web控件的表單才能執行此操作。 – janderson

回答

1

我的回答基於評論中提供的附加信息。
有一個WCF服務訪問用戶界面窗口或其組件是非常不尋常的,因爲服務應該在服務器的後臺運行,並且不得干擾用戶界面(有許多使用過程序員的故事消息框在調試服務時忘了在部署解決方案之前刪除它們,因爲它應該是以消息框阻止服務器結束的)。
出於這個原因,我建議以下方法(順序是意圖):

  1. 儘量做到在服務中刮UI少的方式,例如通過使用WebClient或類似的東西來獲取原始HTML文檔並分析其內容。
  2. 如果您需要讓用戶與WebBrowser進行交互以便能夠訪問您想要廢棄的文檔(例如用於身份驗證),請使用與現在一樣的單獨應用程序並將結果存儲在數據庫中。但是獨立於服務使用應用程序。該服務只能返回存儲在數據庫中的數據(可能帶有時間戳,以便在檢索數據時通知用戶,並且如果數據太舊則可以作出反應)。
  3. 自行託管應用程序中的WCF服務。由此你可以以你想要的方式組合應用程序和服務。缺點是隻有應用程序運行時纔可以訪問服務 - 但您可以按照自己的需要控制和同步應用程序和服務的生命週期。
  4. 如果上述兩種方法都不起作用,請將您的依賴關閉,以便服務啓動應用程序,而不是相反。 WCF服務將是僅運行一次並控制應用程序的中心點。如果您嘗試讓應用程序在服務器上註冊自己,如果用戶多次啓動應用程序,則會遇到麻煩。

還有一件事:您提到您希望使用委託來允許服務訪問應用程序中的信息。如果您想讓服務以這種方式與應用程序進行交互,那麼由於應用程序和服務存在於不同的進程中,因此代表不會這樣做。如果你真的需要這個,你可以使用Duplex WCF服務。但是我認爲,在重新設計上述設計之後,您不需要從服務訪問應用程序的概率很高。

+0

感謝Markus的出色答案。我選擇了簡單地讓我的WCF服務本身開始,我只是添加了我的刮板表單現在調用的「設置數據」操作合同。所以WCF服務有2個客戶端,1個設置數據(刮板)和我使用刮取數據的其他程序。 – janderson

+0

@Watson:不客氣 - 很高興知道它有幫助。 – Markus