Search code examples
c#wcfserializationserviceinterface

Service breaking when returning an Interface type - Serialization issue?


My system is composed of the following parts:

  • A client script that communicates with a service via proxy
  • A service that implements a service contract
    This service creates a session by using/referring to Session.dll and SessionContracts.dll (all compiled from different Namespaces)

The service code runs, but the Proxy breaks on return (System.ServiceModel.CommunicationObjectFaultedException). See code here below (something to do with returning an Interface type maybe?):

Note that the channel breaks in that case, but if I return a null instead of the session, it does not break anymore.

namespace SessionContracts  
{  
    public interface ISessionBase
    {
        void Connect();
    }
}

namespace SessionNameSpace  
{  
    [DataContract]  
    //    [KnownType(typeof(SessionBase))]     // Needed???
    [KnownType(typeof(ISessionBase))]  
    public class SessionBase : ISessionBase  
    {  
        public SessionBase() 
        {  
        }     
        public void Connect()  
        {  
        }  
    }  
}  

namespace Device.ServiceContract
{
    [ServiceContract(Namespace = "http://Device.Service", CallbackContract = typeof(IDeviceServiceCallback))]
    public interface IDeviceService
    {
        [OperationContract]
        ISessionBase CreateSession(Uin16 id);
    }
}

// This code works, but break within the Proxy on return
namespace Device.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
                     ConcurrencyMode = ConcurrencyMode.Multiple)]

    public class DeviceService : IDeviceService
    {
        public DeviceService() { }

        ISessionBase CreateSession(Uin16 id) // Id not used for now
        {
            return new SessionBase();
        }
    }
}

// Proxy breaking on return
namespace Device.Proxy
{
    public class DeviceProxy : IDeviceService
    {
        public readonly IDeviceService _channel = null;

        private DeviceProxy()
        {
            // Channel factory stuffs here…
        }
    }

    private static DeviceProxy _instance = null;
    public static DeviceProxy Instance() => _instance ?? (_instance = new DeviceProxy());

    public ISessionBase CreateSession(UInt16 id)
    {
        ISessionBase a = _channel?.CreateSession(id);   // This line breaks
        return a;
    }
}    

Edit, adding exception details:

The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.

Server stack trace:  
   at System.ServiceModel.Channels.CommunicationObject.ThrowIfFaulted()  
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)  
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)  
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)

Exception rethrown at [0]:   
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)  
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)  
   at Device.ServiceContract.IDeviceService.CreateSession()  
   at Device.Proxy.DeviceProxy.CreateSession() in E:\...\Device.Proxy\DeviceProxy.cs:line 71  
   at Client1.Program.Main(String[] args) in E:\...\Client1\Program.cs:line 97  

Solution

  • I figured that CreateSession in the Service Contract needs to know more about the type of sessions that are supported (ServiceKnownType has to be used). If not done, there is no way the service can know how to serialise this type of data.

    Here is what was missing:

    namespace Device.ServiceContract
    {
        [ServiceContract(Namespace = "http://Device.Service", CallbackContract = typeof(IDeviceServiceCallback))]
        public interface IDeviceService
        {
            [OperationContract]
            [ServiceKnownType(typeof(SessionBase))]
            ISessionBase CreateSession(Uin16 id);
        }
    }