【问题标题】:using a wcf service that contains a DataContract inside Excel VBA在 Excel VBA 中使用包含 DataContract 的 wcf 服务
【发布时间】:2011-11-03 21:13:02
【问题描述】:

你读了标题并呻吟着。没关系。我也是。但是我们按照我们的要求去做,对吧?我需要构建一个可以通过 Excel 中的名字对象访问的服务(2003,但我假设任何版本的 Excel 都应该支持此功能)。目前,我要做的就是将电子表格发布数据到从远程计算机上的 Windows 服务运行的 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 代码将在 Public Sub PutData 方法中失败,并显示以下内容:

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

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

从我读过的内容来看,一种解决方案似乎是将其设为 COM 对象并引用 DLL。但是,这对我的环境来说不是一个可行的解决方案。有没有其他方法可以解决这个问题?

【问题讨论】:

  • 昨晚我有了另一个想法。假设我创建了两个不同的接口——一个 Excel 可以安全调用而无需担心额外类的“只写”接口,以及一个向客户端提供更多详细信息(包括此类定义)的“服务”接口?这样一来,Excel 将永远不必知道数据合同之类的东西——它只需将一个字符串发布到服务并完成它。

标签: vb.net wcf excel vba


【解决方案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

这需要更改 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/"""

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

【讨论】:

  • 谢谢你,你帮了我很多忙!
【解决方案2】:

我们还没有遇到过这种特定的场景,但是我们在 WCF 上做了很多工作,我们控制了服务和消费者(silverlight、平板电脑操作系统、单声道的 ipad 应用程序等)。

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

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

【讨论】:

  • 我会试一试。昨晚我有了另一个想法,可能是一种笨拙的方式,但可以让我纯粹通过代码来做到这一点。请参阅我上面的评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-03
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多