Compartilhar via


Migrando do .NET Remoting para o WCF

Este artigo descreve como migrar um aplicativo que usa o .NET Remoting para usar o WCF (Windows Communication Foundation). Ele compara conceitos semelhantes entre esses produtos e descreve como realizar vários cenários comuns de Comunicação Remota no WCF.

O .NET Remoting é um produto herdado que tem suporte apenas para compatibilidade com versões anteriores. Ele não é seguro em ambientes de confiança mista porque não pode manter os níveis de confiança separados entre o cliente e o servidor. Por exemplo, você nunca deve expor um endpoint de .NET Remoting na Internet ou para clientes não confiáveis. Recomendamos que os aplicativos de comunicação remota existentes sejam migrados para tecnologias mais recentes e seguras. Se o design do aplicativo usa apenas HTTP e é RESTful, recomendamos ASP.NET API Web. Para obter mais informações, consulte ASP.NET API Web. Se o aplicativo for baseado em SOAP ou exigir protocolos não Http, como TCP, recomendamos o WCF.

Comparando a comunicação remota do .NET com o WCF

Esta seção compara os blocos de construção básicos do .NET Remoting com seus equivalentes do WCF. Usaremos esses blocos de construção mais tarde para criar alguns cenários comuns de cliente-servidor no WCF. O gráfico a seguir resume as principais semelhanças e diferenças entre o .NET Remoting e o WCF.

Comunicação remota .NET WCF (Windows Communication Foundation)
Tipo de servidor Subclasse MarshalByRefObject Marcar com o atributo [ServiceContract]
Operações de serviço Métodos públicos no tipo de servidor Marcar com o atributo [OperationContract]
Serialização ISerializable ou [Serializable] DataContractSerializer ou XmlSerializer
Objetos passados Por valor ou por referência Somente por valor
Erros/exceções Qualquer exceção serializável FaultContract<TDetail>
Objetos proxy do cliente Proxies transparentes fortemente tipados são criados automaticamente no MarshalByRefObjects Proxies fortemente tipados são gerados sob demanda usando o ChannelFactory<TChannel>
Plataforma necessária O cliente e o servidor devem usar o sistema operacional Microsoft e o .NET Multiplataforma
Formato da mensagem Privado Padrões do setor (por exemplo, SOAP e WS-*)

Comparação de implementação de servidor

Criando um servidor no .NET Remoting

Os tipos de servidor de comunicação remota do .NET devem derivar de MarshalByRefObject e definir métodos que o cliente pode chamar, como o seguinte:

public class RemotingServer : MarshalByRefObject  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

Os métodos públicos desse tipo de servidor se tornam o contrato público disponível para os clientes. Não há separação entre a interface pública do servidor e sua implementação– um tipo manipula ambos.

Depois que o tipo de servidor tiver sido definido, ele poderá ser disponibilizado aos clientes, como no exemplo a seguir:

TcpChannel channel = new TcpChannel(8080);  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingConfiguration.RegisterWellKnownServiceType(  
    typeof(RemotingServer),
    "RemotingServer",
    WellKnownObjectMode.Singleton);  
Console.WriteLine("RemotingServer is running.  Press ENTER to terminate...");  
Console.ReadLine();  

Há várias maneiras de disponibilizar o tipo de comunicação remota como servidor, incluindo o uso de arquivos de configuração. Este é apenas um exemplo.

Criando um servidor no WCF

A etapa equivalente no WCF envolve a criação de dois tipos: o "contrato de serviço" público e a implementação concreta. O primeiro é declarado como uma interface marcada com [ServiceContract]. Os métodos disponíveis para clientes são marcados com [OperationContract]:

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    Customer GetCustomer(int customerId);  
}  

A implementação do servidor é definida em uma classe concreta separada, como no exemplo a seguir:

public class WCFServer : IWCFServer  
{  
    public Customer GetCustomer(int customerId) { … }  
}  

Depois que esses tipos tiverem sido definidos, o servidor WCF poderá ser disponibilizado aos clientes, como no exemplo a seguir:

