How can I force WCF to put a type into the Client Proxy if it is not Used in the Service Contract?

StackOverflow https://stackoverflow.com/questions/1050148

  •  20-08-2019
  •  | 
  •  

Question

I have declared an enum in my server code, and would like my client code to be able to use it. Unfortunately, it's not being auto-generated for some reson. My enum is declared similar to the following example:

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

It's not declared inside a class, but alongside several classes that are auto-generated as well (all in the same namespace). I have no problem using these classes in my client code, but this enum isn't being generated, and therefore is not usable. Help!

Thanks!!

EDIT:

As of now, the service neither takes a "MyEnum" as a parameter anywhere, or returns it from a function. That's my problem. It's used several places in my server code, and I'd like to use it in a few places in my client code as well (without having to copy/paste an existing construct).

Was it helpful?

Solution 3

The easiest solution I came up for this problem was to create a simple WCF function that takes a MyEnum parameter and returns it. As a result, "MyEnum" will be exposed to my client.

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

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

Any alternatives that don't require this and/or are more graceful would be appreciated!

OTHER TIPS

I encountered this exact problem the solution was not that straight forward.

I did the following:

  • Define my own ServiceHostFactory for my service
  • In the factory grab the metadata behaviour from the service contract description and replace the built-in MetadataExporter with a custom implementation
  • In the Custom MetaDataExporter override the GetGeneratedMetadata function and manipulate the generated XML schema for my services directly
  • Using reflection on the types exposed by the service I could discover all the enums I wanted and add them to the Schema
  • Now when I add the service to a client and the proxy is built it will include the enums
  • I made the process generic by defining my own custom DataEnumAttribute which I could apply to enums I wanted to include

To my mind this is a deficiency in the wcf service metadata generator - it should be configurable to add enums defined in types but never referenced by any contract properties.

The full code is a bit too long to post but here is the basic idea:

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

Thought I would throw in what I found in relation to this. Reading about the KnownType attribute, the deserializer has to have reason to think that the type you reference with KnownType might be passed. If the deserializer knows all of the types in your data contracts or in your method parameters, it doesn't need your stinkin' "KnownTypes" to do its job, and so it ignores them. If you have "object" defined as a type somewhere in your service.. uh-oh, poor ol' deserializer doesn't have a clue what type might be coming through there so it eats up all your known types and spits them out in the client proxy.

A hack? Oh yeah. If you are desperate, does it work? Yep.

In the kind of separation concerns client server model I assume you're using, you need some shared assembly between your client and server to store items used in both, like this enum and other classes they will both need to understand. A good way to do this might be using interfaces (won't help with the enum, but that can be thrown into the shared assembly).

Seems to me there are a couple options. If the service interface (ServiceContract) exposes a parameter or return value with a typeof equal to the enum, then the client will get the enum via the service definition. In practical terms, the client-side code generated from the WSDL by svcutil will include an enum type that corresponds to the thing used in the interface.

If the enum type is not used anywhere in the public interface of the service, then you can rely on an assembly of shared types.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top