WCF: контракт данных преобразуется в контракт сообщений - PullRequest
2 голосов
/ 27 августа 2010

Моя служба WCF экспортирует одну операцию, помеченную действием catch-all и действием reply, чтобы она представляла общую точку входа в службу:

[ServiceContract]
public interface IService
{
    [OperationContract (Action="*", ReplyAction="*")]
    Message MyMethod (Message msg);
}

Клиентские прокси все еще генерируются как данные контракты.

Однако я обнаружил, что, несмотря на то, что клиент, отправляющий контракт данных, при сериализации msg тело выглядит как эквивалентное сообщение контракт с контрактом данных, а не Сам контракт данных.

Даже это хорошо, за исключением того, что извлечение контракта данных внутри предполагает ручной анализ входящего XML. Сам сервис не имеет фактического типа MessageContract, поэтому доступ к телу означает извлечение узлов, элементов перемаркировки и т. Д. Это ручной процесс для чего-то, что, по-видимому, WCF уже обрабатывает под прикрытием, когда открытые операции не основаны на Message.

Как WCF делает это, когда это контракт между данными и данными? Есть ли способ, которым я могу использовать тот же процесс?

Ответы [ 2 ]

2 голосов
/ 28 августа 2010

Это правильное поведение по умолчанию.Каждый раз, когда данные запроса или ответа отправляются, они автоматически переносятся в элемент обертывания.Он также известен как стиль параметров Wrapped.Если вы не хотите использовать его, а вместо этого хотите использовать стиль параметров Bare, вам нужно определить контракт сообщения и установить для его свойства IsWrapped значение false.Как этот простой пример:

[ServiceContract]
public interface IService
{
    [OperationContract]
    GetMessageResponse GetMessage(GetMessageRequest request);
}

[MessageContract(IsWrapped = false)]
public class GetMessageResponse
{
    [MessageBodyMember]
    public string Result { get; set; }
}

[MessageContract(IsWrapped = false)]
public class GetMessageRequest
{
    [MessageBodyMember]
    public string Data { get; set; }
}

Операция GetMessage не будет использовать перенос в запросе и ответе.

Ограничение состоит в том, что операция должна принимать только один MessageContract в качестве параметра и всегда должна возвращать MessageContract(даже если он возвращает void).Таким образом, самый простой способ выполнить ваше требование - преобразовать все ваши контракты данных в контракты сообщений, заменив атрибуты.

Другой способ - создать отдельный контракт сообщений для каждого запроса и ответа и использовать свойства типов ваших контрактов данных, кактело сообщения.Если по какой-либо причине вам не нравится идея создания двух дополнительных контрактов на сообщения для каждой операции, и вы все еще хотите сохранить старые контракты на данные, вы можете использовать небольшой взлом (я не вижу причин для его использования, но он работает).Добавьте атрибут MessageContract к вашим контрактам данных и атрибут MessageBodyMember ко всем членам ваших данных.

[DataContract, MessageContract(IsWrapped = false)]
public class MyData
{ 
    [DataMember, MessageBodyMember]
    public string Data { get; set; }
}
0 голосов
/ 28 августа 2010

Я согласен с Ладиславом, что вы видите правильное поведение.На MSDN есть отличный документ, который описывает, что происходит, когда WCF получает и отправляет сообщения:

http://msdn.microsoft.com/en-us/library/aa347789.aspx

Ключевой параграф, который относится к тому, что вы спрашиваете, находится здесь:

Чтение сообщения в основном используется сервисной структурой при получении сообщений.Например, когда используется DataContractSerializer, сервисная среда получит XML-считыватель поверх тела и передаст его в механизм десериализации, который затем начнет читать сообщение поэлементно и строить соответствующий граф объекта.

Таким образом, фреймворк использует DataContractSerializer для десериализации полезной нагрузки (которая является информационным набором XML) в соответствующий класс при получении.Вы можете попытаться использовать ту же логику - путем извлечения полезной нагрузки (возможно, с использованием Message.GetReaderAtBodyContents(), которая возвращает XmlReader), а затем с помощью DataContractSerializer для десериализации XML в объект, с которым вы хотите работать, с возвращенным XmlReader.

Надеюсь, это поможет!

...