Как я могу заставить WCF поместить тип в прокси клиента, если он не используется в контракте на обслуживание? - PullRequest
2 голосов
/ 26 июня 2009

Я объявил enum в своем коде сервера и хотел бы, чтобы код моего клиента мог его использовать. К сожалению, он не генерируется автоматически для какого-то резонанса. Мое перечисление объявлено аналогично следующему примеру:

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

Он не объявлен внутри класса, но наряду с несколькими классами, которые также генерируются автоматически (все в одном и том же пространстве имен). У меня нет проблем с использованием этих классов в моем клиентском коде, но это перечисление не генерируется и, следовательно, не может быть использовано. Помогите!

Спасибо !!

EDIT:

На данный момент служба нигде не принимает «MyEnum» в качестве параметра и не возвращает его из функции. Это моя проблема. Он использовал несколько мест в моем серверном коде, и я хотел бы использовать его также в нескольких местах в моем клиентском коде (без необходимости копировать / вставлять существующую конструкцию).

Ответы [ 5 ]

2 голосов
/ 15 августа 2011

Думал, что я добавлю то, что нашел в связи с этим. Читая об атрибуте KnownType, у десериализатора должна быть причина думать, что тип, на который вы ссылаетесь с KnownType, может быть передан. Если десериализатор знает все типы в ваших контрактах данных или в параметрах вашего метода, ему не нужен ваш вонючий "KnownTypes", чтобы выполнять свою работу, и поэтому он игнорирует их. Если у вас есть "объект", определенный как тип где-то в вашем сервисе ... о-о, бедный старый десериализатор не имеет ни малейшего представления, какой тип может проходить через него, поэтому он съедает все ваши известные типы и выплевывает их клиентский прокси.

Взломать? О да. Если вы в отчаянии, это работает? Да.

2 голосов
/ 05 апреля 2011

Я столкнулся с этой точной проблемой, решение которой было не таким простым.

Я сделал следующее:

  • Определите мою собственную ServiceHostFactory для моей службы
  • На фабрике возьмите поведение метаданных из описания контракта на обслуживание и замените встроенный MetadataExporter пользовательской реализацией
  • В пользовательском 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 голос
/ 26 июня 2009

Самым простым решением, которое я нашел для этой проблемы, было создание простой функции WCF, которая принимает параметр MyEnum и возвращает его. В результате «MyEnum» будет выставлен моему клиенту.

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

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

Буду признателен за любые альтернативы, которые не требуют этого и / или являются более изящными!

0 голосов
/ 26 июня 2009

Мне кажется, есть пара вариантов. Если интерфейс службы (ServiceContract) предоставляет параметр или возвращаемое значение с typeof, равным enum, то клиент получит перечисление через определение службы. С практической точки зрения код на стороне клиента, сгенерированный из WSDL с помощью svcutil, будет включать тип enum, соответствующий тому, что используется в интерфейсе.

Если тип enum не используется где-либо в общедоступном интерфейсе службы, вы можете положиться на сборку общих типов.

0 голосов
/ 26 июня 2009

В типе разделения, касающемся модели клиент-сервер, которую я предполагаю, вы используете, вам нужна некоторая общая сборка между вашим клиентом и сервером для хранения элементов, используемых в обоих, таких как enum и другие классы, которые они оба должны будут понять. Хорошим способом сделать это может быть использование интерфейсов (не поможет с enum, но его можно добавить в общую сборку).

...