Search code examples
c#web-serviceswcfoperationcontractmessagecontract

Upload file with messagecontract


I have been following the various tutorials that explain how to upload files via WCF and I am having some issues when I try to share the classes containing MessageContract between the client and the server.

Basically my issue is as follows:

I have a RemoteFileInfo MessageContract class inside of a separate class library:

[MessageContract]
public class RemoteFileInfo : IDisposable
{

    private System.IO.Stream fileByteStream;

    [MessageBodyMember(Order = 1)]
    public System.IO.Stream FileByteStream
    {
        set { this.fileByteStream = value; }
        get { return this.fileByteStream; }
    }

    /*More properties here declared as regular properties or [MessageHeader]*/
}

I then share that library with both the server and the client. This should mean that the client should not implement its own RemoteFileInfo class. However, after I add the .dll to my client project and then use Add Service Reference with Reuse Types enabled, it still remakes the RemoteFileInfo class inside of the service reference's namespace which creates ambiguity between the itself and the type in the class library I originally declare it in.

So my question is, how can I share a class that has been declared as a MessageContract between the client and the server? I need to do this because my OperationContract requires a RemoteFileInfo object and I would like to keep the same properties that I have declared in the original RemoteFileInfo class.

My OperationContract:

    [OperationContract]
    void uploadFile(RemoteFileInfo request);

I want to be able to call that method on the client exactly how it is declared in the service contract. So far even when I toy around with the Generate message headers option when adding the service reference, I end up with a method uploadFile that has a byte[] parameter instead of a stream, and I need it to be a stream.

Any ideas?

Edit: I was going to post a client config but I need to mention that I am developing for a Windows Store App so I do not have the traditional app.config.

Heres what the WCF Test client generated for a client config anyway:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IRTMobileWebService" sendTimeout="00:05:00" />
      </basicHttpBinding>
      <netTcpBinding>
        <binding name="uploadEndpoint" sendTimeout="00:05:00" transferMode="Streamed">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="http://ipaddress/RTWebServices/RTMobileWebService.svc/basic"
          binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IRTMobileWebService"
          contract="IRTMobileWebService" name="BasicHttpBinding_IRTMobileWebService" />
      <endpoint address="net.tcp://ipaddress/RTWebServices/RTMobileWebService.svc/upload"
          binding="netTcpBinding" bindingConfiguration="uploadEndpoint"
          contract="IRTMobileImageService" name="uploadEndpoint">
        <identity>
          <dns value="localhost" />
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

How would I replicate this in a windows store app with no app.config?


Solution

  • If you're sharing a dll with the service interface already in it, then you shouldn't bother adding a service reference using the wizard - it'll miss stuff a good portion of the time and creates a whole heck of a lot more code than you need, including a completely new instance of the service interface, in a lot of cases.

    Instead, in the application that consumes the service, create your own proxy class for the service like this:

    class ClientServiceClass : ClientBase<ITheServiceContract>, ITheServiceContract
    {
    
        public ClientServiceClass ( string endpointConfigurationName ) :
            base( endpointConfigurationName )
        {
        }
    
        internal void uploadFile(RemoteFileInfo request)
        {
            Channel.uploadFile(request);
        }
    }
    

    Where ITheServiceContract is the interface from your shared dll that is the service contract, and ClientServiceClass is whatever you want to call your implementation of the service proxy class (the wizard usually calls it by the name of the service).

    To use the service in your code, do it like this:

    NetTcpBinding binding = new NetTcpBinding();
    binding.TransferMode = TransferMode.Streamed;
    binding.Security = new NetTcpSecurity();
    binding.Security.Mode = SecurityMode.None;
    EndpointAddress address = new EndpointAddress("net.tcp://serviceEndpointAddress");
    ClientServiceClass uploadClient = new ClientServiceClass(binding, address);
    uploadClient.Open( );
    uploadClient.uploadFile( yourFileInfoRequest );
    uploadClient.Close( );