J4N
J4N

Reputation: 20737

Cannot access a disposed object when calling callback method

I've a Duplex TCP IP WCF service. I'm currently unit-testing it.

In everyone of my test, I setup a new server, create a new ChannelFactory, create the InstanceContext and do the call.

Then I trigger the event(it's a Mock on the server side), and the server give me this exception when it tries to reach the client:

Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll Additional information: Cannot access a disposed object.

Important point, this happens ONLY when I run all the tests in a row(sequentially executed but in the same execution).

There is nothing special about my service:

[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
    }
}

And here is how I'm currently UnitTesting it(not exactly, I've a lot of this that has been extracted in some helpers:

[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()
    }
}

Funny part is that all of this works when I'm ONLY running TestCallback test, but if I run all the test of the class, it fails, like if the second time, the InstanceContext was not creating properly the callback.

Any idea how to avoid this?

Upvotes: 0

Views: 1100

Answers (1)

J4N
J4N

Reputation: 20737

I finally found the issue. I feel a little bit stupid, but in fact, in the Service implementation, I was not unregistering from the OnMessage correctly, so when the event was triggered, the previous service instance were trying to communicate with the already closed client.

Upvotes: 2

Related Questions