如果在服务合同中未使用类型,我如何强制WCF将类型放入客户端代理?

时间:2009-06-26 16:57:33

标签: .net wcf auto-generate

我在服务器代码中声明了一个枚举,并希望我的客户端代码能够使用它。不幸的是,它不会因某些共振而自动生成。我的枚举声明类似于以下示例:

[DataContract]
public enum MyEnum {
    [EnumMember]
    First = 1,
    [EnumMember]
    Second = 2
}

它没有在类中声明,而是与几个自动生成的类一起声明(所有类都在同一名称空间中)。我在客户端代码中使用这些类没有问题,但是这个枚举没有生成,因此不可用。救命啊!

谢谢!

修改

截至目前,该服务既不会将“MyEnum”作为参数放在任何位置,也不会从函数返回。那是我的问题。它在我的服务器代码中使用了几个地方,我也想在我的客户端代码中的几个地方使用它(无需复制/粘贴现有​​的构造)。

5 个答案:

答案 0 :(得分:2)

我遇到了这个确切的问题,解决方案不是那么直接。

我做了以下事情:

  • 为我的服务定义我自己的ServiceHostFactory
  • 在工厂中从服务合同描述中获取元数据行为,并使用自定义实现替换内置的MetadataExporter
  • 在Custom MetaDataExporter中覆盖GetGeneratedMetadata函数并直接操作为我的服务生成的XML架构
  • 对服务公开的类型使用反射,我可以发现我想要的所有枚举,并将它们添加到架构中
  • 现在,当我将服务添加到客户端并构建代理时,它将包含枚举
  • 我通过定义自己的自定义DataEnumAttribute使该过程变得通用,我可以将其应用于我想要包含的枚举

在我看来,这是wcf服务元数据生成器的一个缺陷 - 它应该是可配置的,以添加在类型中定义但从未被任何合同属性引用的枚举。

完整的代码有点太长而无法发布,但这是基本的想法:

Public Class CustomMetaDataFactory
Inherits System.ServiceModel.Activation.ServiceHostFactory

  '----------------------------------------------------------------------------
  ' CreateServiceHost (Overridden)
  '----------------------------------------------------------------------------
  Protected Overrides Function CreateServiceHost(ByVal serviceType As System.Type, _
                                                 ByVal baseAddresses() As System.Uri) _
                                                   As System.ServiceModel.ServiceHost
    '--------------------------------------------------------------------------
    ' Local Variables
    '--------------------------------------------------------------------------
    Dim host As System.ServiceModel.ServiceHost = _
      MyBase.CreateServiceHost(serviceType, baseAddresses)
    Dim smb As ServiceMetadataBehavior
    Dim bFound As Boolean = False

    '--------------------------------------------------------------------------
    ' Attach a meta data behaviour so that the service will publish
    ' wsdl and xsd descriptions
    '--------------------------------------------------------------------------
    Dim behavior As IServiceBehavior


    For Each behavior In host.Description.Behaviors
      If (TypeOf behavior Is ServiceMetadataBehavior) Then
        smb = CType(behavior, ServiceMetadataBehavior)
        '------------------------------------------------------------------------
        ' Replace the default MetadataExporter with our custom implementation
        '------------------------------------------------------------------------
        smb.MetadataExporter = New CustomWsdlExporter
        bFound = True
        Exit For
      End If
    Next

    If (Not bFound) Then
      smb = New ServiceMetadataBehavior
      smb.HttpGetEnabled = True
      smb.HttpGetUrl = baseAddresses(0)
      '--------------------------------------------------------------------------
      ' Replace the default MetadataExporter with our custom implementation
      '--------------------------------------------------------------------------
      smb.MetadataExporter = New CustomWsdlExporter
      host.Description.Behaviors.Add(smb)
    End If

    Return host

  End Function

End Class

Public Class CustomWsdlExporter
  Inherits WsdlExporter

  '----------------------------------------------------------------------------
  ' ExportContract
  '----------------------------------------------------------------------------
  Public Overrides Sub ExportContract(ByVal contract As System.ServiceModel.Description.ContractDescription)
    '--------------------------------------------------------------------------
    ' Local Variables
    '--------------------------------------------------------------------------
    Dim op As OperationDescription

    ' iterate the operations, collecting the return type and parameter types
    For Each op In contract.Operations
    ' Add code here to reflect the operations and the types used by them
    Next

    MyBase.ExportContract(contract)
  End Sub

  '----------------------------------------------------------------------------
  ' GetGeneratedMetadata
  '----------------------------------------------------------------------------
  Public Overrides Function GetGeneratedMetadata() As System.ServiceModel.Description.MetadataSet
    '--------------------------------------------------------------------------
    ' Local Variables
    '--------------------------------------------------------------------------
    Dim schemas As XmlSchemaSet
    Dim schema As XmlSchema

    schemas = MyBase.GeneratedXmlSchemas
    If (schemas IsNot Nothing AndAlso schemas.Count > 0) Then
      For Each schema In schemas.Schemas
    ' Add code here to manipulate each XML schema generated for the service
      Next
    End If
    Return MyBase.GetGeneratedMetadata()

  End Function



End Class

答案 1 :(得分:2)

以为我会抛出与此相关的内容。阅读KnownType属性,反序列化器必须有理由认为您可以传递使用KnownType引用的类型。如果反序列化器知道数据协定或方法参数中的所有类型,则不需要使用stinkin“KnownTypes”来完成其工作,因此它会忽略它们。如果你在服务中的某个地方定义了“对象”...呃 - 哦,糟糕的'反序列化器不知道那里可能会有什么类型的类型,所以它会吞噬你所有已知的类型并将它们吐出来客户代理。

黑客?哦耶。如果你绝望,它有效吗?是的。

答案 2 :(得分:1)

我遇到这个问题的最简单的解决方案是创建一个简单的WCF函数,该函数接受MyEnum参数并返回它。结果,“MyEnum”将暴露给我的客户。

//Declaration
[DataContract]
MyEnum GetMyEnum(MyEnum value);

//Definition
public MyEnum GetMyEnum(MyEnum value){
    return value;
}

任何不需要此和/或更优雅的替代品将不胜感激!

答案 3 :(得分:0)

在我所假设你正在使用的客户服务器模型的分离方式中,你需要在客户端和服务器之间进行一些共享程序集来存储两者中使用的项目,比如这个枚举和他们都需要理解的其他类。执行此操作的一种好方法可能是使用接口(对枚举没有帮助,但可以将其抛入共享程序集中)。

答案 4 :(得分:0)

对我来说有几种选择。如果服务接口(ServiceContract)公开typeof等于enum的参数或返回值,则客户端将通过服务定义获取枚举。实际上,svcutil从WSDL生成的客户端代码将包含与接口中使用的内容相对应的枚举类型。

如果未在服务的公共接口中的任何位置使用枚举类型,则可以依赖于共享类型的程序集。

相关问题