NetTcpBinding binding = new NetTcpBinding();  
Uri baseAddress = new Uri("net.tcp://localhost:8000/wcfserver");  
  
using (ServiceHost serviceHost = new ServiceHost(typeof(WCFServer), baseAddress))  
{  
    serviceHost.AddServiceEndpoint(typeof(IWCFServer), binding, baseAddress);  
    serviceHost.Open();  
  
    Console.WriteLine($"The WCF server is ready at {baseAddress}.");
    Console.WriteLine("Press <ENTER> to terminate service...");  
    Console.WriteLine();  
    Console.ReadLine();  
}  

Observação

O TCP é usado em ambos os exemplos para mantê-los o mais semelhante possível. Consulte os passo a passo do cenário mais adiante neste tópico para obter exemplos usando HTTP.

Há muitas maneiras de configurar e hospedar serviços WCF. Este é apenas um exemplo, conhecido como "auto-hospedado". Para obter mais informações, consulte os seguintes tópicos:

Comparação de implementação do cliente

Criando um cliente na comunicação remota do .NET

Depois que um objeto de servidor de Comunicação Remota do .NET tiver sido disponibilizado, ele poderá ser consumido pelos clientes, como no exemplo a seguir:

TcpChannel channel = new TcpChannel();  
ChannelServices.RegisterChannel(channel, ensureSecurity : true);  
RemotingServer server = (RemotingServer)Activator.GetObject(  
                            typeof(RemotingServer),
                            "tcp://localhost:8080/RemotingServer");  
  
RemotingCustomer customer = server.GetCustomer(42);  
Console.WriteLine($"Customer {customer.FirstName} {customer.LastName} received.");

A instância RemotingServer retornada de Activator.GetObject() é conhecida como um "proxy transparente". Ela implementa a API pública para o tipo RemotingServer no cliente, mas todos os métodos chamam o objeto do servidor que está em execução em um processo ou computador diferente.

Criando um cliente no WCF

A etapa equivalente no WCF envolve usar uma fábrica de canais para criar o proxy explicitamente. Como no Remoting, o objeto proxy pode ser usado para invocar operações no servidor, como no exemplo a seguir:

NetTcpBinding binding = new NetTcpBinding();  
String url = "net.tcp://localhost:8000/wcfserver";  
EndpointAddress address = new EndpointAddress(url);  
ChannelFactory<IWCFServer> channelFactory =
    new ChannelFactory<IWCFServer>(binding, address);  
IWCFServer server = channelFactory.CreateChannel();  
  
Customer customer = server.GetCustomer(42);  
Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");

Este exemplo mostra a programação no nível do canal porque é mais semelhante ao exemplo do Remoting. Também está disponível a abordagem Adicionar Referência de Serviço no Visual Studio que gera código para simplificar a programação do cliente. Para obter mais informações, consulte os seguintes tópicos:

Uso de serialização

A Comunicação Remota do .NET e o WCF usam a serialização para enviar objetos entre o cliente e o servidor, mas eles diferem destas maneiras importantes:

  1. Eles usam serializadores e convenções diferentes para indicar o que serializar.

  2. O .NET Remoting dá suporte à serialização "por referência" que permite que o acesso à propriedade ou método em uma camada execute o código na outra camada, que está entre os limites de segurança. Essa funcionalidade expõe vulnerabilidades de segurança e é um dos principais motivos pelos quais os pontos de extremidade de comunicação remota nunca devem ser expostos a clientes não confiáveis.

  3. A serialização usada pelo Remoting é opt-out (excluir explicitamente o que não deve ser serializado) e a serialização do WCF é opt-in (marcar explicitamente quais membros serializar).

Serialização no .NET Remoting

A Comunicação Remota do .NET dá suporte a duas maneiras de serializar e desserializar objetos entre o cliente e o servidor:

  • Por valor – os valores do objeto são serializados entre limites de camada e uma nova instância desse objeto é criada na outra camada. Todas as chamadas para métodos ou propriedades dessa nova instância são executadas apenas localmente e não afetam o objeto ou a camada original.

  • Por referência – uma "referência de objeto" especial é serializada entre limites de camada. Quando uma camada interage com métodos ou propriedades desse objeto, ela se comunica novamente com o objeto original na camada original. Objetos por referência podem fluir em qualquer direção – servidor para cliente ou cliente para servidor.

