2012-12-19 85 views
2

我正在编写一个WCF WebMethod来上传文件,其中我从网络上截取了一些片段。 WCF的接口看起来是这样的:在WCF内实例化一个类

<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 

在ASP.NET中,当Web方法被消耗,由于某种原因,它只能在接口被用作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 

任何人都可以提出建议,为什么它是不可能使用下面的方法来创建的TransferService一个实例:

Dim cu As New ServiceReference1.TransferServiceClient() 

如果我尝试ABOV E,它打破了这一行:

cu.UploadFile(uri) 

...并UploadFile必须有三个参数(文件名,长度,FileByteStream)甚至存在使用这个签名没有方法被调用。

为什么在创建这个类的实例时需要接口?

回答

1

问题是,当遇到MessageContract作为参数时,WCF客户端生成默认情况下假定您要实现消息传递式接口,并提供消息协定中的离散属性作为客户端的一部分,侧面的界面。

MSDNDN中的Using Messaging Contracts文章包含了对消息传递协议可以做什么的非常详细的描述,并且我怀疑Microsoft选择了这种默认行为,因为可以使用这些消息来播放一些“游戏”。

但是,如果您在客户端检查为您的UploadFile生成的代码,则会有一些有趣的小技巧来帮助解释发生了什么。

首先是在接口UploadFile方法的注释:

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

这意味着,如果该消息的合同有不同的实现合同将已生成不同。

第二个是,你会看到有什么特别的地方,用于实际上使服务调用的代码:

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 

因此,在这种情况下,你的代码是做什么生成的代码确实。但是,如果MessageContract更复杂,我怀疑这将不再是这种情况。

因此,对于您的问题:

任何人都可以提出建议,为什么它是不可能使用下面的方法来创建 TransferService的实例...

没有理由不只要您确认方法调用的实现与您的代码功能相同,就可以采用此方法。

有用于改变在客户机的默认生成的方法的几个选项:

1)从RemoteFileInfo类取出MessageContract属性。 2)尽管它看起来与直觉相反,但您可以在Configure Service Reference Dialog Box中检查Always generate message contracts复选框。

+0

哇 - 惊人的答案 - 谢谢!关于您的评论: _ >>从RemoteFileInfo类中删除MessageContract属性<< _ 我需要用DataContractAttribute替换它吗? 另外,请您详细说明可以玩什么“游戏”?作为WCF的新手,我希望得到任何额外的建议,即使这意味着更多的阅读......!再次感谢 – EvilDr

+1

Re RemoteFileInfo:是的,最好使用DataContract,尽管它并不是严格要求的,因为WCF会自动处理普通的类对象(PO​​CO)。重新游戏:我主要指的是可以操纵底层SOAP消息的多种方式(加密部件,使用不同的名称空间,使用不同的序列化引擎等),这些方式可能会对客户端生成的消息产生影响,也可能不会产生影响代码(请参阅MSDN文章以深入讨论各种功能)。 –

+0

超级。非常感谢您的详细回复。 – EvilDr

2

当您使用“添加服务引用”对话框为您的服务创建代理时,默认情况下,代理创建代码将“展开”消息合约,如您拥有的代码。如果您希望消息合约按照您在代理服务器上定义的方式显示,则需要选择“高级”选项卡,然后选中“始终生成消息合约”选项。有了这个,你也可以在你的客户端获得消息合约。