Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Este tópico descreve quais são os contratos de serviço, como eles são definidos, quais operações estão disponíveis (e as implicações para as trocas de mensagens subjacentes), quais tipos de dados são usados e outros problemas que ajudam você a projetar operações que atendam aos requisitos do seu cenário.
Criando um contrato de serviço
Os serviços mostram várias operações. Em aplicativos WCF (Windows Communication Foundation), defina as operações criando um método e marcando-o com o OperationContractAttribute atributo. Em seguida, para criar um contrato de serviço, agrupe suas operações, declarando-as dentro de uma interface marcada com o ServiceContractAttribute atributo ou definindo-as em uma classe marcada com o mesmo atributo. (Para obter um exemplo básico, consulte Como definir um contrato de serviço.)
Todos os métodos que não têm um OperationContractAttribute atributo não são operações de serviço e não são expostos pelos serviços do WCF.
Este tópico descreve os seguintes pontos de decisão ao projetar um contrato de serviço:
Se deve usar classes ou interfaces.
Como especificar os tipos de dados que você deseja trocar.
Os tipos de padrões de troca que você pode usar.
Se você pode tornar os requisitos de segurança explícitos parte do contrato.
As restrições para entradas e saídas de operação.
Classes ou interfaces
As classes e as interfaces representam um agrupamento de funcionalidades e, portanto, ambas podem ser usadas para definir um contrato de serviço WCF. No entanto, é recomendável que você use interfaces porque elas modelam diretamente os contratos de serviço. Sem uma implementação, as interfaces não fazem mais do que definir um agrupamento de métodos com determinadas assinaturas. Implemente uma interface de contrato de serviço e você implementou um serviço WCF.
Todos os benefícios das interfaces gerenciadas se aplicam às interfaces de contrato de serviço:
As interfaces de contrato de serviço podem estender qualquer número de outras interfaces de contrato de serviço.
Uma única classe pode implementar qualquer número de contratos de serviço implementando essas interfaces de contrato de serviço.
Você pode modificar a implementação de um contrato de serviço alterando a implementação da interface, enquanto o contrato de serviço permanece o mesmo.
Você pode fazer a versão do serviço implementando a interface antiga e a nova. Clientes antigos se conectam à versão original, enquanto os clientes mais recentes podem se conectar à versão mais recente.
Observação
Ao herdar de outras interfaces de contrato de serviço, você não pode substituir as propriedades da operação, como o nome ou o namespace. Se você tentar fazer isso, criará uma nova operação no contrato de serviço atual.
Para obter um exemplo de como usar uma interface para criar um contrato de serviço, consulte Como criar um serviço com uma interface de contrato.
No entanto, você pode usar uma classe para definir um contrato de serviço e implementar esse contrato ao mesmo tempo. A vantagem de criar seus serviços aplicando ServiceContractAttribute e OperationContractAttribute diretamente à classe e aos métodos na classe, respectivamente, é a velocidade e a simplicidade. As desvantagens são que as classes gerenciadas não dão suporte a várias heranças e, como resultado, só podem implementar um contrato de serviço por vez. Além disso, qualquer modificação nas assinaturas de classe ou método modifica o contrato público para esse serviço, o que pode impedir que clientes não modificados usem seu serviço. Para obter mais informações, consulte Implementando contratos de serviço.
Para obter um exemplo que usa uma classe para criar um contrato de serviço e implementá-lo ao mesmo tempo, consulte Como criar um serviço com uma classe de contrato.
Neste ponto, você deve entender a diferença entre definir seu contrato de serviço usando uma interface e usar uma classe. A próxima etapa é decidir quais dados podem ser passados entre um serviço e seus clientes.
Parâmetros e valores retornados
Cada operação tem um valor retornado e um parâmetro, mesmo que sejam void. No entanto, ao contrário de um método local, no qual você pode passar referências a objetos de um objeto para outro, as operações de serviço não passam referências para objetos. Em vez disso, eles passam cópias dos objetos.
Isso é significativo porque cada tipo usado em um parâmetro ou valor retornado deve ser serializável; ou seja, deve ser possível converter um objeto desse tipo em um fluxo de bytes e de um fluxo de bytes em um objeto.
Os tipos primitivos são serializáveis por padrão, assim como muitos tipos no .NET Framework.
Observação
O valor dos nomes de parâmetro na assinatura de operação fazem parte do contrato e diferenciam maiúsculas de minúsculas. Se você quiser usar o mesmo nome de parâmetro localmente, mas modificar o nome nos metadados publicados, consulte o System.ServiceModel.MessageParameterAttribute.
Contratos de dados
Aplicativos orientados a serviço, como aplicativos WCF (Windows Communication Foundation), foram projetados para interoperar com o maior número possível de aplicativos cliente em plataformas Microsoft e não Microsoft. Para a interoperabilidade mais ampla possível, é recomendável marcar seus tipos com os atributos DataContractAttribute e DataMemberAttribute para criar um contrato de dados, que é a parte do contrato de serviço que descreve os dados que suas operações de serviço trocam.
Os contratos de dados são contratos de estilo opt-in: nenhum tipo ou membro de dados é serializado, a menos que você aplique explicitamente o atributo de contrato de dados. Os contratos de dados não estão relacionados ao escopo de acesso do código gerenciado: os membros de dados privados podem ser serializados e enviados para outro lugar para serem acessados publicamente. (Para obter um exemplo básico de um contrato de dados, consulte Como criar um contrato de dados básico para uma classe ou estrutura.) O WCF manipula a definição das mensagens SOAP subjacentes que habilitam a funcionalidade da operação, bem como a serialização de seus tipos de dados dentro e fora do corpo das mensagens. Desde que seus tipos de dados sejam serializáveis, você não precisa pensar na infraestrutura de troca de mensagens subjacente ao projetar suas operações.
Embora o aplicativo WCF típico use os atributos DataContractAttribute e DataMemberAttribute, para criar contratos de dados para operações, você pode usar outros mecanismos de serialização. Os mecanismos padrão ISerializable, SerializableAttribute, e IXmlSerializable funcionam para lidar com a serialização de seus tipos de dados nas mensagens SOAP subjacentes que os transportam de um aplicativo para outro. Você poderá empregar mais estratégias de serialização se os tipos de dados exigirem suporte especial. Para obter mais informações sobre as opções de serialização de tipos de dados em aplicativos WCF, consulte Especificando a transferência de dados em contratos de serviço.
Mapeando parâmetros e valores retornados para trocas de mensagens
As operações de serviço têm suporte por uma troca subjacente de mensagens SOAP que transferem dados do aplicativo bidirecionalmente, além dos dados necessários pelo aplicativo para dar suporte a determinados recursos padrão relacionados à segurança, transação e sessão. Como esse é o caso, a assinatura de uma operação de serviço determina um determinado MEP (padrão de troca de mensagens ) subjacente que pode dar suporte à transferência de dados e aos recursos necessários para uma operação. Você pode especificar três padrões no modelo de programação do WCF: solicitação/resposta, padrões de mensagens unidirecionais e duplex.
Solicitação/Resposta
Um padrão de solicitação/resposta é aquele em que um remetente de solicitação (um aplicativo cliente) recebe uma resposta com a qual a solicitação está correlacionada. Esse é o MEP padrão porque dá suporte a uma operação na qual um ou mais parâmetros são passados para a operação e um valor retornado é passado de volta para o chamador. Por exemplo, o exemplo de código C# a seguir mostra uma operação de serviço básica que usa uma cadeia de caracteres e retorna uma cadeia de caracteres.
[OperationContractAttribute]
string Hello(string greeting);
Veja a seguir o código equivalente do Visual Basic.
<OperationContractAttribute()>
Function Hello (ByVal greeting As String) As String
Esta assinatura de operação determina a forma de troca de mensagens subjacente. Se não houver correlação, o WCF não poderá determinar para qual operação o valor retornado se destina.
Observe que, a menos que você especifique um padrão de mensagem subjacente diferente, até mesmo as operações de serviço que retornam void (Nothing no Visual Basic) são trocas de mensagens de solicitação/resposta. O resultado da operação é que, a menos que um cliente invoque a operação de forma assíncrona, o cliente interromperá o processamento até que a mensagem de retorno seja recebida, mesmo que essa mensagem esteja vazia no caso normal. O exemplo de código C# a seguir mostra uma operação que não retorna até que o cliente tenha recebido uma mensagem vazia em resposta.
[OperationContractAttribute]
void Hello(string greeting);
Veja a seguir o código equivalente do Visual Basic.
<OperationContractAttribute()>
Sub Hello (ByVal greeting As String)
O exemplo anterior pode diminuir o desempenho e a capacidade de resposta do cliente se a operação levar muito tempo para ser executada, mas há vantagens em operações de solicitação/resposta mesmo quando elas retornam void. A mais óbvia é que as falhas SOAP podem ser retornadas na mensagem de resposta, o que indica que alguma condição de erro relacionada ao serviço ocorreu, seja na comunicação ou no processamento. As falhas SOAP especificadas em um contrato de serviço são passadas para o aplicativo cliente como um FaultException<TDetail> objeto, em que o parâmetro de tipo é o tipo especificado no contrato de serviço. Isso facilita a notificação de clientes sobre condições de erro nos serviços do WCF. Para obter mais informações sobre exceções, falhas SOAP e tratamento de erros, consulte Especificando e tratando falhas em contratos e serviços. Para ver um exemplo de um serviço de solicitação/resposta e cliente, consulte Como criar um contrato de Request-Reply. Para obter mais informações sobre problemas com o padrão de solicitação e resposta, consulte Request-Reply Services.
Unidirecional
Se o cliente de um aplicativo de serviço WCF não deve aguardar a conclusão da operação e não processar falhas SOAP, a operação poderá especificar um padrão de mensagem unidirecional. Uma operação unidirecional é aquela em que um cliente invoca uma operação e continua o processamento após o WCF gravar a mensagem na rede. Normalmente, isso significa que, a menos que os dados enviados na mensagem de saída sejam extremamente grandes, o cliente continuará sendo executado quase imediatamente (a menos que haja um erro ao enviar os dados). Esse tipo de padrão de troca de mensagens dá suporte a comportamentos semelhantes a eventos de um cliente para um aplicativo de serviço.
Uma troca de mensagens na qual uma mensagem é enviada e nenhuma é recebida não pode dar suporte a uma operação de serviço que especifica um valor retornado diferente void; nesse caso, uma exceção InvalidOperationException é gerada.
Nenhuma mensagem de retorno também significa que não pode haver nenhuma falha SOAP retornada para indicar erros no processamento ou na comunicação. (Comunicar informações de erro quando as operações são operações unidirecionais requer um padrão de troca de mensagens duplex.)
Para especificar uma troca de mensagens unidirecional para uma operação que retorna void, defina a IsOneWay propriedade como true, como no exemplo de código C# a seguir.
[OperationContractAttribute(IsOneWay=true)]
void Hello(string greeting);
Veja a seguir o código equivalente do Visual Basic.
<OperationContractAttribute(IsOneWay := True)>
Sub Hello (ByVal greeting As String)
Esse método é idêntico ao exemplo de solicitação/resposta anterior, mas definir a IsOneWay propriedade true significa que, embora o método seja idêntico, a operação de serviço não envia uma mensagem de retorno e os clientes retornam imediatamente depois que a mensagem de saída é entregue à camada de canal. Para obter um exemplo, consulte Como criar um contrato de One-Way. Para obter mais informações sobre o padrão unidirecional, consulte One-Way Services.
Duplex
Um padrão duplex é caracterizado pela capacidade do serviço e do cliente de enviar mensagens uns aos outros de forma independente, seja usando mensagens unidirecionais ou de solicitação/resposta. Essa forma de comunicação bidirecional é útil para serviços que devem se comunicar diretamente com o cliente ou para fornecer uma experiência assíncrona para ambos os lados de uma troca de mensagens, incluindo comportamento semelhante a um evento.
O padrão duplex é um pouco mais complexo do que os padrões de solicitação/resposta ou unidirecional devido ao mecanismo adicional para se comunicar com o cliente.
Para criar um contrato duplex, você também deve criar um contrato do retorno de chamada e atribuir o tipo desse contrato de retorno de chamada à propriedade CallbackContract do atributo ServiceContractAttribute que marca seu contrato de serviço.
Para implementar um padrão duplex, você deve criar uma segunda interface que contenha as declarações de método que são chamadas no cliente.
Para obter um exemplo de criação de um serviço e um cliente que acessa esse serviço, consulte Como criar um contrato duplex e como acessar serviços com um contrato Duplex. Para obter um exemplo de trabalho, consulte Duplex. Para obter mais informações sobre problemas usando contratos duplex, consulte Duplex Services.
Cuidado
Quando um serviço recebe uma mensagem duplex, ele examina o ReplyTo elemento nessa mensagem de entrada para determinar para onde enviar a resposta. Se o canal não estiver protegido, um cliente não confiável pode enviar uma mensagem mal-intencionada com um ReplyTo do computador de destino, o que gera uma negação do serviço no computador de destino.
Parâmetros out e ref
Na maioria dos casos, você pode usar in parâmetros (ByVal no Visual Basic) e outref parâmetros (ByRef no Visual Basic). Como os parâmetros out e ref indicam que dados são retornados de uma operação, uma assinatura de operação como a seguinte especifica que uma operação de solicitação/resposta é necessária, mesmo que a assinatura retorne void.
[ServiceContractAttribute]
public interface IMyContract
{
[OperationContractAttribute]
public void PopulateData(ref CustomDataType data);
}
Veja a seguir o código equivalente do Visual Basic.
<ServiceContractAttribute()> _
Public Interface IMyContract
<OperationContractAttribute()> _
Public Sub PopulateData(ByRef data As CustomDataType)
End Interface
As únicas exceções são os casos em que sua assinatura tem uma estrutura específica. Por exemplo, você pode usar a NetMsmqBinding vinculação para se comunicar com clientes somente se o método usado para declarar uma operação retornar void; não pode haver nenhum valor de saída, seja um valor de retorno ref ou um parâmetro out.
Além disso, o uso dos parâmetros out ou ref exige que a operação tenha uma mensagem de resposta subjacente para transferir o objeto modificado. Se a operação for uma operação unidirecional, uma exceção InvalidOperationException será lançada em runtime.
Especificar o nível de proteção de mensagem no contrato
Ao projetar seu contrato, você também deve decidir o nível de proteção de mensagem dos serviços que implementam seu contrato. Somente é necessário se a segurança da mensagem for aplicada à associação no ponto de extremidade do contrato. Se a associação tiver a segurança desativada (ou seja, se a associação fornecida pelo sistema definir o System.ServiceModel.SecurityMode valor SecurityMode.None), você não precisará decidir sobre o nível de proteção de mensagem do contrato. Na maioria dos casos, as associações fornecidas pelo sistema com segurança em nível de mensagem aplicada fornecem um nível de proteção suficiente e você não precisa considerar o nível de proteção para cada operação ou para cada mensagem.
O nível de proteção é um valor que especifica se as mensagens (ou partes de mensagem) que dão suporte a um serviço são assinadas, assinadas e criptografadas ou enviadas sem assinaturas ou criptografia. O nível de proteção pode ser definido em vários escopos: no nível de serviço, para uma operação específica, para uma mensagem dentro dessa operação ou uma parte de mensagem. Os valores definidos em um escopo tornam-se o valor padrão para escopos menores, a menos que explicitamente substituídos. Se uma configuração de vinculação não puder fornecer o nível de proteção mínimo necessário para o contrato, uma exceção será lançada. E quando nenhum valor de nível de proteção é definido explicitamente no contrato, a configuração de associação controla o nível de proteção de todas as mensagens se a associação tiver segurança de mensagem. Esse é o comportamento padrão.
Importante
Decidir se deve definir explicitamente vários escopos de um contrato para menos do que o nível total de proteção de ProtectionLevel.EncryptAndSign é geralmente uma decisão que compromete algum grau de segurança em troca de um aumento de desempenho. Nesses casos, suas decisões devem girar em torno de suas operações e do valor dos dados que elas trocam. Para obter mais informações, consulte Segurança de Serviços.
Por exemplo, o código de exemplo a seguir não define nem a propriedade ProtectionLevel nem a propriedade ProtectionLevel no contrato.
[ServiceContract]
public interface ISampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute]
public int GetInt();
}
Veja a seguir o código equivalente do Visual Basic.
<ServiceContractAttribute()> _
Public Interface ISampleService
<OperationContractAttribute()> _
Public Function GetString()As String
<OperationContractAttribute()> _
Public Function GetData() As Integer
End Interface
Ao interagir com uma implementação ISampleService em um ponto de extremidade com um WSHttpBinding padrão (o System.ServiceModel.SecurityMode padrão, que é Message), todas as mensagens são criptografadas e assinadas, já que esse é o nível de proteção padrão. No entanto, quando um ISampleService serviço é usado com um padrão BasicHttpBinding (o padrão SecurityMode, que é None), todas as mensagens são enviadas como texto porque não há segurança para essa associação e, portanto, o nível de proteção é ignorado (ou seja, as mensagens não são criptografadas nem assinadas). Se SecurityMode for alterado para Message, essas mensagens serão criptografadas e assinadas (porque esse agora seria o nível de proteção padrão do vínculo).
Se você quiser especificar ou ajustar explicitamente os requisitos de proteção do seu contrato, defina a ProtectionLevel propriedade (ou qualquer uma das ProtectionLevel propriedades em um escopo menor) para o nível exigido pelo contrato de serviço. Nesse caso, o uso de uma configuração explícita requer a associação para dar suporte a essa configuração no mínimo para o escopo usado. Por exemplo, o exemplo de código a seguir especifica um ProtectionLevel valor explicitamente para a GetGuid operação.
[ServiceContract]
public interface IExplicitProtectionLevelSampleService
{
[OperationContractAttribute]
public string GetString();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.None)]
public int GetInt();
[OperationContractAttribute(ProtectionLevel=ProtectionLevel.EncryptAndSign)]
public int GetGuid();
}
Veja a seguir o código equivalente do Visual Basic.
<ServiceContract()> _
Public Interface IExplicitProtectionLevelSampleService
<OperationContract()> _
Public Function GetString() As String
End Function
<OperationContract(ProtectionLevel := ProtectionLevel.None)> _
Public Function GetInt() As Integer
End Function
<OperationContractAttribute(ProtectionLevel := ProtectionLevel.EncryptAndSign)> _
Public Function GetGuid() As Integer
End Function
End Interface
Um serviço que implementa esse IExplicitProtectionLevelSampleService contrato e tem um endpoint que usa o padrão WSHttpBinding (o padrão System.ServiceModel.SecurityMode, que é Message) tem o seguinte comportamento:
As
GetStringmensagens de operação são criptografadas e assinadas.As
GetIntmensagens de operação são enviadas como texto não criptografado e sem sinal (ou seja, sem formatação).A
GetGuidoperação System.Guid é retornada em uma mensagem criptografada e assinada.
Para obter mais informações sobre os níveis de proteção e como usá-los, consulte Noções básicas sobre o nível de proteção. Para obter mais informações sobre segurança, consulte Proteção de Serviços.
Outros requisitos de assinatura de operações
Alguns recursos de aplicativo exigem um tipo específico de assinatura de operação. Por exemplo, a associação NetMsmqBinding oferece suporte a serviços duráveis e clientes, nos quais um aplicativo pode reiniciar no meio da comunicação e retomar de onde parou sem nenhuma mensagem. (Para obter mais informações, consulte Filas no WCF.) No entanto, as operações duráveis devem ter apenas um parâmetro in e não ter nenhum valor retornado.
Outro exemplo é a utilização de tipos Stream nas operações. Como o Stream parâmetro inclui todo o corpo da mensagem, se uma entrada ou uma saída (ou seja, ref parâmetro, out parâmetro ou valor retornado) for do tipo Stream, ele deverá ser a única entrada ou saída especificada em sua operação. Além disso, o parâmetro ou tipo de retorno deve ser Stream, System.ServiceModel.Channels.Messageou System.Xml.Serialization.IXmlSerializable. Para obter mais informações sobre fluxos, consulte Dados Grandes e Streaming.
Nomes, namespaces e ofuscação
Os nomes e namespaces dos tipos .NET na definição de contratos e operações são significativos quando os contratos são convertidos em WSDL e quando as mensagens de contrato são criadas e enviadas. Portanto, é altamente recomendável que os nomes e namespaces de contrato de serviço sejam definidos explicitamente usando as propriedades Name e Namespace de todos os atributos de contrato de suporte, como os atributos ServiceContractAttribute, OperationContractAttribute, DataContractAttribute, DataMemberAttribute e outros atributos de contrato.
O resultado é que, se os nomes e namespaces não forem definidos explicitamente, o uso da ofuscação de IL no assembly altera os nomes e namespaces de tipo de contrato e resulta em trocas de WSDL e de fio modificadas que normalmente têm falhas. Se você não definir explicitamente os nomes de contrato e namespaces, mas pretende usar ofuscação, use os atributos ObfuscationAttribute e ObfuscateAssemblyAttribute para impedir a modificação dos nomes de tipo de contrato e namespaces.
Consulte também
- Como criar um contrato de Request-Reply
- Como criar um contrato de One-Way
- Como: criar um contrato duplex
- Especificando a transferência de dados em contratos de serviço
- Especificando e tratando falhas em contratos e serviços
- Utilizando sessões
- Operações síncronas e assíncronas
- Reliable Services
- Serviços e transações