ServiceStack サービスをテストするときに Context Disconnected エラーが発生します。COM オブジェクトの応答コールバック スレッド、ServiceStack サービスのオブジェクト、および COM サーバー自体のガベージ コレクションの GC 間の競合状態が失敗したためだと思います。
編集:ここで説明されているのと同じ問題である可能性が最も高い: STA COM オブジェクトが作成されたスレッドをシャットダウンするときに切断されたコンテキストの警告を回避する- これは、「すべての COM オブジェクトになるまで、ワーカー スレッドを存続させるためにいくつかの参照カウントを実装することをお勧めします」確実にリリースされています」(オプション #1 - COM オブジェクトがサード パーティのライブラリからのものであるため、MTA スレッド モデルをサポートするように COM オブジェクトを書き直すことはできません。)
編集(2):コールバックメソッド内での賢明な使用Marshal.ReleaseComObject(obj)
により、問題が解消されました。問題の COM オブジェクトが明確に識別でき、しかも数が限られていたことは幸運でした。
1. 切断されたコンテキストの例外が発生しないようにするにはどうすればよいですか?
2. スレッドとライフタイムに関して、ServiceStack サービス オブジェクトのライフサイクルはどのようなものですか?
以下のテストはパスします。ただし、リクエストが返されるまでに長い時間がかかる場合 (「長い時間」の値が 30 秒を超える場合)、テストの完了後に切断されたコンテキスト エラーが半分の時間発生します。
[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);
}
私の ServiceStack サービスは、要求クラスのインスタンスを COM サーバーに渡します。このクラスは、応答を処理するためのコールバック メソッドを実装します。私の ServiceStack サービスは AutoResetEvent を作成し、それをサードパーティ サービスの要求オブジェクトに渡し、WaitOne() を呼び出して応答データを待ちます。コールバック メソッドは新しいスレッドで非同期に実行され、Set() を呼び出して、データが処理されたことを ServiceStack サービスに通知します。 (エラー処理も同様です - わかりやすくするためにコードは省略されています。) 以下は、単純化された ServiceStack サービスと、必要なコールバック メソッドを備えた COM オブジェクトの DataClient クラスです。
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;
}
}
そして、コールバックを示す COM オブジェクトの要求クラス。
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<String>();
foreach (KeyValue name in names)
{
responseData.data.Add(name.key_);
}
// Signal the application thread
requestEvent.Set();
}
}