Note that there are some explanatory texts on larger screens.

plurals
  1. POServiceStack + 3rd Party COM Inteop + Context disconnected exception
    text
    copied!<p>I'm getting Context Disconnected errors when testing my ServiceStack service. I assume it's due to failing race conditions between the GC for the COM object's response callback thread, my ServiceStack Service's objects, and the COM server's own garbage collection. </p> <p><strong>Edit:</strong> It's most likely the same problem explained here: <a href="https://stackoverflow.com/q/8067669/149060">Avoiding disconnected context warning when shutting down a thread on which STA COM objects have been created</a> -- which recommends I implement "some reference counting to keep the worker thread alive until all of it's COM objects have definitely been released" <em>(option #1 - re-write the COM objects to support the MTA threading model is not possible, given that the COM objects are from a 3rd party library.)</em> </p> <p><strong>Edit (2):</strong> Judicious use of <code>Marshal.ReleaseComObject(obj)</code> within the callback method eliminated the problem. It was fortunate that the COM objects in question were clearly identifiable, and in in limited number.</p> <p><strike>1. How can I prevent Disconnected Context exceptions from occurring?</strike><br> 2. What is the lifecycle of a ServiceStack service object, with regard to threads and lifetime? </p> <p>The test below passes. However, if the request takes a long time to return (for values of 'long time' > 30 seconds), I get a disconnected context error after the test completes, half the time. </p> <pre><code> [TestFixtureSetUp] public void OnTestFixtureSetUp() { // TODO: remove default login credentials from code // Instantiate singleton wrapper to COM object var appSettings = new AppSettings(); var config = appSettings.Get("3rdPartyLogin", new Config { UserName = "debug_username", Password = "debug_password" }); COMServer.SetUser(config.UserName,config.Password); appHost.Init(); appHost.Start(ListeningOn); } [TestFixtureTearDown] public void OnTestFixtureTearDown() { appHost.Dispose(); } [Test] public void TestDataList() { JsonServiceClient client = new JsonServiceClient(BaseUri); client.ReadWriteTimeout = new TimeSpan(0, 10, 0); // 5 minutes to timeout DataList response = client.Get(new DataList()); Assert.Contains("Expected Item", response.data); } </code></pre> <p>My ServiceStack service passes a request class instance to the COM server. The class implements a callback method to process the response. My ServiceStack service creates an AutoResetEvent, passes it into the 3rd party service's request object, and calls WaitOne() to wait for the response data. The callback method is executed, asynchronously, in a new thread and calls Set() to notify the ServiceStack service that the data has been processed. <em>(Error handling is similar - code omitted for clarity.)</em> Here are the simplified ServiceStack service and the COM Object's DataClient class, with required callback method.</p> <pre><code>public class DataListService : Service { public DataList Get(DataList request) { ComDataClient c = new ComDataClient(); try { ComDataService data = COMServer.getDataService(); if (data != null) { AutoResetEvent requestEvent = new AutoResetEvent(false); c.requestEvent = requestEvent; data.setClient(c); data.getData(ComObjClass.enumDataId); requestEvent.WaitOne(); } } catch (Exception ex) { Console.WriteLine("Error Connecting to Data Service: " + ex.Message); } return c.responseData; } } </code></pre> <p>And the COM object's request class, illustrating the callback.</p> <pre><code>class ComDataClient : IDataClient { public DataList responseData { get; set; } public AutoResetEvent requestEvent { get; set; } public void acceptData(ref KeyValue[] names, ComObjClass.Content enumDataId) { responseData = new DataList(); responseData.data = new List&lt;String&gt;(); foreach (KeyValue name in names) { responseData.data.Add(name.key_); } // Signal the application thread requestEvent.Set(); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload