Nota
L'accés a aquesta pàgina requereix autorització. Podeu provar d'iniciar la sessió o de canviar els directoris.
L'accés a aquesta pàgina requereix autorització. Podeu provar de canviar els directoris.
Windows Communication Foundation (WCF) es la opción recomendada y segura sobre el modelo de objetos de componente distribuido (DCOM) para las llamadas de código administrado entre servidores y clientes en un entorno distribuido. En este artículo se muestra cómo migrar código de DCOM a WCF para los escenarios siguientes.
El servicio remoto devuelve un objeto por valor al cliente.
El cliente envía un objeto por valor al servicio remoto.
El servicio remoto devuelve un objeto por referencia al cliente.
Por motivos de seguridad, no se permite enviar un objeto por referencia desde el cliente al servicio en WCF. 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 sobre los servicios dúplex, consulte Servicios dúplex.
Para obtener más información sobre cómo crear servicios y clientes WCF para esos servicios, vea Programación básica de WCF, Diseño e Implementación de servicios y Creación de clientes.
Código de ejemplo de DCOM
En estos escenarios, las interfaces DCOM que se muestran mediante WCF tienen la estructura siguiente:
[ComVisible(true)]
[Guid("AA9C4CDB-55EA-4413-90D2-843F1A49E6E6")]
public interface IRemoteService
{
Customer GetObjectByValue();
IRemoteObject GetObjectByReference();
void SendObjectByValue(Customer customer);
}
[ComVisible(true)]
[Guid("A12C98DE-B6A1-463D-8C24-81E4BBC4351B")]
public interface IRemoteObject
{
}
public class Customer
{
}
El servicio devuelve un objeto por valor
En este escenario, realizas una llamada a un servicio y el método devuelve un objeto, que es pasado por valor del servidor al cliente. Este escenario representa la siguiente llamada COM:
public interface IRemoteService
{
Customer GetObjectByValue();
}
En este escenario, el cliente recibe una copia deserializada de un objeto del servicio remoto. El cliente puede interactuar con esta copia local sin llamar al servicio. En otras palabras, se garantiza que el servicio no estará implicado de ninguna manera cuando se llame a los métodos de la copia local. WCF siempre devuelve objetos del servicio por valor, por lo que los pasos siguientes describen la creación de un servicio WCF normal.
Paso 1: Definir la interfaz de servicio WCF
Defina una interfaz pública para el servicio WCF y marquela con el atributo [ServiceContractAttribute]. Marque los métodos que desea exponer a los clientes con el atributo [OperationContractAttribute]. En el ejemplo siguiente se muestra el uso de estos atributos para identificar la interfaz del lado servidor y los métodos de interfaz a los que puede llamar un cliente. El método usado para este escenario se muestra en negrita.
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
. . .
[ServiceContract]
public interface ICustomerManager
{
[OperationContract]
void StoreCustomer(Customer customer);
[OperationContract] Customer GetCustomer(string firstName, string lastName);
}
Paso 2: Definir el contrato de datos
A continuación, debe crear un contrato de datos para el servicio, que describirá cómo se intercambiarán los datos entre el servicio y sus clientes. Las clases descritas en el contrato de datos deben marcarse con el atributo [DataContractAttribute]. Las propiedades o campos individuales que desea que sean visibles para el cliente y el servidor deben marcarse con el atributo [DataMemberAttribute]. Si desea que se permitan los tipos derivados de una clase del contrato de datos, debe identificarlos con el atributo [KnownTypeAttribute]. WCF solo serializará o deserializará tipos en la interfaz de servicio y los tipos identificados como tipos conocidos. Si intenta usar un tipo que no es un tipo conocido, se producirá una excepción.
Para obtener más información sobre los contratos de datos, consulte Contratos de datos.
[DataContract]
[KnownType(typeof(PremiumCustomer))]
public class Customer
{
[DataMember]
public string Firstname;
[DataMember]
public string Lastname;
[DataMember]
public Address DefaultDeliveryAddress;
[DataMember]
public Address DefaultBillingAddress;
}
[DataContract]
public class PremiumCustomer : Customer
{
[DataMember]
public int AccountID;
}
[DataContract]
public class Address
{
[DataMember]
public string Street;
[DataMember]
public string Zipcode;
[DataMember]
public string City;
[DataMember]
public string State;
[DataMember]
public string Country;
}
Paso 3: Implementar el servicio WCF
A continuación, debe implementar la clase de servicio WCF que implementa la interfaz que definió en el paso anterior.
public class CustomerService: ICustomerManager
{
public void StoreCustomer(Customer customer)
{
// write to a database
}
public Customer GetCustomer(string firstName, string lastName)
{
// read from a database
}
}
Paso 4: Configurar el servicio y el cliente
Para ejecutar un servicio WCF, debe 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. Un enlace especifica los detalles de transporte, codificación y protocolo para que los clientes y el servidor se comuniquen. Normalmente, se agregan enlaces al archivo de configuración del proyecto de servicio (web.config). A continuación se muestra una entrada de binding para el servicio de ejemplo:
<configuration>
<system.serviceModel>
<services>
<service name="Server.CustomerService">
<endpoint address="http://localhost:8083/CustomerManager"
binding="basicHttpBinding"
contract="Shared.ICustomerManager" />
</service>
</services>
</system.serviceModel>
</configuration>
A continuación, debe configurar el cliente para que coincida con la información de enlace especificada por el servicio. Para ello, agregue lo siguiente al archivo de configuración de la aplicación del cliente (app.config).
<configuration>
<system.serviceModel>
<client>
<endpoint name="customermanager"
address="http://localhost:8083/CustomerManager"
binding="basicHttpBinding"
contract="Shared.ICustomerManager"/>
</client>
</system.serviceModel>
</configuration>
Paso 5: Ejecución del servicio
Por último, puede hospedarlo automáticamente en una aplicación de consola agregando las líneas siguientes a la aplicación de servicio e iniciando la aplicación. Para obtener más información sobre otras formas de hospedar una aplicación de servicio WCF, Servicios de hospedaje.
ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));
customerServiceHost.Open();
Paso 6: Llamar al servicio desde el cliente
Para llamar al servicio desde el cliente, debe crear un generador de canales para el servicio y solicitar un canal, que le permitirá llamar directamente al GetCustomer método directamente desde el cliente. El canal implementa la interfaz del servicio y gestiona la lógica subyacente de solicitud/respuesta. El valor devuelto de esa llamada al método es la copia deserializada de la respuesta del servicio.
ChannelFactory<ICustomerManager> factory =
new ChannelFactory<ICustomerManager>("customermanager");
ICustomerManager service = factory.CreateChannel();
Customer customer = service.GetCustomer("Mary", "Smith");
El cliente envía un objeto por valor al servidor.
En este escenario, el cliente envía un objeto al servidor, por valor. Esto significa que el servidor recibirá una copia deserializada del objeto . El servidor puede llamar a métodos en esa copia y se garantiza que no hay ninguna devolución de llamada en el código cliente. Como se mencionó anteriormente, los intercambios normales de datos de WCF son por valor. Esto garantiza que la llamada a métodos en uno de estos objetos solo se ejecuta localmente; no invocará código en el cliente.
Este escenario representa la siguiente llamada al método COM:
public interface IRemoteService
{
void SendObjectByValue(Customer customer);
}
En este escenario se usa la misma interfaz de servicio y contrato de datos, como se muestra en el primer ejemplo. Además, el cliente y el servicio se configurarán de la misma manera. En este ejemplo, se crea un canal para enviar el objeto y ejecutar de la misma manera. En este ejemplo, creará un cliente que invocará el servicio, pasando un objeto por valor. El método de servicio al que llamará el cliente en el contrato de servicio se muestra en negrita:
[ServiceContract]
public interface ICustomerManager
{
[OperationContract] void StoreCustomer(Customer customer);
[OperationContract]
Customer GetCustomer(string firstName, string lastName);
}
Agregar código al cliente que envía un objeto por valor
En el código siguiente se muestra cómo el cliente crea un nuevo objeto de cliente por valor, crea un canal para comunicarse con el ICustomerManager servicio y envía el objeto de cliente a él.
El objeto de cliente se serializará y se enviará al servicio, donde el servicio lo deserializa en una nueva copia de ese objeto. Los métodos a los que llama el servicio en este objeto solo se ejecutarán localmente en el servidor. Es importante tener en cuenta que este código muestra el envío de un tipo derivado (PremiumCustomer). El contrato de servicio espera un Customer objeto, pero el contrato de datos de servicio usa el atributo [KnownTypeAttribute] para indicar que PremiumCustomer también se permite. WCF producirá un error al intentar serializar o deserializar cualquier otro tipo a través de esta interfaz de servicio.
PremiumCustomer customer = new PremiumCustomer();
customer.Firstname = "John";
customer.Lastname = "Doe";
customer.DefaultBillingAddress = new Address();
customer.DefaultBillingAddress.Street = "One Microsoft Way";
customer.DefaultDeliveryAddress = customer.DefaultBillingAddress;
customer.AccountID = 42;
ChannelFactory<ICustomerManager> factory =
new ChannelFactory<ICustomerManager>("customermanager");
ICustomerManager customerManager = factory.CreateChannel();
customerManager.StoreCustomer(customer);
El servicio devuelve un objeto por referencia.
En este escenario, la aplicación cliente realiza una llamada al servicio remoto y el método devuelve un objeto , que se pasa por referencia del servicio al cliente.
Como se mencionó anteriormente, los servicios WCF siempre devuelven el objeto por valor. Sin embargo, puede lograr un resultado similar mediante la EndpointAddress10 clase . EndpointAddress10 es un objeto serializable por valor que el cliente puede usar para obtener un objeto por referencia con estado de sesión en el servidor.
El comportamiento del objeto por referencia en WCF que se muestra en este escenario es diferente de DCOM. En DCOM, el servidor puede devolver un objeto por referencia al cliente directamente y el cliente puede llamar a los métodos de ese objeto, que se ejecutan en el servidor. Sin embargo, en WCF, el objeto devuelto siempre es por valor. El cliente debe tomar ese objeto by-value, representado por EndpointAddress10 y usarlo para crear su propio objeto sessionful by-reference. El método del cliente llama al objeto con estado de sesión que se ejecuta en el servidor. En otras palabras, este objeto que se pasa por referencia en WCF es un servicio WCF normal que está configurado para mantener estado de 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. Los contratos WCF con sesión son similares a los patrones de solicitud y respuesta de red orientados a la conexión.
Este escenario se representa mediante el siguiente método DCOM.
public interface IRemoteService
{
IRemoteObject GetObjectByReference();
}
Paso 1: Definir la interfaz e implementación del servicio WCF basado en sesión
En primer lugar, defina una interfaz de servicio WCF que contenga el objeto con sesión.
En este código, el objeto con sesión se marca con el ServiceContract atributo , que lo identifica como una interfaz de servicio WCF normal. Además, la SessionMode propiedad se establece para indicar que será un servicio con estado de sesión.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface ISessionBoundObject
{
[OperationContract]
string GetCurrentValue();
[OperationContract]
void SetCurrentValue(string value);
}
El código siguiente muestra la implementación del servicio.
El servicio se marca con el atributo [ServiceBehavior] y su propiedad InstanceContextMode establecida en InstanceContextMode.PerSessions para indicar que se debe crear 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;
}
}
Paso 2: Definir el servicio de factoría WCF para el objeto con sesión
El servicio que crea el objeto con sesión debe definirse e implementarse. En el código siguiente se muestra cómo hacerlo. Este código crea otro servicio WCF que devuelve un EndpointAddress10 objeto . Se trata de una forma serializable de un punto de conexión que puede usar para crear el objeto session-full.
[ServiceContract]
public interface ISessionBoundFactory
{
[OperationContract]
EndpointAddress10 GetInstanceAddress();
}
A continuación se muestra la implementación de este servicio. 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 en la dirección remota asociada a este canal.
EndpointAddress10 es un tipo de dato que se puede devolver al cliente por valor.
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);
}
}
Paso 3: Configurar e iniciar los servicios WCF
Para hospedar estos servicios, deberá realizar las siguientes adiciones al archivo de configuración del servidor (web.config).
Agregue una
<client>sección que describa el punto de conexión para el objeto con sesión. En este escenario, el servidor también actúa como cliente y debe configurarse para habilitarlo.En la
<services>sección, declare los puntos de conexión de servicio para la fábrica y el objeto con estado de sesión. Esto permite al cliente comunicarse con los puntos de conexión de servicio, adquirir EndpointAddress10 y crear el canal con sesión.
A continuación se muestra un archivo de configuración de ejemplo con estos valores:
<configuration>
<system.serviceModel>
<client>
<endpoint name="sessionbound"
address="net.tcp://localhost:8081/SessionBoundObject"
binding="netTcpBinding"
contract="Shared.ISessionBoundObject"/>
</client>
<services>
<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>
Agregue las líneas siguientes a una aplicación de consola para hospedar automáticamente el servicio e inicie la aplicación.
ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));
factoryHost.Open();
ServiceHost sessionBoundServiceHost = new ServiceHost(
typeof(MySessionBoundObject));
sessionBoundServiceHost.Open();
Paso 4: Configurar el cliente y llamar al servicio
Configure el cliente para comunicarse con los servicios WCF realizando las siguientes entradas en el archivo de configuración de la aplicación del proyecto (app.config).
<configuration>
<system.serviceModel>
<client>
<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 llamar al servicio, agregue el código al cliente para hacer lo siguiente:
Cree un canal para el
ISessionBoundFactoryservicio.Utilice el canal para invocar el servicio
ISessionBoundFactoryy obtener un objeto EndpointAddress10.Usa el EndpointAddress10 para crear un canal para obtener un objeto que mantenga la sesión.
Llame a los métodos
SetCurrentValueyGetCurrentValuepara demostrar que se utiliza la misma instancia de objeto en varias llamadas.
ChannelFactory<ISessionBoundFactory> factory =
new ChannelFactory<ISessionBoundFactory>("factory");
ISessionBoundFactory sessionBoundFactory = factory.CreateChannel();
EndpointAddress10 address = sessionBoundFactory.GetInstanceAddress();
ChannelFactory<ISessionBoundObject> sessionBoundObjectFactory =
new ChannelFactory<ISessionBoundObject>(
new NetTcpBinding(),
address.ToEndpointAddress());
ISessionBoundObject sessionBoundObject =
sessionBoundObjectFactory.CreateChannel();
sessionBoundObject.SetCurrentValue("Hello");
if (sessionBoundObject.GetCurrentValue() == "Hello")
{
Console.WriteLine("Session-full instance management works as expected");
}