Tipos por valor em Comunicação Remota são marcados com o atributo [Serializável] ou implementam ISerializable, como no exemplo a seguir:

[Serializable]  
public class RemotingCustomer  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

Os tipos de referência derivam da classe MarshalByRefObject, como no exemplo a seguir:

public class RemotingCustomerReference : MarshalByRefObject  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
    public int CustomerId { get; set; }  
}  

É extremamente importante entender as implicações dos objetos de referência do Remoting. Se uma camada (cliente ou servidor) enviar um objeto por referência para a outra camada, todas as chamadas de método serão executadas novamente na camada que possui o objeto. Por exemplo, um cliente que chama métodos em um objeto por referência retornado pelo servidor executará o código no servidor. Da mesma forma, um servidor que chama métodos em um objeto por referência fornecido pelo cliente executará o código de volta no cliente. Por esse motivo, o uso da Comunicação Remota do .NET é recomendado somente em ambientes totalmente confiáveis. Expor um ponto de extremidade público do .NET Remoting a clientes não confiáveis tornará um servidor Remoting vulnerável a ataques.

Serialização no WCF

O WCF dá suporte apenas à serialização por valor. A maneira mais comum de definir um tipo para trocar entre o cliente e o servidor é como no exemplo a seguir:

[DataContract]  
public class WCFCustomer  
{  
    [DataMember]  
    public string FirstName { get; set; }  
  
    [DataMember]  
    public string LastName { get; set; }  
  
    [DataMember]  
    public int CustomerId { get; set; }  
}  

O atributo [DataContract] identifica esse tipo como aquele que pode ser serializado e desserializado entre cliente e servidor. O atributo [DataMember] identifica as propriedades ou campos individuais a serem serializados.

Quando o WCF envia um objeto entre camadas, ele serializa apenas os valores e cria uma nova instância do objeto na outra camada. Todas as interações com os valores do objeto ocorrem apenas localmente – elas não se comunicam com a outra camada da mesma forma que os objetos de referência de comunicação remota do .NET. Para obter mais informações, consulte Serialização e Desserialização.

Recursos de tratamento de exceções

Exceções na comunicação remota do .NET

As exceções geradas por um servidor de Comunicação Remota são serializadas, enviadas ao cliente e lançadas localmente no cliente, como qualquer outra exceção. Exceções personalizadas podem ser criadas por meio da subclassificação do tipo Exceção e marcando-o com [Serializable]. A maioria das exceções de framework já está marcada dessa forma, permitindo que a maioria seja lançada pelo servidor, serializada e relançada no cliente. Embora esse design seja conveniente durante o desenvolvimento, as informações do lado do servidor podem ser inadvertidamente divulgadas para o cliente. Essa é uma das muitas razões pelas quais a comunicação remota deve ser usada apenas em ambientes totalmente confiáveis.

Exceções e falhas no WCF

O WCF não permite que tipos de exceção arbitrárias sejam retornados do servidor para o cliente porque isso pode levar à divulgação inadvertida de informações. Se uma operação de serviço lançar uma exceção inesperada, isso fará com que uma FaultException de propósito geral seja lançada no cliente. Essa exceção não contém informações sobre por que ou onde o problema ocorreu e, para alguns aplicativos, isso é suficiente. Aplicativos que precisam comunicar informações de erro mais avançadas ao cliente fazem isso definindo um contrato de falha.

Para fazer isso, primeiro crie um tipo [DataContract] para carregar as informações sobre a falha.

[DataContract]  
public class CustomerServiceFault  
{  
    [DataMember]  
    public string ErrorMessage { get; set; }  
  
    [DataMember]  
    public int CustomerId {get;set;}  
}  

Especifique o contrato de falha a ser usado para cada operação de serviço.

[ServiceContract]  
public interface IWCFServer  
{  
    [OperationContract]  
    [FaultContract(typeof(CustomerServiceFault))]  
    Customer GetCustomer(int customerId);  
}  

O servidor relata condições de erro lançando uma FaultException.

throw new FaultException<CustomerServiceFault>(  
    new CustomerServiceFault() {
        CustomerId = customerId,
        ErrorMessage = "Illegal customer Id"
    });  

E sempre que o cliente faz uma solicitação para o servidor, ele pode detectar falhas como exceções normais.

try  
{  
    Customer customer = server.GetCustomer(-1);  
}  
catch (FaultException<CustomerServiceFault> fault)  
{  
    Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}  

Para obter mais informações sobre contratos de falha, consulte FaultException.

Considerações de segurança

Segurança no .NET Remoting

Alguns canais de Comunicação Remota do .NET dão suporte a recursos de segurança, como autenticação e criptografia na camada de canal (IPC e TCP). O canal HTTP depende do IIS (Serviços de Informações da Internet) para autenticação e criptografia. Apesar desse suporte, você deve considerar a Comunicação Remota do .NET um protocolo de comunicação não seguro e usá-lo somente em ambientes totalmente confiáveis. Nunca exponha um ponto de extremidade público do Remoting à Internet ou a clientes não confiáveis.

Segurança no WCF

O WCF foi projetado com segurança em mente, em parte para resolver os tipos de vulnerabilidades encontradas na Comunicação Remota do .NET. O WCF oferece segurança no nível de transporte e mensagem e oferece muitas opções para autenticação, autorização, criptografia e assim por diante. Para obter mais informações, consulte os seguintes tópicos:

Migrando para o WCF

Por que migrar do Remoting para o WCF?

  • O .NET Remoting é um produto herdado. Conforme descrito na Comunicação Remota do .NET, ele é considerado um produto herdado e não é recomendado para o novo desenvolvimento. A API Web do WCF ou ASP.NET é recomendada para aplicativos novos e existentes.

  • O WCF usa padrões multiplataforma. O WCF foi projetado com interoperabilidade entre plataformas em mente e dá suporte a muitos padrões do setor (SOAP, WS-Security, WS-Trust etc.). Um serviço WCF pode interoperar com clientes em execução em sistemas operacionais diferentes do Windows. A comunicação remota foi projetada principalmente para ambientes em que os aplicativos cliente e servidor são executados usando o .NET Framework em um sistema operacional Windows.

  • O WCF tem segurança interna. O WCF foi projetado com segurança em mente e oferece muitas opções para autenticação, segurança em nível de transporte, segurança em nível de mensagem, etc. A comunicação remota foi projetada para facilitar a interoperação dos aplicativos, mas não foi projetada para ser segura em ambientes não confiáveis. O WCF foi projetado para funcionar em ambientes confiáveis e não confiáveis.

Recomendações de migração

Estas são as etapas recomendadas para migrar do .NET Remoting para o WCF:

  • Crie o contrato de serviço. Defina os tipos de interface de serviço e marque-os com o atributo [ServiceContract]. Marque todos os métodos que os clientes poderão chamar com [OperationContract].

  • Crie o contrato de dados. Defina os tipos de dados que serão trocados entre o servidor e o cliente e marque-os com o atributo [DataContract]. Marque todos os campos e propriedades que o cliente poderá usar com [DataMember].

  • Crie o contrato de falha (opcional). Crie os tipos que serão trocados entre o servidor e o cliente quando forem encontrados erros. Marque esses tipos com [DataContract] e [DataMember] para torná-los serializáveis. Para todas as operações de serviço marcadas com [OperationContract], marque-as também com [FaultContract] para indicar quais erros elas podem retornar.

  • Configure e hospede o serviço. Depois que o contrato de serviço tiver sido criado, a próxima etapa é configurar um binding para expor o serviço em um endpoint. Para obter mais informações, consulte Pontos de extremidade: endereços, associações e contratos.

