Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se describe cómo migrar una aplicación que usa .NET Remoting para usar Windows Communication Foundation (WCF). Compara conceptos similares entre estos productos y, a continuación, describe cómo realizar varios escenarios comunes de comunicación remota en WCF.
.NET Remoting es un producto heredado que solo se admite para la compatibilidad con versiones anteriores. No es seguro en entornos de confianza mixta porque no puede mantener los niveles de confianza independientes entre el cliente y el servidor. Por ejemplo, nunca debe exponer un punto de conexión de .NET Remoting a Internet o a los clientes que no son de confianza. Se recomienda migrar las aplicaciones de comunicación remota existentes a tecnologías más recientes y seguras. Si el diseño de la aplicación solo usa HTTP y es RESTful, se recomienda ASP.NET API web. Para obtener más información, consulte ASP.NET API web. Si la aplicación se basa en SOAP o requiere protocolos que no sean Http como TCP, se recomienda WCF.
Comparación de .NET Remoting con WCF
En esta sección se comparan los elementos básicos de .NET Remoting con sus equivalentes de WCF. Usaremos estos bloques de creación más adelante para crear algunos escenarios comunes de cliente-servidor en WCF. En el gráfico siguiente se resumen las principales similitudes y diferencias entre .NET Remoting y WCF.
Comunicación remota de .NET | WCF (Windows Communication Foundation) | |
---|---|---|
tipo de servidor | Subclase MarshalByRefObject |
Marcar con [ServiceContract] atributo |
Operaciones de servicio | Métodos públicos en el tipo de servidor | Marcar con [OperationContract] atributo |
Serialización de |
ISerializable o [Serializable] |
DataContractSerializer o XmlSerializer |
Objetos pasados | Por valor o por referencia | Solo por valor |
Errores o excepciones | Cualquier excepción serializable | FaultContract<TDetail> |
Objetos de proxy de cliente | Los servidores proxy transparentes fuertemente tipados se crean automáticamente desde MarshalByRefObjects | Los proxy fuertemente tipados se generan a petición con ChannelFactory<TChannel> |
Plataforma necesaria | Tanto el cliente como el servidor deben usar microsoft OS y .NET | Multiplataforma |
Formato de mensaje | Privada | Estándares del sector (por ejemplo, SOAP y WS-*) |
Comparación de implementación del servidor
Creación de un servidor en .NET Remoting
Los tipos de servidor de .NET Remoting deben derivar de MarshalByRefObject y definir métodos al que puede llamar el cliente, como el siguiente:
public class RemotingServer : MarshalByRefObject
{
public Customer GetCustomer(int customerId) { … }
}
Los métodos públicos de este tipo de servidor se convierten en el contrato público disponible para los clientes. No hay ninguna separación entre la interfaz pública del servidor y su implementación: un tipo controla ambos.
Una vez definido el tipo de servidor, se puede poner a disposición de los clientes, como en el ejemplo siguiente:
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();
Hay muchas maneras de hacer que el tipo de Remoting esté disponible como servidor, incluido el uso de archivos de configuración. Esto es solo un ejemplo.
Creación de un servidor en WCF
El paso equivalente de WCF implica la creación de dos tipos: el "contrato de servicio" público y la implementación concreta. La primera se declara como una interfaz marcada con [ServiceContract]. Los métodos disponibles para los clientes se marcan con [OperationContract]:
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
Customer GetCustomer(int customerId);
}
La implementación del servidor se define en una clase concreta independiente, como en el ejemplo siguiente:
public class WCFServer : IWCFServer
{
public Customer GetCustomer(int customerId) { … }
}
Una vez definidos estos tipos, el servidor WCF se puede poner a disposición de los clientes, como en el ejemplo siguiente:
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();
}
Nota:
TCP se usa en ambos ejemplos para mantenerlos lo más similares posible. Consulte los tutoriales más adelante en este tema para obtener ejemplos usando HTTP.
Hay muchas maneras de configurar y hospedar servicios WCF. Este es solo un ejemplo, conocido como "autohospedado". Para obtener más información, consulte los temas siguientes:
Comparación de la implementación del cliente
Creación de un cliente en .NET Remoting
Una vez que se ha puesto a disposición un objeto de servidor de .NET Remoting, los clientes pueden consumirlo, como en el ejemplo siguiente:
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.");
La instancia remotingServer devuelta de Activator.GetObject() se conoce como un "proxy transparente". Implementa la API pública para el tipo RemotingServer en el cliente, pero todos los métodos llaman al objeto de servidor que se ejecuta en un proceso o máquina diferente.
Creación de un cliente en WCF
El paso equivalente de WCF implica el uso de un generador de canales para crear explícitamente el proxy. Al igual que la comunicación remota, el objeto proxy se puede usar para invocar operaciones en el servidor, como en el ejemplo siguiente:
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.");
En este ejemplo se muestra la programación a nivel de canal porque es más similar al ejemplo de Remoting. También está disponible el enfoque Agregar referencia de servicio en Visual Studio que genera código para simplificar la programación del cliente. Para obtener más información, consulte los temas siguientes:
Uso de serialización
Tanto .NET Remoting como WCF usan la serialización para enviar objetos entre el cliente y el servidor, pero difieren de estas maneras importantes:
Usan diferentes serializadores y convenciones para indicar lo que se va a serializar.
.NET Remoting admite la serialización "por referencia" que permite el acceso a métodos o propiedades en un nivel para ejecutar código en el otro nivel, que está a través de los límites de seguridad. Esta funcionalidad expone vulnerabilidades de seguridad y es una de las principales razones por las que los puntos de conexión remoto nunca deben exponerse a clientes que no son de confianza.
La serialización que usa .NET Remoting no es participativa (excluye explícitamente lo que no hay que serializar) y la serialización de WCF es participativa (marca explícitamente los miembros para serializar).
Serialización en .NET Remoting
.NET Remoting admite dos maneras de serializar y deserializar objetos entre el cliente y el servidor:
Por valor : los valores del objeto se serializan a través de los límites del nivel y se crea una nueva instancia de ese objeto en el otro nivel. Las llamadas a métodos o propiedades de esa nueva instancia solo se ejecutan localmente y no afectan al objeto o nivel original.
Por referencia: se serializa una "referencia de objeto" especial en los límites del nivel. Cuando un nivel interactúa con métodos o propiedades de ese objeto, se comunica de nuevo con el objeto original en el nivel original. Los objetos por referencia pueden fluir en cualquier dirección: servidor a cliente o cliente al servidor.
Los tipos por valor de Remoting se marcan con el atributo [Serializable] o implementan ISerializable, como en el siguiente ejemplo:
[Serializable]
public class RemotingCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
Los tipos de referencia derivan de la clase MarshalByRefObject, como en el ejemplo siguiente:
public class RemotingCustomerReference : MarshalByRefObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
Es muy importante comprender las implicaciones de los objetos por referencia de Remoting. Si cualquiera de los niveles (cliente o servidor) envía un objeto por referencia al otro nivel, todas las llamadas de método se ejecutan de nuevo en el nivel que posee el objeto. Por ejemplo, un cliente que llama a métodos en un objeto por referencia devuelto por el servidor ejecutará código en el servidor. De forma similar, un servidor que llama a métodos de un objeto por referencia proporcionado por el cliente ejecutará el código nuevamente en el cliente. Por este motivo, el uso de .NET Remoting solo se recomienda en entornos de plena confianza. Exponer un punto de conexión de .NET Remoting público a clientes que no son de confianza hará que un servidor remoto sea vulnerable a ataques.
Serialización en WCF
WCF solo admite la serialización por valor. La manera más común de definir un tipo para intercambiar entre el cliente y el servidor es como en el ejemplo siguiente:
[DataContract]
public class WCFCustomer
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public int CustomerId { get; set; }
}
El atributo [DataContract] identifica este tipo como uno que se puede serializar y deserializar entre el cliente y el servidor. El atributo [DataMember] identifica las propiedades o campos individuales que se van a serializar.
Cuando WCF envía un objeto entre niveles, serializa solo los valores y crea una nueva instancia del objeto en el otro nivel. Las interacciones con los valores del objeto solo se producen localmente: no se comunican con el otro nivel como lo hacen los objetos de .NET Remoting by-reference. Para obtener más información, consulte Serialización y deserialización.
Funcionalidades de control de excepciones
Excepciones en .NET Remoting
Las excepciones producidas por un servidor de comunicación remota se serializan, se envían al cliente y se lanzan localmente en el cliente, al igual que cualquier otra excepción. Las excepciones personalizadas se pueden crear subclasificando el tipo Exception y marcándola con [Serializable]. La mayoría de las excepciones del marco de trabajo ya están marcadas de esta manera, lo que permite que sean lanzadas por el servidor, serializadas y vuelvan a lanzarse en el cliente. Aunque este diseño es cómodo durante el desarrollo, la información del lado servidor se puede revelar involuntariamente al cliente. Esta es una de las muchas razones por las que la comunicación remota solo se debe usar en entornos de plena confianza.
Excepciones y errores en WCF
WCF no permite devolver tipos de excepciones arbitrarios desde el servidor al cliente porque podría provocar divulgación involuntaria de información. Si una operación de servicio produce una excepción inesperada, hace que se produzca una excepción FaultException de propósito general en el cliente. Esta excepción no contiene información sobre por qué o dónde se produjo el problema, y para algunas aplicaciones esto es suficiente. Las aplicaciones que necesitan comunicar información de error más completa al cliente lo hacen definiendo un contrato de error.
Para ello, primero crean un tipo [DataContract] para transportar la información del error.
[DataContract]
public class CustomerServiceFault
{
[DataMember]
public string ErrorMessage { get; set; }
[DataMember]
public int CustomerId {get;set;}
}
Especifique el contrato de error que se usará para cada operación de servicio.
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
[FaultContract(typeof(CustomerServiceFault))]
Customer GetCustomer(int customerId);
}
El servidor informa las condiciones de error lanzando una excepción FaultException.
throw new FaultException<CustomerServiceFault>(
new CustomerServiceFault() {
CustomerId = customerId,
ErrorMessage = "Illegal customer Id"
});
Y cada vez que el cliente realiza una solicitud al servidor, puede detectar errores como excepciones normales.
try
{
Customer customer = server.GetCustomer(-1);
}
catch (FaultException<CustomerServiceFault> fault)
{
Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}
Para obtener más información sobre los contratos de error, vea FaultException.
Consideraciones de seguridad
Seguridad en .NET Remoting
Algunos canales de .NET Remoting admiten características de seguridad como la autenticación y el cifrado en la capa de canal (IPC y TCP). El canal HTTP se basa en Internet Information Services (IIS) para la autenticación y el cifrado. A pesar de este apoyo, debe considerar .NET Remoting como un protocolo de comunicación no seguro y usarlo solo dentro de entornos de plena confianza. Nunca exponga un punto de conexión remoto público a Internet o a clientes que no sean de confianza.
Seguridad en WCF
WCF se diseñó teniendo en cuenta la seguridad, en parte para abordar los tipos de vulnerabilidades que se encuentran en .NET Remoting. WCF ofrece seguridad en el nivel de transporte y mensaje, y ofrece muchas opciones para la autenticación, autorización, cifrado, etc. Para obtener más información, consulte los temas siguientes:
Migración a WCF
¿Por qué migrar de Remoting a WCF?
.NET Remoting es un producto heredado. Como se describe en .NET Remoting, se considera un producto heredado y no se recomienda para el nuevo desarrollo. Se recomienda WCF o ASP.NET WEB API para aplicaciones nuevas y existentes.
WCF usa estándares multiplataforma. WCF se diseñó teniendo en cuenta la interoperabilidad multiplataforma y admite muchos estándares del sector (SOAP, WS-Security, WS-Trust, etc.). Un servicio WCF puede interoperar con clientes que se ejecutan en sistemas operativos distintos de Windows. La comunicación remota se diseñó principalmente para entornos en los que las aplicaciones cliente y de servidor se ejecutan mediante .NET Framework en un sistema operativo Windows.
WCF tiene seguridad integrada. WCF se diseñó teniendo en cuenta la seguridad y ofrece muchas opciones para la autenticación, la seguridad de nivel de transporte, la seguridad de nivel de mensaje, etc. La comunicación remota se diseñó para facilitar la interoperación de aplicaciones, pero no se diseñó para ser segura en entornos que no son de confianza. WCF se diseñó para funcionar en entornos de confianza y no de confianza.
Recomendaciones de migración
A continuación se indican los pasos recomendados para migrar de .NET Remoting a WCF:
Cree el contrato de servicio. Defina los tipos de interfaz de servicio y marquelos con el atributo [ServiceContract]. Marque todos los métodos a los que los clientes podrán llamar con [OperationContract].
Cree el contrato de datos. Defina los tipos de datos que se intercambiarán entre el servidor y el cliente y los marque con el atributo [DataContract]. Marque todos los campos y propiedades que el cliente podrá usar con [DataMember].
Cree el contrato de error (opcional). Cree los tipos que se intercambiarán entre el servidor y el cliente cuando se encuentren errores. Marque estos tipos con [DataContract] y [DataMember] para que sean serializables. Marque también con [FaultContract] todas las operaciones de servicio que marcó con [OperationContract] para indicar qué errores pueden devolver.
Configurar y hospedar el servicio. Una vez creado el contrato de servicio, el siguiente paso es configurar un enlace para exponer el servicio en un punto de conexión. Para obtener más información, vea Puntos de conexión: direcciones, enlaces y contratos.
Una vez que se ha migrado una aplicación de comunicación remota a WCF, todavía es importante quitar dependencias en .NET Remoting. Esto asegura que las vulnerabilidades de comunicación remota se eliminen de la aplicación. Estos pasos incluyen lo siguiente:
Descontinue el uso de MarshalByRefObject. El tipo MarshalByRefObject solo existe para comunicación remota y WCF no lo usa. Cualquier tipo de aplicación que sea subclase de MarshalByRefObject debe eliminarse o modificarse.
Interrumpa el uso de [Serializable] e ISerializable. El atributo [Serializable] y la interfaz ISerializable se diseñaron originalmente para serializar los tipos dentro de entornos de confianza y Remoting los usa. La serialización de WCF se basa en los tipos que se marcan con [DataContract] y [DataMember]. Los tipos de datos usados por una aplicación deben modificarse para usar [DataContract] en lugar de usar ISerializable o [Serializable].
Escenarios de migración
Ahora veamos cómo realizar los siguientes escenarios comunes de comunicación remota en WCF:
El servidor devuelve un objeto por valor al cliente
El servidor devuelve un objeto por referencia al cliente.
El cliente envía un objeto por valor al servidor
Nota:
No se permite enviar un objeto por referencia desde el cliente al servidor en WCF.
Al leer estos escenarios, supongamos que nuestras interfaces de línea base para .NET Remoting tienen un aspecto similar al ejemplo siguiente. La implementación de .NET Remoting no es importante aquí porque queremos ilustrar solo cómo usar WCF para implementar una funcionalidad 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) {…}
}
Escenario 1: El servicio devuelve un objeto por valor
En este escenario se muestra un servidor que devuelve un objeto al cliente por valor. WCF siempre devuelve objetos del servidor por valor, por lo que los pasos siguientes simplemente describen cómo crear un servicio WCF normal.
Empiece por definir una interfaz pública para el servicio WCF y marcarla con el atributo [ServiceContract]. Usamos [OperationContract] para identificar los métodos del lado servidor a los que llamará nuestro cliente.
[ServiceContract] public interface ICustomerService { [OperationContract] Customer GetCustomer(int customerId); [OperationContract] bool UpdateCustomer(Customer customer); }
El siguiente paso es crear el contrato de datos para este servicio. Para ello, creamos clases (no interfaces) marcadas con el atributo [DataContract]. Las propiedades o campos individuales que queremos que sean visibles para el cliente y el servidor están marcados con [DataMember]. Si queremos que se permitan tipos derivados, debemos usar el atributo [KnownType] para identificarlos. Los únicos tipos que WCF permite serializar o deserializar para este servicio son los de la interfaz de servicio y estos "tipos conocidos". Se rechazará el intento de intercambiar cualquier otro tipo que no esté en esta lista.
[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; } }
A continuación, proporcionamos la implementación de la interfaz de servicio.
public class CustomerService : ICustomerService { public Customer GetCustomer(int customerId) { // read from database } public bool UpdateCustomer(Customer customer) { // write to database } }
Para ejecutar el servicio WCF, es necesario declarar un punto de conexión que exponga esa interfaz de servicio en una dirección URL específica mediante un enlace WCF específico. Esto suele hacerse agregando las secciones siguientes al archivo web.config del proyecto 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>
A continuación, el servicio WCF se puede iniciar con el código siguiente:
ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService)); customerServiceHost.Open();
Cuando se inicia este ServiceHost, usa el archivo web.config para establecer el contrato, el enlace y el punto de conexión adecuados. Para obtener más información sobre los archivos de configuración, consulte Configuración de servicios mediante archivos de configuración. Este estilo de inicio del servidor se conoce como autohospedaje. Para obtener más información sobre otras opciones para hospedar servicios WCF, consulte Servicios de hospedaje.
El app.config del proyecto cliente debe declarar información de vinculación que coincida con el punto de conexión del servicio. La manera más fácil de hacerlo en Visual Studio es usar Agregar referencia de servicio, que actualizará automáticamente el archivo app.config. Como alternativa, estos mismos cambios se pueden agregar manualmente.
<configuration> <system.serviceModel> <client> <endpoint name="customerservice" address="http://localhost:8083/CustomerService" binding="basicHttpBinding" contract="Shared.ICustomerService"/> </client> </system.serviceModel> </configuration>
Para obtener más información sobre cómo usar Agregar referencia de servicio, vea How to: Add, Update, or Remove a Service Reference.
Ahora podemos llamar al servicio WCF desde el cliente. Para ello, creamos una fábrica de canales para ese servicio, solicitamos un canal y llamamos directamente al método que queremos en ese canal. Podemos hacerlo porque el canal implementa la interfaz del servicio y controla la lógica de solicitud y respuesta subyacente para nosotros. El valor devuelto de esa llamada al método es la copia deserializada de la respuesta del 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.");
Los objetos devueltos por WCF desde el servidor al cliente siempre son por valor. Los objetos son copias deserializadas de los datos enviados por el servidor. El cliente puede llamar a métodos en estas copias locales sin ningún peligro de invocar código de servidor a través de callbacks.
Escenario 2: El servidor devuelve un objeto por referencia
En este escenario se muestra el servidor que proporciona un objeto al cliente por referencia. En .NET Remoting, esto se controla de forma automática para cualquier tipo derivado de MarshalByRefObject, que se serializa por referencia. Un ejemplo de este escenario es permitir que varios clientes tengan objetos del lado servidor con sesión independientes. Como se mencionó anteriormente, los objetos devueltos por un servicio WCF siempre son por valor, por lo que no hay ningún equivalente directo de un objeto por referencia, pero es posible lograr algo similar a la semántica de referencia mediante un EndpointAddress10 objeto . Se trata de un objeto por valor serializable que puede usar el cliente para obtener un objeto por referencia con sesión en el servidor. Esto habilita el escenario de tener varios clientes con objetos del lado servidor con sesión independientes.
En primer lugar, es necesario definir un contrato de servicio WCF que corresponda al propio objeto con sesión.
[ServiceContract(SessionMode = SessionMode.Allowed)] public interface ISessionBoundObject { [OperationContract] string GetCurrentValue(); [OperationContract] void SetCurrentValue(string value); }
Sugerencia
Observe que el objeto con sesión está marcado con [ServiceContract], convirtiéndolo en una interfaz de servicio WCF normal. El hecho de establecer la propiedad SessionMode indica que será un servicio con sesión. En WCF, una sesión es una manera de correlacionar varios mensajes enviados entre dos puntos de conexión. Esto significa que una vez que un cliente obtiene una conexión a este servicio, se establecerá una sesión entre el cliente y el servidor. El cliente usará una única instancia del objeto del lado servidor para todas sus interacciones dentro de esta sola sesión.
A continuación, es necesario proporcionar la implementación de esta interfaz de servicio. Al indicarlo con [ServiceBehavior] y estableciendo InstanceContextMode, le indicamos a WCF que queremos usar una instancia única de este tipo para cada sesión.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class MySessionBoundObject : ISessionBoundObject { private string _value; public string GetCurrentValue() { return _value; } public void SetCurrentValue(string val) { _value = val; } }
Ahora necesitamos un modo de obtener una instancia de este objeto con sesión. Para ello, creamos otra interfaz de servicio WCF que devuelve un objeto EndpointAddress10. Se trata de una forma serializable de un punto de conexión que el cliente puede usar para crear el objeto con sesión.
[ServiceContract] public interface ISessionBoundFactory { [OperationContract] EndpointAddress10 GetInstanceAddress(); }
Y implementamos este servicio 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); } }
Esta implementación mantiene un generador de canales de singleton para crear objetos con sesión. Cuando se llama a GetInstanceAddress(), crea un canal y crea un objeto EndpointAddress10 que apunta eficazmente a la dirección remota asociada a este canal. EndpointAddress10 es simplemente un tipo de datos que se puede devolver al cliente por valor.
Es necesario modificar el archivo de configuración del servidor haciendo las dos cosas siguientes, como se muestra en el ejemplo siguiente:
Declare una sección <cliente> que describa el punto de conexión para el objeto con sesión. Esto es necesario porque el servidor también actúa como cliente en esta situación.
Declare los extremos para el objeto de generador y el objeto con sesión. Esto es necesario para permitir que el cliente se comunique con los puntos de conexión del servicio para adquirir EndpointAddress10 y para crear el canal con sesión.
<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>
Y, a continuación, podemos iniciar estos servicios:
ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory)); factoryHost.Open(); ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject)); sessionHost.Open();
Configuramos el cliente declarando estos mismos puntos de conexión en el archivo app.config del proyecto.
<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>
Para crear y usar este objeto con sesión, el cliente debe realizar los pasos siguientes:
Cree un canal en el servicio ISessionBoundFactory.
Usa ese canal para invocar ese servicio y obtener un EndpointAddress10.
Usar EndpointAddress10 para crear un canal para obtener un objeto con sesión.
Interactuar con el objeto con sesión para mostrar que sigue siendo la misma instancia en varias llamadas.
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"); }
WCF siempre devuelve objetos por valor, pero es posible admitir el equivalente de semántica por referencia mediante el uso de EndpointAddress10. Esto permite al cliente solicitar una instancia de servicio WCF con sesión, después de lo cual puede interactuar con él como cualquier otro servicio WCF.
Escenario 3: El cliente envía un servidor a una instancia de By-Value
Este escenario muestra el cliente enviando una instancia de objeto no primitivo al servidor por valor. Dado que WCF solo envía objetos por valor, este escenario muestra el uso normal de WCF.
Use el mismo servicio WCF del escenario 1.
Utilice el cliente para crear un nuevo objeto por valor (Customer), establecer un canal para comunicarse con el servicio ICustomerService, y enviarle el objeto.
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}.");
El objeto de cliente se serializará y se enviará al servidor, donde se deserializa en una nueva copia de ese objeto.
Nota:
Este código también muestra el envío de un tipo derivado (PremiumCustomer). La interfaz de servicio espera un objeto Customer, pero el atributo [KnownType] en la clase Customer indica que también se permite PremiumCustomer. WCF rechazará cualquier intento de serializar o deserializar cualquier otro tipo mediante esta interfaz de servicio.
Los intercambios normales de datos de WCF son por valor. Esto garantiza que la invocación de métodos en uno de estos objetos de datos solo se ejecuta localmente; no invocará código en el otro nivel. Aunque es posible lograr algo parecido a los objetos de referencia devueltos desde el servidor, no es posible que un cliente pase un objeto por referencia al servidor. Un escenario que requiere una conversación entre el cliente y el servidor se puede lograr en WCF mediante un servicio dúplex. Para obtener más información, consulte Servicios dúplex.
Resumen
.NET Remoting es un marco de comunicación diseñado para usarse solo en entornos de plena confianza. Es un producto heredado y solo se admite para la compatibilidad con versiones anteriores. No se debe usar para compilar nuevas aplicaciones. Por el contrario, WCF se diseñó teniendo en cuenta la seguridad y se recomienda para las aplicaciones nuevas y existentes. Microsoft recomienda migrar las aplicaciones de comunicación remota existentes para usar WCF o ASP.NET API web en su lugar.