2

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

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

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

谢谢!!

编辑:

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

4

5 回答 5

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
于 2011-04-05T13:29:09.510 回答
2

以为我会抛出与此相关的发现。阅读有关 KnownType 属性的信息,反序列化器必须有理由认为您使用 KnownType 引用的类型可能会被传递。如果反序列化器知道你的数据契约或方法参数中的所有类型,它就不需要你的臭名昭著的“KnownTypes”来完成它的工作,因此它会忽略它们。如果您将“对象”定义为服务中某处的类型.. 哦,可怜的反序列化器不知道那里可能会出现什么类型,所以它会吃掉您所有已知的类型并将它们吐出来客户端代理。

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

于 2011-08-15T13:44:58.293 回答
1

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

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

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

任何不需要这个和/或更优雅的替代方案将不胜感激!

于 2009-06-26T17:15:50.560 回答
0

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

于 2009-06-26T17:23:34.733 回答
0

在我看来,有几个选择。如果服务接口(ServiceContract)暴露了一个typeof等于枚举的参数或返回值,那么客户端将通过服务定义获取枚举。实际上,svcutil 从 WSDL 生成的客户端代码将包含一个与接口中使用的事物相对应的枚举类型。

如果枚举类型没有在服务的公共接口中的任何地方使用,那么您可以依赖共享类型的程序集。

于 2009-06-26T19:05:38.033 回答