I have Googled and read for hours now and I can't find anyone that deals with my specific scenario...
I want to use interfaces in my WCF service contracts to loosely couple the service from the classes used on each end of the wire. This will enable us to have a low-level assembly that contains just the Service and Data Contracts (just interfaces) that we can hand to a consultant. On their end of the wire they can instantiate their data classes that implement our Data Contract interface, send it over the wire to us, and our WCF service will then translate/cast/whatever that incoming data into our version of a data class that implements the same interface.
Here's an example. IDataContract
contains the bare information I want to transmit over the wire. The endpoints and other WCF-specific config are all default stuff (my problems may lie in that, so I can include more of it if that's where I need to change things).
EDIT: I've included more of the code and renamed a couple classes to help it be less confusing. The Name & Namespace additions to the DataContractAttributes, as well as the two sections in the config files are new additions based on information from this blog post. If I switch to an abstract base class instead of an interface, it works. However, I'd like to get this working with an interface if possible.
Shared library (my code, shared with client authors):
public interface IDataContract
{
string MyProperty { get; set; }
}
[ServiceContract]
public interface ITestService
{
[OperationContract]
IDataContract TestSharedInterface(IDataContract clientData);
}
Client code (their's):
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ClientDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
private static void CallTestSharedInterface()
{
EndpointAddress address = new EndpointAddress("http://localhost/ServiceContractsTest.WcfService/TestService.svc");
ChannelFactory<ITestService> factory = new ChannelFactory<ITestService>("ITestService", address);
ITestService proxy = factory.CreateChannel();
((IClientChannel)proxy).Open();
IDataContract clientData = new ClientDataClass() { MyProperty = "client data" };
IDataContract serverData = proxy.TestSharedInterface(clientData);
MessageBox.Show(serverData.MyProperty);
}
Client config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfClient.ClientDataClass, ServiceContractsTest.WcfClient"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
Server code (mine):
public class TestService : ITestService
{
public IDataContract TestSharedInterface(IDataContract clientData)
{
ServerDataClass convertedClientData = (ServerDataClass)clientData;
IDataContract serverData = new ServerDataClass() { MyProperty = convertedClientData.MyProperty + " + server data added" };
return serverData;
}
}
[DataContract(Name = "IDataContract", Namespace = "http://services.sliderhouserules.com")]
public class ServerDataClass : IDataContract
{
[DataMember]
public string MyProperty { get; set; }
}
Server config:
<system.runtime.serialization>
<dataContractSerializer>
<declaredTypes>
<add type="ServiceContractsTest.Contracts.DataContracts.IDataContract, ServiceContractsTest.Contracts">
<knownType type="ServiceContractsTest.WcfService.ServerDataClass, ServiceContractsTest.WcfService"/>
</add>
</declaredTypes>
</dataContractSerializer>
</system.runtime.serialization>
I am getting a serialization error on the client call complaining about known types. Am I just missing some metadata markup in that client class? I'm at a loss as to where to even know the problem even lies, as I've tried all the searches I can think of and no one seems to have dealt with this specific scenario.
Basically, I want ClientDataClass
to serialize to <IDataContract><MyProperty>client data</MyProperty></IDataContract>
and then be able to deserialize that into a ServerDataClass
instance. This seems like it should be possible.