【问题标题】:Cannot access a disposed object when calling callback method调用回调方法时无法访问已释放的对象
【发布时间】:2017-07-27 12:05:45
【问题描述】:

我有一个双工 TCP IP WCF 服务。我目前正在对其进行单元测试。

在我的每个测试中,我设置了一个新服务器,创建一个新 ChannelFactory,创建 InstanceContext 并进行调用。

然后我触发事件(它是服务器端的 Mock),当它试图到达客户端时,服务器给我这个异常:

抛出异常:mscorlib.dll 中的“System.ObjectDisposedException” 附加信息:无法访问已处置的对象。

重要的一点,只有当我连续运行所有测试时才会发生这种情况(按顺序执行但在同一执行中)。

我的服务没有什么特别之处:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IMyServiceCallback))]
public interface IMyService{
    [OperationContract]
    void SomeVariousMethods();
} 

[ServiceContract]
public interface IMyServiceCallback
{
    [OperationContract]
    void HandleMessageFromServer(String message);
}


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MyService : IMyService{
    public MyService(ISomeServerComponent component){
        component.OnMessage += OnMessageReceived;
    }

    public void SomeVariousMethods(){
        //...
    }

    private void OnMessageReceived(object sender, EventArgs<String> e){
        IMyServiceCallback callback = OperationContext.Current.GetCallbackChannel<IMyServiceCallback>();
        callBack.HandleMessageFromServer(e.Data);//Crash here
    }
}

这就是我目前对它进行单元测试的方式(不完全是,我已经在一些帮助程序中提取了很多这样的内容:

[TestFixture]
public class MyServiceTest:IMyServiceCallback{

    private Mock<ISomeServerComponent> _mock;

    [OneTimeSetUp]
    public void Setup(){
        //... Creating a mock for the ISomeServerComponent that the MyService receives
    }

    [Test]
    public void TestSomeVariousMethods(){
        string serviceName = nameof(TestSomeVariousMethods);
        using(ServiceHost host = CreateServer(_mock.Object,serviceName)){
            using (IMyService service = CreateClient(serviceName, this)){
                service.SomeVariousMethods();
            }
        }
    }

    [Test]
    public void TestCallback(){
        string serviceName = nameof(TestSomeVariousMethods);
        using(ServiceHost host = CreateServer(_mock.Object,serviceName)){
            using (IMyService service = CreateClient(serviceName, this)){
                _mock.TriggerCallBack();
                //Assert-that-the-flag-has-been-set
            }
        }
    }

    public void HandleMessageFromServer(String msg){
        //Flag that this method has been called
    }




    private ServiceHost CreateServer(ISomeServerComponent mock, string serviceName){
        UnityServiceHost serviceHost = new UnityServiceHost(m_container);//This extends ServiceHost to be able to inject some objects to my services
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
        binding.ReliableSession.Enabled = true;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
        binding.MaxBufferPoolSize = Int64.MaxValue;
        binding.MaxBufferSize = Int32.MaxValue;
        binding.MaxReceivedMessageSize = Int32.MaxValue;

        Uri uri = new Uri(String.Format("net.tcp://{0}:{1}/{2}", IPAddress.Any, 9999, serviceName));

        ServiceEndpoint serviceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(IMyService)), binding, uri);
        serviceEndpoint.EndpointBehaviors.Add(new ProtoEndpointBehavior());

        serviceHost.AddServiceEndpoint(serviceEndpoint);
        return serviceHost;
    }

    private IMyService CreateClient(string serviceName, IMyServiceCallback callback){
        UnityServiceHost serviceHost = new UnityServiceHost(m_container);//This extends ServiceHost to be able to inject some objects to my services
        NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
        binding.ReliableSession.Enabled = true;
        binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;
        binding.MaxBufferPoolSize = Int64.MaxValue;
        binding.MaxBufferSize = Int32.MaxValue;
        binding.MaxReceivedMessageSize = Int32.MaxValue;

        Uri uri = new Uri(String.Format("net.tcp://{0}:{1}/{2}", IPAddress.Loopback, 9999, serviceName));


        InstanceContext context = new InstanceContext(callBack);
        DuplexChannelFactory channelFactory = new DuplexChannelFactory<T>(context, binding, new EndpointAddress(uri));
        return channelFactory.CreateChannel()
    }
}

有趣的是,当我只运行TestCallback 测试时,所有这些都有效,但如果我运行该类的所有测试,它就会失败,就像第二次,InstanceContext 没有正确创建回调。

知道如何避免这种情况吗?

【问题讨论】:

  • 我只是规划了你的代码,但是当涉及到 tcp 时,你会得到对象处理异常,尤其是当套接字没有正确关闭时
  • @EmrahSüngü 好吧,我在所有处置中都设置了一些断点,一切都已正确关闭。我感觉它尝试为第二个单元测试的回调重用相同的“透明代理”(并且当第二个测试运行时,第一个 InstanceContext 已经被释放
  • @测试是一个接一个地运行还是同时运行?如果同时,由于 tcp 是阻塞的,就不可能绑定到同一个套接字
  • @EmrahSüngü 一个接一个

标签: c# wcf duplex


【解决方案1】:

我终于找到了问题所在。感觉有点傻,但其实在Service的实现中,我并没有正确的从OnMessage注销,所以触发事件的时候,之前的服务实例是在尝试和已经关闭的客户端通信。

【讨论】:

  • 很高兴你找到了它:)
猜你喜欢
  • 2015-03-16
  • 2020-06-25
  • 1970-01-01
  • 2020-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多