使用在Excel VBA中包含DataContract的wcf服务

你读的标题和呻吟。 没关系。 我也是。 但是,我们做我们所要求的,对吧? 我需要build立一个服务,可以从Excel(2003,但我假设任何版本的Excel应该支持这个function)通过一个绰号访问。 目前我所要做的就是将电子表格发布数据发送到从远程计算机上的Windows服务运行的WCF服务。 因为这些数据需要比VBA更复杂的东西来检索,所以我决定build立一个数据合同。 这里是我的代码(目前这只是一个概念validation,但它与完成时需要看的密切相关)。

这里是WCF相关的东西:

Imports System.ServiceModel Imports System.Runtime.Serialization <ServiceContract()> Public Interface IWCF <OperationContract()> Sub PutData(ByVal what As String) <OperationContract()> Function GetWhats() As TheWhats() End Interface <DataContract()> Public Class TheWhats <DataMember()> Public Property Timestamp As DateTime <DataMember()> Public Property TheWhat As String End Class Public Class WCF Implements IWCF Shared Whats As New List(Of TheWhats) Public Sub PutData(ByVal what As String) Implements IWCF.PutData Whats.Add(New TheWhats With {.Timestamp = Now, .TheWhat = what}) End Sub Public Function GetWhats() As TheWhats() Implements IWCF.GetWhats Return Whats.ToArray End Function End Class 

我的app.config:

 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <compilation debug="true"></compilation> </system.web> <system.serviceModel> <services> <service name="DataCollectionService.WCF"> <endpoint address="" binding="netTcpBinding" contract="DataCollectionService.IWCF" /> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:9100/DataCollectionService/ "/> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration> 

和我的vba类来处理发布的东西:

 Private Const pConxString As String = _ "service:mexAddress=""net.tcp://localhost:7891/Test/WcfService1/Service1/mex"", " & _ "address=""net.tcp://localhost:7891/Test/WcfService1/Service1/"", " & _ "binding=""NetTcpBinding_IService1"", bindingNamespace = ""http://tempuri.org/"", " & _ "contract=""IService1"", contractNamespace=""http://tempuri.org/""" Public ServiceObject As Object Private Sub Class_Initialize() Set ServiceObject = GetObject(pConxString) End Sub Public Sub PutData(ByVal what As String) ServiceObject.PutData what End Sub Private Sub Class_Terminate() Set ServiceObject = Nothing End Sub 

如果我包含DataContract属性和返回数据合同对象的函数,则我的vba代码将在Public Sub PutData方法中失败,如下所示:

“MessagePartDescription Name的实例='GetWhatsResult'Namespace ='http://tempuri.org/'不能在此上下文中使用:必需'Type'属性未设置。

如果我拿出DataContract并在服务定义中注释掉这个函数,我很好。 我不打算在Excel中使用GetWhats()函数。 但是我猜测它想要TheWhats的types定义。

从我读过的一个解决scheme似乎是使这个COM对象,并引用该DLL。 但是,这不是我的环境可行的解决scheme。 有其他的方法来解决这个问题吗?

好的,回答我自己的问题。 解决scheme(至less在我的情况下)是拆分接口,并让我的服务类实现两个接口。 这是我的新接口文件:

 Imports System.ServiceModel Imports System.Runtime.Serialization <ServiceContract()> Public Interface IWCF_WriteOnly <OperationContract()> Sub PutData(ByVal what As String) End Interface <ServiceContract()> Public Interface IWCF_ReadOnly <OperationContract()> Function GetData() As TheWhats() End Interface <DataContract()> Public Class TheWhats <DataMember()> Public Property Timestamp As DateTime <DataMember()> Public Property TheWhat As String End Class Public Class WCF Implements IWCF_WriteOnly Implements IWCF_ReadOnly Shared Whats As New List(Of TheWhats) Public Sub PutData(ByVal what As String) Implements IWCF_WriteOnly.PutData Whats.Add(New TheWhats With {.Timestamp = Now, .TheWhat = what}) End Sub Public Function GetData() As TheWhats() Implements IWCF_ReadOnly.GetData Return Whats.ToArray End Function End Class 

这需要在app.config中进行更改,以便两个单独的端点可以在相同的地址上运行:

 <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.web> <compilation debug="true"></compilation> </system.web> <system.serviceModel> <services> <service behaviorConfiguration="GenericBehavior" name="DataCollectionService.WCF"> <endpoint address="wo" binding="netTcpBinding" contract="DataCollectionService.IWCF_WriteOnly" /> <endpoint address="ro" binding="netTcpBinding" contract="DataCollectionService.IWCF_ReadOnly" /> <endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" /> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:9100/DataCollectionService/" /> </baseAddresses> </host> </service> </services> <behaviors> <serviceBehaviors> <behavior name="GenericBehavior"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration> 

而且由于只写端点与需要数据约定定义的端点是隔离的,所以在Excel定义中的这种改变是必要的:

 Private Const pConxString As String = _ "service:mexAddress=""net.tcp://localhost:9100/DataCollectionService/Mex"", " & _ "address=""net.tcp://localhost:9100/DataCollectionService/wo"", " & _ "binding=""NetTcpBinding_IWCF_WriteOnly"", bindingNamespace = ""http://tempuri.org/"", " & _ "contract=""IWCF_WriteOnly"", contractNamespace=""http://tempuri.org/""" 

我用WCFtesting客户端testing了这个configuration。 我不得不手动给它的mex端点,但是当我这样做,它拿起了这两个合同。 我使用PutData方法来填充服务类,然后进入Excel并填充一些。 我回到了WCFtesting客户端,运行GetData函数,它返回了从Test Client和Excel中添加的所有项目。

我们还没有遇到这个特定的场景,但是我们在WCF上做了很多工作,我们同时控制服务和消费者(silverlight,tablet os,单声道的ipad应用等)。

我们使用的通用解决scheme是在pipe道两端的相同名称空间中具有相同的类。 我不是100%确定这会在你的环境中工作,但是在VBA中重新创build你的TheWhats类可能是值得的,看看是否对你有帮助。

如果是这样,并且VBA支持它,您可以将该类移到它自己的文件中,并从服务和客户端项目中引用它。