Depois que um aplicativo de Comunicação Remota tiver sido migrado para o WCF, ainda é importante remover as dependências da Comunicação Remota do .NET. Isso garante que quaisquer vulnerabilidades de Remoting sejam removidas do aplicativo. Estas etapas incluem:

  • Descontinue o uso de MarshalByRefObject. O tipo MarshalByRefObject existe apenas para Remoting e não é usado pelo WCF. Todos os tipos de aplicativo que subclassem MarshalByRefObject devem ser removidos ou alterados.

  • Descontinue o uso de [Serializable] e ISerializable. O atributo [Serializable] e a interface ISerializable foram originalmente projetados para serializar tipos em ambientes confiáveis e são usados pelo Remoting. A serialização do WCF depende dos tipos marcados com [DataContract] e [DataMember]. Os tipos de dados usados por um aplicativo devem ser modificados para usar [DataContract] e não usar ISerializable ou [Serializable].

Cenários de migração

Agora vamos ver como realizar os seguintes cenários comuns do Remoting no WCF:

  1. O servidor retorna um objeto por valor para o cliente

  2. O servidor retorna um objeto por referência ao cliente

  3. O cliente envia um objeto por valor para o servidor

Observação

O envio de um objeto por referência do cliente para o servidor não é permitido no WCF.

Ao ler esses cenários, suponha que nossas interfaces de linha de base para comunicação remota do .NET se pareçam com o exemplo a seguir. A implementação de Comunicação Remota do .NET não é importante aqui porque queremos ilustrar apenas como usar o WCF para implementar a funcionalidade equivalente.

public class RemotingServer : MarshalByRefObject  
{  
    // Demonstrates server returning object by-value  
    public Customer GetCustomer(int customerId) {…}  
  
    // Demonstrates server returning object by-reference  
    public CustomerReference GetCustomerReference(int customerId) {…}  
  
    // Demonstrates client passing object to server by-value  
    public bool UpdateCustomer(Customer customer) {…}  
}  

Cenário 1: o serviço retorna um objeto por valor

Esse cenário demonstra um servidor retornando um objeto para o cliente por valor. O WCF sempre retorna objetos do servidor por valor, portanto, as etapas a seguir simplesmente descrevem como criar um serviço WCF normal.

  1. Comece definindo uma interface pública para o serviço WCF e marque-a com o atributo [ServiceContract]. Usamos [OperationContract] para identificar os métodos do lado do servidor que nosso cliente chamará.

    [ServiceContract]  
    public interface ICustomerService  
    {  
        [OperationContract]  
        Customer GetCustomer(int customerId);  
    
        [OperationContract]  
        bool UpdateCustomer(Customer customer);  
    }  
    
  2. A próxima etapa é criar o contrato de dados para esse serviço. Fazemos isso criando classes (não interfaces) marcadas com o atributo [DataContract]. As propriedades ou campos individuais que queremos visíveis para o cliente e o servidor são marcados com [DataMember]. Se quisermos que tipos derivados sejam permitidos, devemos usar o atributo [KnownType] para identificá-los. Os únicos tipos que o WCF permitirá que sejam serializados ou desserializados para esse serviço são aqueles na interface de serviço e esses "tipos conhecidos". A tentativa de trocar qualquer outro tipo que não esteja nesta lista será rejeitada.

    [DataContract]  
    [KnownType(typeof(PremiumCustomer))]  
    public class Customer  
    {  
        [DataMember]  
        public string FirstName { get; set; }  
    
        [DataMember]  
        public string LastName { get; set; }  
    
        [DataMember]  
        public int CustomerId { get; set; }  
    }  
    
    [DataContract]  
    public class PremiumCustomer : Customer
    {  
        [DataMember]  
        public int AccountId { get; set; }  
    }  
    
  3. Em seguida, fornecemos a implementação para a interface de serviço.

    public class CustomerService : ICustomerService  
    {  
        public Customer GetCustomer(int customerId)  
        {  
            // read from database  
        }  
    
        public bool UpdateCustomer(Customer customer)  
        {  
            // write to database  
        }  
    }  
    
  4. Para executar o serviço WCF, precisamos declarar um ponto de extremidade que expõe essa interface de serviço em uma URL específica usando uma associação WCF específica. Isso normalmente é feito adicionando as seções a seguir ao arquivo de web.config do projeto de servidor.

    <configuration>  
      <system.serviceModel>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    
  5. Em seguida, o serviço WCF pode ser iniciado com o seguinte código:

    ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));  
        customerServiceHost.Open();  
    

    Quando este ServiceHost é iniciado, ele usa o arquivo web.config para estabelecer o contrato, a vinculação e o endpoint adequados. Para obter mais informações sobre arquivos de configuração, consulte Configurando serviços usando arquivos de configuração. Esse estilo de iniciar o servidor é conhecido como auto-hospedagem. Para saber mais sobre outras opções para hospedar serviços do WCF, consulte Serviços de Hospedagem.

  6. O app.config do projeto cliente deve declarar informações de associação correspondentes para o ponto de extremidade do serviço. A maneira mais fácil de fazer isso no Visual Studio é usar a Referência de Adicionar Serviço, que atualizará automaticamente o arquivo app.config. Como alternativa, essas mesmas alterações podem ser adicionadas manualmente.

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    

    Para obter mais informações sobre como usar Adicionar Referência de Serviço, consulte Como adicionar, atualizar ou remover uma referência de serviço.

  7. Agora podemos chamar o serviço WCF do cliente. Fazemos isso criando uma fábrica de canais para esse serviço, solicitando um canal e chamando diretamente o método que queremos nesse canal. Podemos fazer isso porque o canal implementa a interface do serviço e manipula a lógica de solicitação/resposta subjacente para nós. O valor retornado dessa chamada de método é a cópia desserializada da resposta do servidor.

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    Customer customer = service.GetCustomer(42);  
    Console.WriteLine($"  Customer {customer.FirstName} {customer.LastName} received.");
    

