Конечная точка службы WCF, принимающая другой запрос / ответ - PullRequest
0 голосов
/ 25 августа 2009

Я использую Framework 3.5 и хотел бы иметь ServiceContract, который может принимать различные типы объектов запроса / ответа, возможно ли это?

Ответы [ 3 ]

0 голосов
/ 25 августа 2009

Как говорит Марк, у вас есть контракт на обслуживание, который принимает параметры сообщения. Но вам не нужно создавать XML вручную, вместо этого вы можете совместно использовать интерфейс и сообщения в общей DLL, которая является общей как для сервера, так и для клиента.

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

[ServiceContract(
    Namespace = Constants.Service.Namespace.Location, 
    Name = "ServiceMonitorContract")]
public interface IMonitor
{
    [OperationContract(
        Action = Constants.Service.Actions.GetTasksRequest,
        ReplyAction = Constants.Service.Actions.GetTasksResponse)]
    Message GetTasks(Message request);
}

и у меня есть контракты сообщений, которые выглядят как

[MessageContract(IsWrapped = true, 
                 WrapperNamespace = Constants.Messages.Namespace.Location)]
public sealed class GetTasksRequest 
{
    ....
}

Чтобы подключиться к услуге, я делаю следующее

private static IMonitor GetChannelToWebService()
{
    EndpointAddress endpoint = new EndpointAddress("http://example/service.svc");
    ChannelFactory<IMonitor> channelFactory = 
        new ChannelFactory<IMonitor>(new BasicHttpBinding(), endpoint);

    return channelFactory.CreateChannel();
} 

И тогда я могу сделать следующее, чтобы использовать его с общими сообщениями.

IMonitor channel = GetChannelToWebService();

// Create the GetTasksRequest message
GetTasksRequest getTasksRequest = new GetTasksRequest();
// Set the various properties on the message

// Convert it to a strongly type message
TypedMessageConverter requestMessageConverter = TypedMessageConverter.Create(
    typeof(GetTasksRequest),
    Constants.Service.Actions.GetTasksRequest,
    Constants.Service.Namespace.Location);
Message request = requestMessageConverter.ToMessage(
                      getTasksRequest, 
                      MessageVersion.Soap11);

// Send it and get the response.
Message response = channel.GetTasks(request);

// Check for SOAP faults
if (response.IsFault)
{
    MessageFault fault = MessageFault.CreateFault(response, int.MaxValue);
    // React accordingly        
}

TypedMessageConverter responseMessageConverter = TypedMessageConverter.Create(
    typeof(GetTasksResponse),
    Constants.Service.Actions.GetTasksResponse,
    Constants.Service.Namespace.Location);
GetTasksResponse getTasksResponse = 
    responseMessageConverter.FromMessage(response) as GetTasksResponse;

((IClientChannel)channel).Close();

Единственное, о чем нужно знать, это то, что ошибки не будут переданы клиентской стороне. Вы должны проверить объект Message, когда он возвращается в качестве ответа вручную, и действовать соответственно, как вы можете видеть из примера.

Серверная часть. Я делаю то же самое с TypedMessageConvertor

// Convert the inbound message to a GetTasksRequest.
TypedMessageConverter getTasksMessageConverter = TypedMessageConverter.Create(
    typeof(GetTasksRequest),
    Constants.Service.Actions.GetTasksRequest,
    Constants.Service.Namespace.Location);
GetTasksRequest getTasksMessage = 
    getTasksMessageConverter.FromMessage(request) as GetTasksRequest;

// Validate the message is the correct type.
if (getTasksMessage == null)
{
    throw FaultHelper.UnknownMessageTypeFault();
}

// Do my thing

GetTasksResponse responseMessage = new GetTasksResponse();

// Set appropriate response bits in the responseMessage

TypedMessageConverter responseConverter = TypedMessageConverter.Create(
    typeof(GetTasksResponse),
    Constants.Service.Actions.GetTasksResponse,
    Constants.Service.Namespace.Location);
Message response = responseConverter.ToMessage(responseMessage, request.Version);
response.Headers.RelatesTo = request.Headers.MessageId;

return response;

Только не забудьте установить заголовок RelatesTo в качестве MessageId из заголовков запроса

0 голосов
/ 09 октября 2009

Вы также можете передать XElement. Это дает объекту запроса гибкость для содержания любого содержимого, которое вы выбираете.

В идеале вы должны указать XSD, который содержит несколько элементов 'choice', каждый из которых указывает один из различных типов запроса.

<xs:element name="request">
  <xs:complexType>
    <xs:choice>
      <xs:element name="requestType1"/>
          ....
      </xs:element>
      <xs:element name="requestType2"/>
          ....
      </xs:element>
    </xs:choice>
  </xs:complexType>
</xs:element>

Ваш код на стороне службы должен просто определить, какие из объектов 'выбора' присутствуют, чтобы определить, что делать с параметром.

0 голосов
/ 25 августа 2009

Да, если вы действительно хотите, вы можете иметь дело с общим типом Message в качестве параметра и / или возвращаемого значения.

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    Message GetData();

    [OperationContract]
    void PutData(Message m);
}

Подробности см. В документации MSDN .

Однако это означает, что вам придется вручную выполнять множество манипуляций с XML voodoo и самостоятельно обрабатывать множество вещей, которые будут обрабатываться бесплатно, если вы используете строго типизированные [DataContract] типы.

Ты можешь это сделать? Да. Должны ли вы сделать это? Ну, зависит от того, насколько сильно ты этого хочешь! : -)

Марк

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...