Search code examples
c#wcfidataobject

WCF defines it's own version of a DataObject in the Service Reference


I’ve created an object that I would like to pass in a WCF call… but inside ServiceReference1… this object is redefined… is there a way to just use the original object everywhere… it seems like people have done this but I can’t figure out what I am doing wrong.

The object is used as a parameter to a function in the service contract.

    [OperationContract(IsOneWay = true)]
    void UpdateInformation(MyObject myObject);

The error that I get when I try to call the function from my client is “Argument 1: cannot convert from ‘MyNameSpaceDTO.MyObject' to ‘MyNameSpace.ServiceReference1.MyObject’”

The object is in it’s own class library dll and it is marked with [DataObject] and [DataMember] attributes.

namespace MyNameSpaceDTO
{
    [DataContract]
    public class MyObject
    {
        [DataMember]
        public string Name { get; set; }
    ….

But, also ends up in Reference.cs after adding the Service Reference as:

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="MyObject", Namespace="http://schemas.datacontract.org/2004/07/MyNameSpaceDTO")]
[System.SerializableAttribute()]
public partial class MyObject : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {

    [System.NonSerializedAttribute()]
    private System.Runtime.Serialization.ExtensionDataObject extensionDataField;

    [System.Runtime.Serialization.OptionalFieldAttribute()]
    private string NameField;
    ...

Also, I do have the following set in the Advanced section of the Add Service Reference:

[x] Reuse types in referenced assemblies

(o) Reuse types in all referenced assemblies


Solution

  • For consuming a WCF service you often see samples (and they're undoubtedly advisable!) where you're instructed to add that service via the Add Service Reference dialog. By referencing a service that way your client application creates proxy classes form the WSDL exposed by the service.

    As a result you end up having e.g. a class MyNameSpaceDTO.MyObject in your contract-assembly and a MyNameSpace.ServiceReference1.MyObject in your client application which was generated form the WSDL. This may seem somewhat redundant.

    One situation in which you may need this behaviour could be the following: Imagine you'd want to consume an arbitrary public web service which you don't control. You have no access to the contract-assembly which defines the types etc. In that situation creating your own local proxy classes from the exposed WSDL is optimal since it's your only way to get the needed types and so on.

    But your concrete situation seems to be a little bit different. I think what you're looking for is a shared contract. Since you're in control of the client and server code (and both live happily side by side in the same solution), you're in the comfortable situation to just share the contract:

    So instead of adding a service reference within your client-app (via Add Service Reference), you'd just reference the contract-assembly (via the usual Add Reference dialogue). By doing this there'll by only one MyNameSpaceDTO.MyObject since the second one is never created and not needed. This approach is called contract sharing.

    Please take a look at that example:

    enter image description here

    EDIT:

    Please note some changes: The most important one is that you usually wouldn't want to share the assembly which holds your implementation logic of your service. So I extracted that part from the Contract-assembly and put it in a separate Implementation-assembly. By doing so, you simply share the interfaces and types and not the implementation logic. This change is reflected in the screenshot above, too.

    You could set up that small solution with the following classes:

    Contract - IService1.cs:

    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(int value);
    }
    

    Implementation - Service1.cs:

    public class Service1 : IService1
    {
        public string GetData(int value)
        {
            return string.Format("You entered: {0}", value);
        }
    }
    

    Host - Program.cs:

    class Program
    {
        static void Main(string[] args)
        {
            var baseAddress = new Uri("http://localhost:8732/Design_Time_Addresses/Service1/");
    
            using (var host = new ServiceHost(typeof(Service1), baseAddress))
            {
                // Enable metadata publishing.
                var smb = new ServiceMetadataBehavior();
                smb.HttpGetEnabled = true;
                smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
                host.Description.Behaviors.Add(smb);
    
                // Open the ServiceHost to start listening for messages. Since no endpoints are
                // explicitly configured, the runtime will create one endpoint per base address
                // for each service contract implemented by the service.
                host.Open();
    
                Console.WriteLine("The service is ready at {0}", baseAddress);
                Console.WriteLine("Press <Enter> to stop the service.");
                Console.ReadLine();
    
                host.Close();
            }
        }
    }
    

    Client - Program.cs:

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press <Enter> to proceed.");
            Console.ReadLine();
    
            var binding = new BasicHttpBinding();
            var endpoint = new EndpointAddress("http://localhost:8732/Design_Time_Addresses/Service1/");
            var channelFactory = new ChannelFactory<IService1>(binding, endpoint);
    
            // Create a channel.
            IService1 wcfClient1 = channelFactory.CreateChannel();
            string s = wcfClient1.GetData(42);
            Console.WriteLine(s);
            ((IClientChannel)wcfClient1).Close();
    
            Console.WriteLine("Press <Enter> to quit the client.");
            Console.ReadLine();
        }
    }