Os objetos retornados pelo WCF do servidor para o cliente são sempre por valor. Os objetos são cópias desserializadas dos dados enviados pelo servidor. O cliente pode chamar métodos nessas cópias locais sem qualquer perigo de invocar o código do servidor por meio de retornos de chamada.

Cenário 2: O servidor retorna um objeto por referência

Esse cenário demonstra o servidor que fornece um objeto para o cliente por referência. No .NET Remoting, isso é tratado automaticamente para qualquer tipo derivado de MarshalByRefObject, que é serializado por referência. Um exemplo desse cenário é permitir que vários clientes tenham objetos independentes com estado de sessão no servidor. Como mencionado anteriormente, os objetos retornados por um serviço WCF são sempre por valor, portanto, não há equivalente direto de um objeto por referência, mas é possível alcançar algo semelhante à semântica por referência usando um EndpointAddress10 objeto. Esse é um objeto serializável por valor que pode ser usado pelo cliente para obter um objeto por referência de sessão no servidor. Isso possibilita o cenário de ter vários clientes com objetos no lado do servidor que mantêm sessão de forma independente.

  1. Primeiro, precisamos definir um contrato de serviço WCF que corresponda ao próprio objeto de sessão.

    [ServiceContract(SessionMode = SessionMode.Allowed)]  
        public interface ISessionBoundObject  
        {  
            [OperationContract]  
            string GetCurrentValue();  
    
            [OperationContract]  
            void SetCurrentValue(string value);  
        }  
    

    Dica

    Observe que o objeto sessionful está marcado com [ServiceContract], tornando-o uma interface de serviço WCF normal. Definir a propriedade SessionMode indica que será um serviço de sessão. WCF, uma sessão é uma forma de correlacionar várias mensagens enviadas entre dois pontos finais. Isso significa que, uma vez que um cliente obtém uma conexão com esse serviço, uma sessão será estabelecida entre o cliente e o servidor. O cliente usará uma única instância exclusiva do objeto do lado do servidor para todas as suas interações nesta única sessão.

  2. Em seguida, precisamos fornecer a implementação dessa interface de serviço. Ao denotá-lo com [ServiceBehavior] e definir o InstanceContextMode, informamos ao WCF que queremos usar uma instância exclusiva desse tipo para cada sessão.

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
        public class MySessionBoundObject : ISessionBoundObject  
        {  
            private string _value;  
    
            public string GetCurrentValue()  
            {  
                return _value;  
            }  
    
            public void SetCurrentValue(string val)  
            {  
                _value = val;  
            }  
    
        }  
    
  3. Agora, precisamos de uma maneira de obter uma instância deste objeto com sessão. Fazemos isso criando outra interface de serviço WCF que retorna um objeto EndpointAddress10. Essa é uma forma serializável de um ponto de extremidade que o cliente pode usar para criar o objeto de sessão.

    [ServiceContract]  
        public interface ISessionBoundFactory  
        {  
            [OperationContract]  
            EndpointAddress10 GetInstanceAddress();  
        }  
    

    E implementamos este serviço WCF:

    public class SessionBoundFactory : ISessionBoundFactory  
        {  
            public static ChannelFactory<ISessionBoundObject> _factory =
                new ChannelFactory<ISessionBoundObject>("sessionbound");  
    
            public SessionBoundFactory()  
            {  
            }  
    
            public EndpointAddress10 GetInstanceAddress()  
            {  
                IClientChannel channel = (IClientChannel)_factory.CreateChannel();  
                return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress);  
            }  
        }  
    

    Essa implementação mantém uma fábrica de canais única para criar objetos de sessão. Quando GetInstanceAddress() é chamado, ele cria um canal e cria um objeto EndpointAddress10 que efetivamente aponta para o endereço remoto associado a esse canal. EndpointAddress10 é simplesmente um tipo de dados que pode ser retornado ao cliente por valor.

  4. Precisamos modificar o arquivo de configuração do servidor fazendo as duas coisas a seguir, conforme mostrado no exemplo abaixo:

    1. Declare uma seção <cliente> que descreva o ponto de extremidade para o objeto de sessão. Isso é necessário porque o servidor também atua como um cliente nessa situação.

    2. Declare pontos de extremidade para a fábrica e o objeto de sessão. Isso é necessário para permitir que o cliente se comunicque com os pontos de extremidade de serviço para adquirir EndpointAddress10 e criar o canal de sessão.

    <configuration>  
      <system.serviceModel>  
         <client>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
        </client>  
        <services>  
          <service name="Server.CustomerService">  
            <endpoint address="http://localhost:8083/CustomerService"  
                      binding="basicHttpBinding"  
                      contract="Shared.ICustomerService" />  
          </service>  
          <service name="Server.MySessionBoundObject">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundObject"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundObject" />  
          </service>  
          <service name="Server.SessionBoundFactory">  
            <endpoint address="net.tcp://localhost:8081/SessionBoundFactory"  
                      binding="netTcpBinding"  
                      contract="Shared.ISessionBoundFactory" />  
          </service>  
        </services>  
      </system.serviceModel>  
    </configuration>  
    

    E, em seguida, podemos iniciar esses serviços:

    ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
    factoryHost.Open();  
    
    ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject));  
    sessionHost.Open();  
    
  5. Configuramos o cliente declarando esses mesmos endpoints no arquivo app.config do projeto.

    <configuration>  
      <system.serviceModel>  
        <client>  
          <endpoint name="customerservice"  
                    address="http://localhost:8083/CustomerService"  
                    binding="basicHttpBinding"  
                    contract="Shared.ICustomerService"/>  
          <endpoint name="sessionbound"  
                    address="net.tcp://localhost:8081/SessionBoundObject"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundObject"/>  
          <endpoint name="factory"  
                    address="net.tcp://localhost:8081/SessionBoundFactory"  
                    binding="netTcpBinding"  
                    contract="Shared.ISessionBoundFactory"/>  
        </client>  
      </system.serviceModel>  
    </configuration>  
    
  6. Para criar e usar esse objeto de sessão, o cliente deve executar as seguintes etapas:

    1. Crie um canal para o serviço ISessionBoundFactory.

    2. Use esse canal para invocar esse serviço para obter um EndpointAddress10.

    3. Use o EndpointAddress10 para criar um canal para obter um objeto de sessão.

    4. Interaja com o objeto de sessão para demonstrar que ele permanece a mesma instância em várias chamadas.

    ChannelFactory<ISessionBoundFactory> channelFactory =
        new ChannelFactory<ISessionBoundFactory>("factory");  
    ISessionBoundFactory sessionFactory = channelFactory.CreateChannel();  
    
    EndpointAddress10 address1 = sessionFactory.GetInstanceAddress();  
    EndpointAddress10 address2 = sessionFactory.GetInstanceAddress();  
    
    ChannelFactory<ISessionBoundObject> sessionObjectFactory1 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address1.ToEndpointAddress());  
    ChannelFactory<ISessionBoundObject> sessionObjectFactory2 =
        new ChannelFactory<ISessionBoundObject>(new NetTcpBinding(),
                                                address2.ToEndpointAddress());  
    
    ISessionBoundObject sessionInstance1 = sessionObjectFactory1.CreateChannel();  
    ISessionBoundObject sessionInstance2 = sessionObjectFactory2.CreateChannel();  
    
    sessionInstance1.SetCurrentValue("Hello");  
    sessionInstance2.SetCurrentValue("World");  
    
    if (sessionInstance1.GetCurrentValue() == "Hello" &&  
        sessionInstance2.GetCurrentValue() == "World")  
    {  
        Console.WriteLine("sessionful server object works as expected");  
    }  
    

