2011-11-03 56 views
3

您阅读标题并呻吟。没关系。我也是。但我们会按照我们的要求做,对吧?我需要构建一个服务,可以从Excel(2003,但我假设任何版本的Excel应该支持此功能)通过一个名字对象访问。目前我所要做的只是将电子表格发布数据发送到从远程计算机上的Windows服务运行的WCF服务。因为这些数据需要通过比VBA更复杂的东西来检索,所以我决定建立一个数据合同。这里是我的代码(目前这只是一个概念验证,但它与完成时需要看的密切相关)。使用包含Excel中的DataContract的wcf服务VBA

这里的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代码在失败方法如下:

“MessagePartDescription Name ='GetWhatsResult'的实例无法在此上下文中使用:必需'Type'属性未设置。

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

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

+0

昨晚我又有一个念头。假设我创建了两个不同的接口 - 一个是Excel可以安全调用的“只写”接口,而不用担心额外的类,另外一个“服务”接口向客户端提供更多详细信息,包括此类定义。这样,Excel永远不需要知道数据合同以及什么 - 它只会将一个字符串发布到一个服务上并且完成它。 – mounty

回答

1

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

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 

这需要在应用程序的变化。配置使两个独立的端点可以在同一个地址进行操作:

<?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/""" 

我用WCF测试客户端测试了这个配置。我不得不手动为它提供mex端点,但是当我这样做时,它拿起了两份合同。我使用PutData方法稍微填充服务类,然后进入Excel并填充一些。我回到了WCF测试客户端,运行了GetData函数,它返回了从测试客户端和Excel中添加的所有项目。

+0

谢谢你,你帮了我很多! –

0

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

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

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

+0

我会给它一个镜头。我昨晚得到了另一个想法,可能是一种笨拙的方式,但会允许我纯粹通过代码来完成。看到我上面的评论。 – mounty