在测试我的 ServiceStack 服务时,我遇到了上下文断开错误。我认为这是由于 COM 对象的响应回调线程、我的 ServiceStack 服务对象和 COM 服务器自己的垃圾收集之间的竞争条件失败。
编辑:这很可能与此处解释的相同问题:在关闭已创建 STA COM 对象的线程时避免断开连接的上下文警告- 建议我实施“一些引用计数以保持工作线程处于活动状态,直到它的所有 COM 对象肯定已经发布” (选项 #1 - 重新编写 COM 对象以支持 MTA 线程模型是不可能的,因为 COM 对象来自第 3 方库。)
编辑(2):明智地使用Marshal.ReleaseComObject(obj)
内部回调方法消除了问题。幸运的是,所讨论的 COM 对象可以清楚地识别,并且数量有限。
1.如何防止发生Disconnected Context异常?
2. 关于线程和生命周期,ServiceStack 服务对象的生命周期是什么?
下面的测试通过了。但是,如果请求需要很长时间才能返回(对于 'long time' > 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,将其传递给第 3 方服务的请求对象,并调用 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();
}
}