O WCF sempre retorna objetos por valor, mas é possível dar suporte ao equivalente de semântica por referência por meio do uso de EndpointAddress10. Isso permite que o cliente solicite uma instância de serviço WCF com sessão, após o que ele pode interagir com a instância da mesma forma que qualquer outro serviço WCF.

Cenário 3: o cliente envia ao servidor uma instância de By-Value

Esse cenário demonstra o cliente enviando uma instância de objeto não primitiva para o servidor por valor. Como o WCF envia apenas objetos por valor, esse cenário demonstra o uso normal do WCF.

  1. Use o mesmo serviço WCF do Cenário 1.

  2. Use o cliente para criar um novo objeto por valor (Cliente), criar um canal para se comunicar com o serviço ICustomerService e enviar o objeto para ele.

    ChannelFactory<ICustomerService> factory =  
        new ChannelFactory<ICustomerService>("customerservice");  
    ICustomerService service = factory.CreateChannel();  
    PremiumCustomer customer = new PremiumCustomer {
    FirstName = "Bob",
    LastName = "Jones",
    CustomerId = 43,
    AccountId = 99};  
    bool success = service.UpdateCustomer(customer);  
    Console.WriteLine($"  Server returned {success}.");
    

    O objeto do cliente será serializado e enviado para o servidor, onde será desserializado em uma nova cópia desse objeto.

    Observação

    Esse código também ilustra o envio de um tipo derivado (PremiumCustomer). A interface de serviço espera um objeto Customer, mas o atributo [KnownType] na classe Customer indicou que PremiumCustomer também era permitido. O WCF falhará ao tentar serializar ou desserializar qualquer outro tipo por meio desta interface de serviço.

As trocas normais de dados do WCF são por valor. Isso garante que a invocação de métodos em um desses objetos de dados seja executada apenas localmente– ele não invocará o código na outra camada. Embora seja possível obter algo como objetos por referência retornados do servidor, não é possível que um cliente passe um objeto por referência para o servidor. Um cenário que requer uma conversa entre cliente e servidor pode ser obtido no WCF usando um serviço duplex. Para obter mais informações, consulte Duplex Services.

Resumo

A Comunicação Remota do .NET é uma estrutura de comunicação destinada a ser usada somente em ambientes totalmente confiáveis. É um produto herdado e tem suporte apenas para compatibilidade com versões anteriores. Ele não deve ser usado para criar novos aplicativos. Por outro lado, o WCF foi projetado com segurança em mente e é recomendado para aplicativos novos e existentes. A Microsoft recomenda que os aplicativos de Comunicações Remotas existentes sejam migrados para usar o WCF ou a API Web do ASP.NET.