Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Windows Communication Foundation (WCF) — это рекомендуемый и безопасный выбор распределенной объектной модели компонентов (DCOM) для вызовов управляемого кода между серверами и клиентами в распределенной среде. В этой статье показано, как перенести код из DCOM в WCF для следующих сценариев.
Удаленная служба возвращает клиенту объект по значению
Клиент отправляет объект по значению в удаленный сервис.
Удаленная служба возвращает объект по ссылке клиенту.
По соображениям безопасности отправка объекта по ссылке от клиента в службу не допускается в WCF. Сценарий, требующий беседы между клиентом и сервером, можно достичь в WCF с помощью дуплексной службы. Дополнительные сведения о дуплексных службах см. в разделе "Дуплексные службы".
Дополнительные сведения о создании служб и клиентов WCF для этих служб см. в статье "Базовый программирование WCF", "Проектирование и реализация служб" и "Создание клиентов".
Пример кода DCOM
Для этих сценариев интерфейсы DCOM, которые иллюстрируются с помощью WCF, имеют следующую структуру:
[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
{
}
Служба возвращает объект по значению
В этом сценарии вы вызываете службу, метод которой возвращает объект, который передается по значению с сервера клиенту. Этот сценарий представляет следующий вызов COM:
public interface IRemoteService
{
Customer GetObjectByValue();
}
В этом сценарии клиент получает десериализированную копию объекта из удаленной службы. Клиент может взаимодействовать с этой локальной копией без вызова службы. Другими словами, клиенту гарантируется, что служба не будет вовлечена каким-либо образом при вызове методов на локальной копии. WCF всегда возвращает объекты из службы по значению, поэтому следующие шаги описывают создание обычной службы WCF.
Шаг 1. Определение интерфейса службы WCF
Определите общедоступный интерфейс для службы WCF и пометьте его атрибутом [ServiceContractAttribute] . Пометьте методы, которые необходимо предоставить клиентам с помощью атрибута [OperationContractAttribute]. В следующем примере показано использование этих атрибутов для идентификации серверного интерфейса и методов интерфейса, которые клиент может вызывать. Метод, используемый для этого сценария, показан полужирным шрифтом.
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);
}
Шаг 2. Определение контракта данных
Затем необходимо создать контракт данных для службы, который будет описывать, как данные будут обмениваться между службой и ее клиентами. Классы, описанные в контракте данных, должны быть помечены атрибутом [DataContractAttribute] . Отдельные свойства или поля, которые должны отображаться для клиента и сервера, должны быть помечены атрибутом [DataMemberAttribute] . Если вы хотите, чтобы типы, производные от класса в контракте данных, были разрешены, необходимо определить их с атрибутом [KnownTypeAttribute] . WCF будет сериализовать или десериализовать типы, включенные в интерфейс службы, и типы, определенные как известные. Если вы пытаетесь использовать тип, который не является известным типом, возникнет исключение.
Дополнительные сведения о контрактах данных см. в разделе "Контракты данных".
[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;
}
Шаг 3. Реализация службы WCF
Затем необходимо реализовать класс службы WCF, который реализует интерфейс, определенный на предыдущем шаге.
public class CustomerService: ICustomerManager
{
public void StoreCustomer(Customer customer)
{
// write to a database
}
public Customer GetCustomer(string firstName, string lastName)
{
// read from a database
}
}
Шаг 4. Настройка службы и клиента
Чтобы запустить службу WCF, необходимо объявить конечную точку, которая предоставляет этот интерфейс службы по определенному URL-адресу с помощью определенной привязки WCF. Привязка задает сведения о транспорте, кодировке и протоколе для клиентов и сервера для обмена данными. Обычно в файл конфигурации проекта службы добавляются привязки (web.config). Ниже представлена запись привязки для службы-примера:
<configuration>
<system.serviceModel>
<services>
<service name="Server.CustomerService">
<endpoint address="http://localhost:8083/CustomerManager"
binding="basicHttpBinding"
contract="Shared.ICustomerManager" />
</service>
</services>
</system.serviceModel>
</configuration>
Затем необходимо настроить клиент для сопоставления сведений о привязке, указанных службой. Для этого добавьте следующий код в файл конфигурации приложения клиента (app.config).
<configuration>
<system.serviceModel>
<client>
<endpoint name="customermanager"
address="http://localhost:8083/CustomerManager"
binding="basicHttpBinding"
contract="Shared.ICustomerManager"/>
</client>
</system.serviceModel>
</configuration>
Шаг 5. Запуск службы
В завершение, вы можете самостоятельно разместить этот сервис в консольном приложении, добавив следующие строки в приложение-службу, а затем запустив его. Для получения более подробной информации о других способах размещения приложений служб WCF см. Размещение служб.
ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService));
customerServiceHost.Open();
Шаг 6. Вызов службы от клиента
Чтобы вызвать службу с клиента, необходимо создать фабрику каналов для службы и запросить канал, который позволит напрямую вызывать метод GetCustomer из клиента. Канал реализует интерфейс службы и обрабатывает базовую логику запроса и ответа. Возвращаемое значение из этого вызова метода — десериализированная копия ответа службы.
ChannelFactory<ICustomerManager> factory =
new ChannelFactory<ICustomerManager>("customermanager");
ICustomerManager service = factory.CreateChannel();
Customer customer = service.GetCustomer("Mary", "Smith");
Клиент отправляет объект по значению на сервер.
В этом сценарии клиент отправляет объект серверу по значению. Это означает, что сервер получит десериализированную копию объекта. Сервер может вызывать методы для этой копии и гарантировать отсутствие обратного вызова в клиентский код. Как упоминалось ранее, обычные обмены данными WCF осуществляются по значению. Это гарантирует, что вызов методов для одного из этих объектов выполняется только локально. Он не будет вызывать код на клиенте.
Этот сценарий представляет следующий вызов метода COM:
public interface IRemoteService
{
void SendObjectByValue(Customer customer);
}
В этом сценарии используется тот же интерфейс службы и контракт данных, что и в первом примере. Кроме того, клиент и служба будут настроены таким же образом. В этом примере канал создается для отправки объекта и выполнения таким же образом. Однако в этом примере вы создадите клиент, который вызывает сервис, передав объект по значению. Метод службы, который клиент будет вызывать в контракте службы, отображается полужирным шрифтом:
[ServiceContract]
public interface ICustomerManager
{
[OperationContract] void StoreCustomer(Customer customer);
[OperationContract]
Customer GetCustomer(string firstName, string lastName);
}
Добавьте код клиенту, который отправляет объект по значению
В следующем коде показано, как клиент создает новый объект клиента по значению, создает канал для взаимодействия со ICustomerManager службой и отправляет в него объект клиента.
Объект клиента будет сериализован и отправлен в службу, где она десериализирована службой в новую копию этого объекта. Все методы, вызывающие службу этого объекта, будут выполняться только локально на сервере. Важно отметить, что этот код иллюстрирует отправку производного типа (PremiumCustomer). Контракт обслуживания ожидает объект Customer, но контракт на данные обслуживания использует атрибут [KnownTypeAttribute] для указания, что PremiumCustomer также допускается. WCF не сможет выполнить попытки сериализации или десериализации любого другого типа через этот интерфейс службы.
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);
Сервис возвращает объект по ссылке
В этом сценарии клиентское приложение вызывает удаленную службу, а метод возвращает объект, который передается по ссылке из службы клиенту.
Как упоминалось ранее, службы WCF всегда возвращают объект по значению. Однако вы можете добиться аналогичного результата, используя класс EndpointAddress10. Сериализуемый по значению объект EndpointAddress10 может использоваться клиентом для получения сеансового объекта по ссылке на сервере.
Поведение объекта по ссылке в WCF, показанное в этом сценарии, отличается от поведения в DCOM. В DCOM сервер может напрямую возвращать объект по ссылке клиенту, а клиент может вызывать методы этого объекта, которые выполняются на сервере. Однако в WCF возвращаемый объект всегда передаётся по значению. Клиент должен принять этот объект по значению, представленный EndpointAddress10 и использовать его для создания собственного сеансового объекта по ссылке. Клиентский метод вызывает сеансовый объект, выполняемый на сервере. Другими словами, этот ссылочный объект в WCF — это обычная служба WCF, настроенная на сеансовое взаимодействие.
В WCF сеанс — это способ сопоставления нескольких сообщений, отправленных между двумя конечными точками. Это означает, что после подключения к этой службе между клиентом и сервером будет установлена сессия. Клиент будет использовать один уникальный экземпляр объекта на стороне сервера для всех его взаимодействий в рамках этого одного сеанса. Контракты сеансового WCF похожи на шаблоны сетевых запросов и ответов, ориентированных на подключение.
Этот сценарий представлен следующим методом DCOM.
public interface IRemoteService
{
IRemoteObject GetObjectByReference();
}
Шаг 1. Определите интерфейс и реализацию службы WCF с поддержкой сеансов
Сначала определите интерфейс службы WCF, содержащий объект с поддержкой сессий.
В этом коде сеансовый объект помечается атрибутом ServiceContract, который идентифицирует его как обычный интерфейс WCF. Кроме того, свойство SessionMode установлено на указание, что это будет служба сессий.
[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface ISessionBoundObject
{
[OperationContract]
string GetCurrentValue();
[OperationContract]
void SetCurrentValue(string value);
}
В следующем коде показана реализация службы.
Служба помечена атрибутом [ServiceBehavior], а свойство InstanceContextMode имеет значение InstanceContextMode.PerSessions, чтобы указать, что для каждого сеанса необходимо создать уникальный экземпляр этого типа.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class MySessionBoundObject : ISessionBoundObject
{
private string _value;
public string GetCurrentValue()
{
return _value;
}
public void SetCurrentValue(string val)
{
_value = val;
}
}
Шаг 2. Определение службы фабричного WCF для сеансового объекта
Служба, создающая сессионный объект, должна быть определена и реализована. В следующем коде показано, как это сделать. Этот код создает другую EndpointAddress10 службу WCF, которая возвращает объект. Это сериализованная форма конечной точки, которая может быть использована для создания сессионного объекта.
[ServiceContract]
public interface ISessionBoundFactory
{
[OperationContract]
EndpointAddress10 GetInstanceAddress();
}
Ниже приведена реализация этой службы. Эта реализация поддерживает фабрику однотонных каналов для создания сеансовых объектов. При GetInstanceAddress вызове он создает канал и создает объект, указывающий EndpointAddress10 на удаленный адрес, связанный с этим каналом.
EndpointAddress10 — это тип данных, который можно вернуть клиенту в виде значения.
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);
}
}
Шаг 3. Настройка и запуск служб WCF
Чтобы разместить эти службы, необходимо внести следующие дополнения в файл конфигурации сервера (web.config).
Добавьте раздел
<client>, который описывает конечную точку для объекта с поддержкой сеансов. В этом сценарии сервер также выступает в качестве клиента и должен быть настроен для включения этого.В разделе
<services>объявите конечные точки службы для фабрики и объект, поддерживающий сеансы. Это позволяет клиенту взаимодействовать с конечными точками службы, получать EndpointAddress10 и создавать сеансовый канал.
Ниже приведен пример файла конфигурации с этими параметрами:
<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>
Добавьте следующие строки в консольное приложение, чтобы самостоятельно разместить службу и запустить приложение.
ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));
factoryHost.Open();
ServiceHost sessionBoundServiceHost = new ServiceHost(
typeof(MySessionBoundObject));
sessionBoundServiceHost.Open();
Шаг 4. Настройка клиента и вызов службы
Настройте клиент для взаимодействия со службами WCF, выполнив следующие записи в файле конфигурации приложения проекта (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>
Чтобы вызвать службу, добавьте код клиенту, чтобы выполнить следующие действия:
Создайте канал к
ISessionBoundFactoryслужбе.Используйте канал для вызова
ISessionBoundFactoryслужбы и получения EndpointAddress10 объекта.Используйте EndpointAddress10 для создания канала, чтобы получить сеансовый объект.
Вызовите методы
SetCurrentValueиGetCurrentValue, чтобы продемонстрировать, что в нескольких вызовах используется один и тот же экземпляр объекта.
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");
}