EvilDr
EvilDr

Reputation: 9580

Instantiating a class within WCF

I'm writing a WCF WebMethod to upload files to, of which I taken snippets from around the web. The WCF interface looks like this:

<ServiceContract()>
Public Interface ITransferService

    <OperationContract()>
    Sub UploadFile(ByVal request As RemoteFileInfo)

End Interface

<MessageContract()>
Public Class RemoteFileInfo
    Implements IDisposable

    <MessageHeader(MustUnderstand:=True)>
    Public FileName As String

    <MessageHeader(MustUnderstand:=True)>
    Public Length As Long

    <MessageBodyMember(Order:=1)>
    Public FileByteStream As System.IO.Stream

    Public Sub Dispose() Implements IDisposable.Dispose
        If FileByteStream IsNot Nothing Then
            FileByteStream.Close()
            FileByteStream = Nothing
        End If
    End Sub

End Class

Within ASP.NET, when the web method is consumed, for some reason it only works when the interface is used as part of the instantiation of RemoteFileInfo:

Protected Sub btn_Click(sender As Object, e As EventArgs) Handles btn.Click
    If fu.HasFile Then
        Dim fi As New System.IO.FileInfo(fu.PostedFile.FileName)

        ' this is the line in question --------------
        Dim cu As ServiceReference1.ITransferService = New ServiceReference1.TransferServiceClient()
        ' -------------------------------------------

        Dim uri As New ServiceReference1.RemoteFileInfo()
        Using stream As New System.IO.FileStream(fu.PostedFile.FileName, IO.FileMode.Open, IO.FileAccess.Read)
            uri.FileName = fu.FileName
            uri.Length = fi.Length
            uri.FileByteStream = stream
            cu.UploadFile(uri)
        End Using
    End If
End Sub

Can anyone advise why it is not possible to create an instance of TransferService using the following approach:

Dim cu As New ServiceReference1.TransferServiceClient()

If I try the above, it breaks this line:

cu.UploadFile(uri)

...and UploadFile must be called with three parameters (FileName, Length, FileByteStream) even there is no method that uses this signature.

Why is the Interface required when creating an instance of this class please?

Upvotes: 2

Views: 416

Answers (2)

competent_tech
competent_tech

Reputation: 44931

The issue is that when a MessageContract is encountered as a parameter, the WCF client generation assumes by default that you want to implement a messaging-style interface, and provides the discrete properties from the message contract as part of the client-side interface.

The Using Messaging Contracts article in MSDN contains a very detailed description of what can be done with a messaging contract and I suspect that Microsoft chose this default behavior because of some of the "games" that you can play with the messages.

However, if you examine the code generated for your UploadFile on the client side, there are some interesting tidbits that help to explain what is going on.

The first is the comments for the UploadFile method in the interface:

    'CODEGEN: Generating message contract since the operation UploadFile is neither RPC nor document wrapped.
    ...
    Function UploadFile(ByVal request As ServiceReference1.RemoteFileInfo) As ServiceReference1.UploadFileResponse

This implies that the contract would have been generated differently if the message contract had a different implementation.

The second is that you will see that there is nothing special about the code that is used to actually make the service call:

    Public Sub UploadFile(ByVal FileName As String, ByVal Length As Long, ByVal FileByteStream As System.IO.Stream)
        Dim inValue As ServiceReference1.RemoteFileInfo = New ServiceReference1.RemoteFileInfo()
        inValue.FileName = FileName
        inValue.Length = Length
        inValue.FileByteStream = FileByteStream
        Dim retVal As ServiceReference1.UploadFileResponse = CType(Me,ServiceReference1.ITransferService).UploadFile(inValue)
    End Sub

So in this case, your code is doing exactly what the generated code does. However, if the MessageContract were more complex, I suspect that this would no longer be the case.

So, for your question:

Can anyone advise why it is not possible to create an instance of TransferService using the following approach...

There is no reason not to take this approach as long as you verify that the implementation of the method call is functionality equivalent to your code.

There are a couple of options for changing the default generation of the method in the client:

1) Remove the MessageContract attribute from the RemoteFileInfo class.

2) Although it seems to be counter-intuitive, you can check the Always generate message contracts checkbox in the Configure Service Reference Dialog Box.

Upvotes: 1

carlosfigueira
carlosfigueira

Reputation: 87228

When you create the proxy for your service with the "Add Service Reference" dialog, by default the proxy creation code will "unwrap" message contracts, like the one you have. If you want the message contract to appear as you defined on the server side on your proxy, you need to select the "Advanced" tab, and check the "Always generate message contracts" option. With that you'll get the message contract in your client as well.

Upvotes: 2

Related Questions