Перенос из .NET на платформу WCFMigrating from .NET Remoting to WCF

В этой статье описан процесс переноса приложения с переходом от использования удаленного взаимодействия .NET к использованию Windows Communication Foundation (WCF).This article describes how to migrate an application that uses .NET Remoting to use Windows Communication Foundation (WCF). В ней сравниваются сходные принципы работы с этими продуктами и описывается выполнение некоторых наиболее распространенных сценариев удаленного взаимодействия в WCF.It compares similar concepts between these products and then describes how to accomplish several common Remoting scenarios in WCF.

Удаленное взаимодействие .NET — это устаревший продукт, который поддерживается только в целях обеспечения обратной совместимости..NET Remoting is a legacy product that is supported only for backward compatibility. Использование этого продукта в средах со смешанными уровнями доверия не является безопасным, поскольку он не может поддерживать отдельные уровни доверия для клиента и сервера.It is not secure across mixed-trust environments because it cannot maintain the separate trust levels between client and server. Например, ни при каких обстоятельствах не следует предоставлять конечную точку удаленного взаимодействия .NET интернет-клиентам или недоверенным клиентам.For example, you should never expose a .NET Remoting endpoint to the Internet or to untrusted clients. Рекомендуется перенести существующие приложения удаленного взаимодействия на базу более новых и безопасных технологий.We recommend existing Remoting applications be migrated to newer and more secure technologies. Если в структуре приложения используется только протокол HTTP и архитектура RESTful, рекомендуется воспользоваться веб-API ASP.NET.If the application’s design uses only HTTP and is RESTful, we recommend ASP.NET Web API. Дополнительные сведения см. в статье, посвященной веб-API ASP.NET.For more information, see ASP.NET Web API. Если приложение основано на SOAP или требует использования протоколов, отличных от HTTP, например TCP, рекомендуется использовать WCF.If the application is based on SOAP or requires non-Http protocols such as TCP, we recommend WCF.

Сравнение удаленного взаимодействия .NET и WCFComparing .NET Remoting to WCF

В этом разделе сравниваются основные структурные элементы системы удаленного взаимодействия .NET с аналогичными элементами WCF.This section compares the basic building blocks of .NET Remoting with their WCF equivalents. Мы будем использовать эти структурные элементы в дальнейшем для создания некоторых наиболее распространенных сценариев взаимодействия «клиент — сервер» в среде WCF. На следующей схеме показаны основные сходства и различия между системой удаленного взаимодействия .NET и WCF.We will use these building blocks later to create some common client-server scenarios in WCF.The following chart summarizes the main similarities and differences between .NET Remoting and WCF.

Удаленное взаимодействие .NET.NET Remoting WCFWCF
Тип сервераServer type Подкласс MarshalByRefObjectSubclass MarshalByRefObject Отметка атрибутом [ServiceContract]Mark with [ServiceContract] attribute
Операции службService operations Открытые методы для типа сервераPublic methods on server type Отметка атрибутом [OperationContract]Mark with [OperationContract] attribute
СериализацияSerialization ISerializable или [Serializable]ISerializable or [Serializable] DataContractSerializer или XmlSerializerDataContractSerializer or XmlSerializer
Передаваемые объектыObjects passed По значению или по ссылкеBy-value or by-reference Только по значениюBy-value only
Ошибки и исключенияErrors/exceptions Любые сериализуемые исключенияAny serializable exception FaultContract<TDetail >FaultContract<TDetail>
Прокси-объекты клиентаClient proxy objects Строго типизированные прозрачные прокси-объекты создаются автоматически из MarshalByRefObjects.Strongly typed transparent proxies are created automatically from MarshalByRefObjects Строго типизированные прокси-объекты создаются по требованию с помощью ChannelFactory<TChannel >Strongly typed proxies are generated on-demand using ChannelFactory<TChannel>
Требуемая платформаPlatform required Клиент и сервер должны использовать ОС Microsoft и .NET.Both client and server must use Microsoft OS and .NET КроссплатформенныйCross-platform
Формат сообщенийMessage format PrivatePrivate Отраслевые стандарты (SOAP, WS-* и т. п.)Industry standards (SOAP, WS-*, etc.)

Сравнение реализации сервераServer Implementation Comparison

Создание сервера в системе удаленного взаимодействия .NETCreating a Server in .NET Remoting

Типы сервера удаленного взаимодействия .NET должны производиться из подкласса MarshalByRefObject и определять методы, которые может вызывать клиент, следующим образом..NET Remoting server types must derive from MarshalByRefObject and define methods the client can call, like the following:

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

Открытые методы этого типа сервера становятся открытым контрактом, доступным для клиентов.The public methods of this server type become the public contract available to clients. Открытый интерфейс сервера и его реализация не разделяются и обрабатываются одним типом.There is no separation between the server’s public interface and its implementation – one type handles both.

После определения типа сервера его можно сделать доступным для клиентов, как показано в следующем примере:Once the server type has been defined, it can be made available to clients, like in the following example:

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();  

Существуют различные способы предоставления доступа к типу удаленного взаимодействия как к серверу, включая использование файлов конфигурации.There are many ways to make the Remoting type available as a server, including using configuration files. Это лишь один пример.This is just one example.

Создание сервера в WCFCreating a Server in WCF

Эквивалентное действие в WCF включает создание двух типов: открытого «контракта службы» и конкретной реализации.The equivalent step in WCF involves creating two types -- the public "service contract" and the concrete implementation. Первый тип объявляется как интерфейс, отмеченный атрибутом [ServiceContract].The first is declared as an interface marked with [ServiceContract]. Доступные клиентам методы отмечаются атрибутом [OperationContract]:Methods available to clients are marked with [OperationContract]:

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

Реализация сервера определяется в отдельном конкретном классе, как показано в следующем примере:The server’s implementation is defined in a separate concrete class, like in the following example:

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

После определения этих типов клиентам можно предоставить доступ к серверу WCF, как показано в следующем примере:Once these types have been defined, the WCF server can be made available to clients, like in the following example:

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();  
}  

Примечание

В обоих примерах используется TCP для обеспечения максимального сходства.TCP is used in both examples to keep them as similar as possible. См. пошаговые руководства по сценариям, приведенные далее в этой статье, чтобы ознакомиться с примерами с использованием HTTP.Refer to the scenario walk-throughs later in this topic for examples using HTTP.

Существуют различные способы настройки и размещения служб WCF.There are many ways to configure and to host WCF services. Это лишь один пример размещения, называемый «резидентным размещением».This is just one example, known as "self-hosted". Дополнительные сведения см. в следующих разделах:For more information, see the following topics:

Сравнение реализации клиентаClient Implementation Comparison

Создание клиента в системе удаленного взаимодействия .NETCreating a Client in .NET Remoting

После создания доступного объекта-сервера удаленного взаимодействия .NET он может использоваться клиентами, как показано в следующем примере:Once a .NET Remoting server object has been made available, it can be consumed by clients, like in the following example:

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.");

Экземпляр RemotingServer, возвращаемый Activator.GetObject(), называется «прозрачным прокси-сервером».The RemotingServer instance returned from Activator.GetObject() is known as a "transparent proxy." Он реализует открытый API для типа RemotingServer на стороне клиента, но все методы вызова объекта-сервера выполняются в рамках другого процесса или на другом компьютере.It implements the public API for the RemotingServer type on the client, but all the methods call the server object running in a different process or machine.

Создание клиента в WCFCreating a Client in WCF

Аналогичное действие в WCF предполагает использование фабрики каналов для создания прокси-объектов явным образом.The equivalent step in WCF involves using a channel factory to create the proxy explicitly. Как и в системе удаленного взаимодействия, прокси-объект можно использовать для вызова операций на сервере, как показано в следующем примере:Like Remoting, the proxy object can be used to invoke operations on the server, like in the following example:

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.");

Это пример программирования на уровне канала, так как он обладает максимальным сходством с примером удаленного взаимодействия.This example shows programming at the channel level because it is most similar to the Remoting example. Также доступно в Add Service Reference подход в Visual Studio, который создает код для упрощения программирования клиента.Also available is the Add Service Reference approach in Visual Studio that generates code to simplify client programming. Дополнительные сведения см. в следующих разделах:For more information, see the following topics:

Использование сериализацииSerialization Usage

Удаленное взаимодействие .NET и WCF используют сериализацию для передачи объектов между клиентом и сервером, но они отличаются в следующих важных аспектах.Both .NET Remoting and WCF use serialization to send objects between client and server, but they differ in these important ways:

  1. Они используют разные сериализаторы и соглашения для указания сериализуемых объектов.They use different serializers and conventions to indicate what to serialize.

  2. Система удаленного взаимодействия .NET поддерживает сериализацию «по ссылке» и разрешает доступ к методу или свойству на одном уровне для выполнения кода на другом уровне, что нарушает границы безопасности..NET Remoting supports "by reference" serialization that allows method or property access on one tier to execute code on the other tier, which is across security boundaries. Эта возможность создает уязвимости системы безопасности и является одной из основных причин, по которым клиентам, не обладающим должным уровнем доверия, не следует предоставлять доступ к конечным точкам удаленного взаимодействия.This capability exposes security vulnerabilities and is one of the main reasons why Remoting endpoints should never be exposed to untrusted clients.

  3. Сериализация в системе удаленного взаимодействия строится на принципе явного отказа (с явным исключением объектов, не подлежащих сериализации), а сериализация в WCF — на принципе явного согласия (с явным указанием объектов для сериализации).Serialization used by Remoting is opt-out (explicitly exclude what not to serialize) and WCF serialization is opt-in (explicitly mark which members to serialize).

Сериализация в среде удаленного взаимодействия .NETSerialization in .NET Remoting

Система удаленного взаимодействия .NET поддерживает два способа сериализации и десериализации объектов между клиентом и сервером..NET Remoting supports two ways to serialize and deserialize objects between the client and server:

  • По значению — значения объекта сериализуются вне границ уровня, и на другом уровне создается новый экземпляр этого объекта.By value – the values of the object are serialized across tier boundaries, and a new instance of that object is created on the other tier. Любые вызовы методов и свойств этого нового экземпляра выполняются только на локальном компьютере и не влияют на исходный объект или уровень.Any calls to methods or properties of that new instance execute only locally and do not affect the original object or tier.

  • По ссылке — вне границ уровня сериализуется специальная «ссылка на объект».By reference – a special "object reference" is serialized across tier boundaries. Когда один уровень взаимодействует с методами или свойствами данного объекта, он устанавливает обратную связь с исходным объектом на исходном уровне.When one tier interacts with methods or properties of that object, it communicates back to the original object on the original tier. Объекты по ссылке могут передаваться в любом направлении — с сервера на клиент или с клиента на сервер.By-reference objects can flow in either direction – server to client, or client to server.

Типы по значению в системе удаленного взаимодействия обозначены атрибутом [Serializable] или реализуют интерфейс ISerializable, как показано в следующем примере:By-value types in Remoting are marked with the [Serializable] attribute or implement ISerializable, like in the following example:

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

Типы по ссылке являются производными от класса MarshalByRefObject, как показано в следующем примере:By-reference types derive from the MarshalByRefObject class, like in the following example:

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

Очень важно понимать последствия использования объектов по ссылке в системе удаленного взаимодействия.It is extremely important to understand the implications of Remoting’s by-reference objects. Если какой-либо уровень (клиент или сервер) отправляет объект по ссылке на другой уровень, все вызовы методов выполняются на уровне, к которому относится объект.If either tier (client or server) sends a by-reference object to the other tier, all method calls execute back on the tier owning the object. Например методы вызова клиента для объекта по ссылке, возвращаемого сервером, выполняют код на сервере.For example, a client calling methods on a by-reference object returned by the server will execute code on the server. Аналогичным образом, методы вызова сервера объекта по ссылке, предоставляемого клиентом, выполняют код на клиенте.Similarly, a server calling methods on a by-reference object provided by the client will execute code back on the client. По этой причине использование системы удаленного взаимодействия .NET рекомендуется только в средах с полным доверием.For this reason, the use of .NET Remoting is recommended only within fully-trusted environments. Предоставление открытой конечной точки удаленного взаимодействия .NET ненадежным клиентам делает сервер удаленного взаимодействия уязвимым для атак.Exposing a public .NET Remoting endpoint to untrusted clients will make a Remoting server vulnerable to attack.

Сериализация в WCFSerialization in WCF

WCF поддерживает только сериализацию по значению.WCF supports only by-value serialization. Наиболее распространенный способ определения типа для обмена между клиентом и сервером показан в следующем примере:The most common way to define a type to exchange between client and server is like in the following example:

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

Атрибут [DataContract] определяет этот тип как тип, доступный для сериализации и десериализации между клиентом и сервером.The [DataContract] attribute identifies this type as one that can be serialized and deserialized between client and server. Атрибут [DataMember] определяет отдельные свойства или поля для сериализации.The [DataMember] attribute identifies the individual properties or fields to serialize.

Когда WCF отправляет объект между уровнями, он сериализует только значения и создает новый экземпляр объекта на другом уровне.When WCF sends an object across tiers, it serializes only the values and creates a new instance of the object on the other tier. Любое взаимодействие со значениями объекта осуществляется только на локальном уровне — они не взаимодействуют с другим уровнем в отличие от объектов по ссылке системы удаленного взаимодействия .NET.Any interactions with the values of the object occur only locally – they do not communicate with the other tier the way .NET Remoting by-reference objects do. Дополнительные сведения см. в разделе сериализации и десериализации.For more information, see Serialization and Deserialization.

Возможности обработки исключенийException Handling Capabilities

Исключения в системе удаленного взаимодействия .NETExceptions in .NET Remoting

Исключения, создаваемые удаленным сервером, сериализуются, отправляются на клиент и возникают локально на стороне клиента так же, как и любые другие исключения.Exceptions thrown by a Remoting server are serialized, sent to the client, and thrown locally on the client like any other exception. Можно создать пользовательские исключения путем формирования подкласса типа «Исключения» и отметки его атрибутом [Serializable].Custom exceptions can be created by sub-classing the Exception type and marking it with [Serializable]. Большинство исключений платформы уже отмечены таким образом, что обеспечивает их создание сервером, сериализацию и повторное создание на клиенте.Most framework exceptions are already marked in this way, allowing most to be thrown by the server, serialized, and re-thrown on the client. Хотя такой подход удобно применять во время разработки, сведения, хранящиеся на стороне сервера, могут быть случайно раскрыты клиенту.Though this design is convenient during development, server-side information can inadvertently be disclosed to the client. Это одна из многих причин, по которым систему удаленного взаимодействия следует использовать только в средах с полным уровнем доверия.This is one of many reasons Remoting should be used only in fully-trusted environments.

Исключения и сбои в WCFExceptions and Faults in WCF

WCF не допускает возврата произвольных типов исключений с сервера на клиент, поскольку это может привести к непреднамеренному разглашению сведений.WCF does not allow arbitrary exception types to be returned from the server to the client because it could lead to inadvertent information disclosure. Если операция службы вызывает непредвиденное исключение, это приводит к созданию общего исключения FaultException на стороне клиента.If a service operation throws an unexpected exception, it causes a general purpose FaultException to be thrown on the client. Это исключение не несет никакой информации о том, почему или где возникла проблема, и для некоторых приложений этого достаточно.This exception does not carry any information why or where the problem occurred, and for some applications this is sufficient. Если приложениям требуется передача на клиент более полных сведений об ошибке, это осуществляется путем определения контракта сбоя.Applications that need to communicate richer error information to the client do this by defining a fault contract.

Для этого сначала создайте тип [DataContract], содержащий сведения о сбое.To do this, first create a [DataContract] type to carry the fault information.

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

Укажите контракт сбоя для каждой операции службы.Specify the fault contract to use for each service operation.

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

Сервер сообщает о состоянии сбоя путем создания исключения FaultException.The server reports error conditions by throwing a FaultException.

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

И каждый раз, когда клиент отправляет запрос на сервер, он может перехватить сбои как обычное исключение.And whenever the client makes a request to the server, it can catch faults as normal exceptions.

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

Дополнительные сведения о контрактах сбоев см. в описании класса FaultException.For more information about fault contracts, see FaultException.

Вопросы безопасностиSecurity Considerations

Безопасность в системе удаленного взаимодействия .NETSecurity in .NET Remoting

Некоторые каналы удаленного взаимодействия .NET поддерживают различные функции обеспечения безопасности, такие как проверка подлинности и шифрование на уровне канала (IPC и TCP).Some .NET Remoting channels support security features such as authentication and encryption at the channel layer (IPC and TCP). Для канала HTTP проверка подлинности и шифрование строятся на основе служб Internet Information Services (IIS).The HTTP channel relies on Internet Information Services (IIS) for both authentication and encryption. Несмотря на это, следует рассматривать систему удаленного взаимодействия .NET как небезопасный протокол связи, который следует использовать только в среде с полным уровнем доверия.Despite this support, you should consider .NET Remoting an unsecure communication protocol and use it only within fully-trusted environments. Никогда не предоставляйте доступ к открытой конечной точке удаленного взаимодействия интернет-клиентам или клиентам без должного уровня доверия.Never expose a public Remoting endpoint to the Internet or untrusted clients.

Безопасность в WCFSecurity in WCF

Система WCF была разработана с учетом аспектов безопасности, в частности для устранения ряда уязвимостей, присущих среде удаленного взаимодействия .NET.WCF was designed with security in mind, in part to address the kinds of vulnerabilities found in .NET Remoting. WCF обеспечивает безопасность на уровне транспорта и сообщений и предоставляет множество функций для проверки подлинности, авторизации, шифрования и т. д.WCF offers security at both the transport and message level, and offers many options for authentication, authorization, encryption, and so on. Дополнительные сведения см. в следующих разделах:For more information, see the following topics:

Миграция в WCFMigrating to WCF

Почему необходимо выполнить миграцию с переходом от удаленного взаимодействия на WCF?Why Migrate from Remoting to WCF?

  • Удаленное взаимодействие .NET — это устаревший продукт..NET Remoting is a legacy product. Как описано в разделе удаленного взаимодействия .NET, он считается это устаревший продукт и не рекомендуется для разработки новых приложений.As described in .NET Remoting, it is considered a legacy product and is not recommended for new development. Для новых и существующих приложений рекомендуется использовать платформу WCF или веб-интерфейс API ASP.NET.WCF or ASP.NET Web API are recommended for new and existing applications.

  • WCF использует кроссплатформенные стандарты.WCF uses cross-platform standards. Решение WCF было разработано с учетом кроссплатформенного взаимодействия и поддерживает различные отраслевые стандарты (SOAP, WS-Security, WS-Trust, и т. д.).WCF was designed with cross-platform interoperability in mind and supports many industry standards (SOAP, WS-Security, WS-Trust, etc.). Служба WCF может взаимодействовать с клиентами, работающими под управлением других операционных систем, отличных от Windows.A WCF service can interoperate with clients running on operating systems other than Windows. Решение удаленного взаимодействия было предназначено в первую очередь для сред, где клиентские и серверные приложения выполнялись с использованием платформы .NET в операционной системе Windows.Remoting was designed primarily for environments where both the server and client applications run using the .NET framework on a Windows operating system.

  • WCF имеет встроенных средств безопасности.WCF has built-in security. Решение WCF было разработано с учетом требований безопасности и предоставляет различные функции проверки подлинности, уровня безопасности транспорта, безопасности на уровне сообщений и т. д. Решение удаленного взаимодействия было разработано для упрощения взаимодействия приложений, но не предназначено для обеспечения безопасности в средах, не заслуживающих доверия.WCF was designed with security in mind and offers many options for authentication, transport level security, message level security, etc. Remoting was designed to make it easy for applications to interoperate but was not designed to be secure in non-trusted environments. Решение WCF было разработано для работы как в доверенных, так и в недоверенных средах.WCF was designed to work in both trusted and non-trusted environments.

Рекомендации по миграцииMigration Recommendations

Ниже приведены рекомендуемые действия по миграции от удаленного взаимодействия .NET к WCF.The following are the recommended steps to migrate from .NET Remoting to WCF:

  • Создайте контракт службы.Create the service contract. Определите типы интерфейсов службы и отметьте их атрибутом [ServiceContract]. Отметьте все методы, доступные для вызова клиентами, атрибутом [OperationContract].Define your service interface types, and mark them with the [ServiceContract] attribute.Mark all the methods the clients will be allowed to call with [OperationContract].

  • Создание контракта данных.Create the data contract. Определите типы данных, которыми будут обмениваться сервер и клиент, и отметьте их атрибутом [DataContract].Define the data types that will be exchanged between server and client, and mark them with the [DataContract] attribute. Отметьте все поля и свойства, доступные для использования клиентом, атрибутом [DataMember].Mark all the fields and properties the client will be allowed to use with [DataMember].

  • Создайте контракт ошибок (необязательно).Create the fault contract (optional). Создайте типы, которыми будут обмениваться сервер и клиент при возникновении ошибок.Create the types that will be exchanged between server and client when errors are encountered. Отметьте эти типы атрибутами [DataContract] и [DataMember], чтобы обеспечить возможность их сериализации.Mark these types with [DataContract] and [DataMember] to make them serializable. Для всех операций службы, отмеченных атрибутом [OperationContract], также задайте атрибут [FaultContract], чтобы указать, какие ошибки они могут возвращать.For all service operations you marked with [OperationContract], also mark them with [FaultContract] to indicate which errors they may return.

  • Настройте и разместите службу.Configure and host the service. После создания контракта службы необходимо настроить привязку для предоставления службы в конечной точке.Once the service contract has been created, the next step is to configure a binding to expose the service at an endpoint. Дополнительные сведения см. в разделе конечные точки: Адреса, привязки и контракты.For more information, see Endpoints: Addresses, Bindings, and Contracts.

После переноса приложения удаленного взаимодействия на платформу WCF необходимо удалить зависимости от решения удаленного взаимодействия .NET.Once a Remoting application has been migrated to WCF, it is still important to remove dependencies on .NET Remoting. Это позволит удалить какие-либо уязвимости удаленного взаимодействия из приложения.This ensures that any Remoting vulnerabilities are removed from the application. Вот эти шаги.These steps include the following:

  • Прекратите использование MarshalByRefObject.Discontinue use of MarshalByRefObject. Тип MarshalByRefObject существует только для решения удаленного взаимодействия и не используется в WCF.The MarshalByRefObject type exists only for Remoting and is not used by WCF. Все типы приложений с вложенным классом MarshalByRefObject должны быть удалены или изменены.Any application types that sub-class MarshalByRefObject should be removed or changed.

  • Прекратите использование [Serializable] и ISerializable.Discontinue use of [Serializable] and ISerializable. Атрибут [Serializable] и интерфейс ISerializable изначально были разработаны для сериализации типов в доверенных средах, и они используются при удаленном взаимодействии.The [Serializable] attribute and ISerializable interface were originally designed to serialize types within trusted environments, and they are used by Remoting. При сериализации WCF используются типы с атрибутом [DataContract] и [DataMember].WCF serialization relies on types being marked with [DataContract] and [DataMember]. Типы данных, используемых приложением, следует изменить таким образом, чтобы использовать [DataContract] и отказаться от использования ISerializable или [Serializable].Data types used by an application should be modified to use [DataContract] and not to use ISerializable or [Serializable].

Сценарии миграцииMigration Scenarios

Теперь давайте рассмотрим выполнение следующих распространенных сценариев удаленного взаимодействия в WCF.Now let’s see how to accomplish the following common Remoting scenarios in WCF:

  1. Сервер возвращает клиенту объект по значению.Server returns an object by-value to the client

  2. Сервер возвращает клиенту объект по ссылке.Server returns an object by-reference to the client

  3. Клиент отправляет серверу объект по значению.Client sends an object by-value to the server

Примечание

В среде WCF отправка клиентом серверу объекта по ссылке не разрешена.Sending an object by-reference from the client to the server is not allowed in WCF.

При просмотре сценариев предполагается, что базовые интерфейсы для удаленного взаимодействия .NET выглядят так, как показано в следующем примере.When reading through these scenarios, assume our baseline interfaces for .NET Remoting look like the following example. Реализация удаленного взаимодействия .NET в данном случае не имеет значения, поскольку мы хотим показать только способы использования WCF для реализации аналогичных функциональных возможностей.The .NET Remoting implementation is not important here because we want to illustrate only how to use WCF to implement equivalent functionality.

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) {…}  
}  

Сценарий 1. Служба возвращает объект по значениюScenario 1: Service Returns an Object by Value

В этом сценарии показана ситуация, когда сервер возвращает объект клиенту по ссылке.This scenario demonstrates a server returning an object to the client by value. WCF всегда возвращает объекты с сервера по значению, поэтому ниже просто приведено описание создания стандартной службы WCF.WCF always returns objects from the server by value, so the following steps simply describe how to build a normal WCF service.

  1. Сначала определите открытый интерфейс для службы WCF и пометьте его атрибутом [ServiceContract].Start by defining a public interface for the WCF service and mark it with the [ServiceContract] attribute. Мы используем [OperationContract] для определения методов на стороне сервера, которые будет вызывать клиент.We use [OperationContract] to identify the server-side methods our client will call.

    [ServiceContract]  
    public interface ICustomerService  
    {  
        [OperationContract]  
        Customer GetCustomer(int customerId);  
    
        [OperationContract]  
        bool UpdateCustomer(Customer customer);  
    }  
    
  2. Следующим шагом является создание контракта данных для этой службы.The next step is to create the data contract for this service. Это осуществляется путем создания классов (а не интерфейсов), отмеченных атрибутом [DataContract].We do this by creating classes (not interfaces) marked with the [DataContract] attribute. Отдельные свойства или поля, которые должны быть видны и для клиента, и для сервера, отмечаются атрибутом [DataMember].The individual properties or fields we want visible to both client and server are marked with [DataMember]. Если следует разрешить производные типы, необходимо использовать атрибут [KnownType] для их идентификации.If we want derived types to be allowed, we must use the [KnownType] attribute to identify them. Единственными типами, сериализацию или десериализацию которых для этой службы разрешает WCF, являются типы в интерфейсе службы и эти «известные типы».The only types WCF will allow to be serialized or deserialized for this service are those in the service interface and these "known types". Попытки обмена любыми другими типами, не указанными в списке, будут отклонены.Attempting to exchange any other type not in this list will be rejected.

    [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. Далее представлена реализация интерфейса службы.Next, we provide the implementation for the service interface.

    public class CustomerService : ICustomerService  
    {  
        public Customer GetCustomer(int customerId)  
        {  
            // read from database  
        }  
    
        public bool UpdateCustomer(Customer customer)  
        {  
            // write to database  
        }  
    }  
    
  4. Для запуска службы WCF необходимо объявить конечную точку, предоставляющую доступ к интерфейсу службы по определенному URL-адресу с помощью определенной привязки WCF.To run the WCF service, we need to declare an endpoint that exposes that service interface at a specific URL using a specific WCF binding. Как правило, это осуществляется путем добавления в файл web.config серверного проекта следующих разделов.This is typically done by adding the following sections to the server project’s web.config file.

    <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. После этого можно запустить службу WCF с помощью следующего кода:The WCF service can then be started with the following code:

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

    При запуске метод ServiceHost использует файл web.config для установления надлежащего контракта, привязки и конечной точки.When this ServiceHost is started, it uses the web.config file to establish the proper contract, binding and endpoint. Дополнительные сведения о файлах конфигурации см. в разделе Настройка служб с помощью файла конфигурации.For more information about configuration files, see Configuring Services Using Configuration Files. Этот метод запуска сервера называется резидентным размещением.This style of starting the server is known as self-hosting. Дополнительные сведения о других вариантах размещения службы WCF, см. в разделе размещение служб.To learn more about other choices for hosting WCF services, see Hosting Services.

  6. В файле app.config клиентского проекта должны быть объявлены соответствующие сведения о привязке для конечной точки службы.The client project’s app.config must declare matching binding information for the service’s endpoint. Самый простой способ сделать это в Visual Studio является использование Add Service Reference, которая автоматически обновит файл app.config.The easiest way to do this in Visual Studio is to use Add Service Reference, which will automatically update the app.config file. Эти же изменения также можно добавить вручную.Alternatively, these same changes can be added manually.

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

    Дополнительные сведения об использовании Add Service Reference, см. в разделе как: Добавление, обновление или удаление ссылки на службу.For more information about using Add Service Reference, see How to: Add, Update, or Remove a Service Reference.

  7. Теперь мы можем вызвать службу WCF из клиента.Now we can call the WCF service from the client. Для этого мы создаем фабрику каналов для этой службы, запрашиваем канал и напрямую вызываем требуемый метод по этому каналу.We do this by creating a channel factory for that service, asking it for a channel, and directly calling the method we want on that channel. Это возможно благодаря тому, что канал реализует интерфейс службы и обрабатывает базовую логику запроса/ответа.We can do this because the channel implements the service’s interface and handles the underlying request/reply logic for us. Возвращаемым значением при выполнении данного вызова метода является десериализованная копия ответа сервера.The return value from that method call is the deserialized copy of the server’s response.

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

Объекты, возвращаемые WCF с сервера на клиент, всегда передаются по значению.Objects returned by WCF from the server to the client are always by value. Объекты представляют собой десериализованные копии данных, отправленных сервером.The objects are deserialized copies of the data sent by the server. Клиент может вызывать методы в этих локальных копиях без опасности вызова серверного кода в ходе обратных вызовов.The client can call methods on these local copies without any danger of invoking server code through callbacks.

Сценарий 2. Сервер возвращает объект по ссылкеScenario 2: Server Returns an Object By Reference

В этом сценарии показана ситуация, когда сервер предоставляет объект клиенту по ссылке.This scenario demonstrates the server providing an object to the client by reference. В удаленном взаимодействии .NET эта операция обрабатывается автоматически для любого типа, производного от MarshalByRefObject, который сериализуется по ссылке.In .NET Remoting, this is handled automatically for any type derived from MarshalByRefObject, which is serialized by-reference. Примером такого сценария является разрешение нескольким клиентам иметь независимые объекты, связанные с сеансами, на стороне сервера.An example of this scenario is allowing multiple clients to have independent sessionful server-side objects. Как упоминалось ранее, объекты, возвращаемые службой WCF, всегда передаются по значению, поэтому прямой эквивалент объектов, передаваемых по ссылке, отсутствует, но существует возможность реализации метода, сходного с семантикой возвращения по ссылке, с использованием объекта EndpointAddress10.As previously mentioned, objects returned by a WCF service are always by value, so there is no direct equivalent of a by-reference object, but it is possible to achieve something similar to by-reference semantics using an EndpointAddress10 object. Это сериализуемый объект, передаваемый по значению, с помощью которого клиент может получить на сервере объект, переданный по ссылке в рамках сеанса.This is a serializable by-value object that can be used by the client to obtain a sessionful by-reference object on the server. Это обеспечивает возможность реализации сценария, при котором различные клиенты обладают независимыми объектами, связанными с сеансами, на стороне сервера.This enables the scenario of having multiple clients with independent sessionful server-side objects.

  1. Сначала необходимо определить контракт службы WCF, соответствующий самому объекту, связанному с сеансами.First, we need to define a WCF service contract that corresponds to the sessionful object itself.

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

    Совет

    Обратите внимание, что объект, связанный с сеансами, отмечен атрибутом [ServiceContract], что делает его обычным интерфейсом службы WCF.Notice that the sessionful object is marked with [ServiceContract], making it a normal WCF service interface. Настройка свойства SessionMode указывает на связь службы с сеансом.Setting the SessionMode property indicates it will be a sessionful service. В WCF сеанс — это способ согласования нескольких сообщений, пересылаемых между двумя конечными точками.In WCF, a session is a way of correlating multiple messages sent between two endpoints. Это означает, что, как только клиент устанавливает подключение к службе, между ним и сервером создается сеанс.This means that once a client obtains a connection to this service, a session will be established between the client and the server. Клиент использует единственный уникальный экземпляр объекта на стороне сервера для любых взаимодействий в рамках данного сеанса.The client will use a single unique instance of the server-side object for all its interactions within this single session.

  2. Далее необходимо представить реализацию этого интерфейса службы.Next, we need to provide the implementation of this service interface. Обозначение его при помощи атрибута [ServiceBehavior] и настройка InstanceContextMode позволяет сообщить WCF, что мы хотим использовать уникальный экземпляр этого типа для каждого сеанса.By denoting it with [ServiceBehavior] and setting the InstanceContextMode, we tell WCF we want to use a unique instance of this type for an each session.

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
        public class MySessionBoundObject : ISessionBoundObject  
        {  
            private string _value;  
    
            public string GetCurrentValue()  
            {  
                return _value;  
            }  
    
            public void SetCurrentValue(string val)  
            {  
                _value = val;  
            }  
    
        }  
    
  3. Теперь нам требуется способ получения экземпляра этого объекта, связанного с сеансом.Now we need a way to obtain an instance of this sessionful object. Для этого необходимо создать другой интерфейс службы WCF, который возвращает объект EndpointAddress10.We do this by creating another WCF service interface that returns an EndpointAddress10 object. Это сериализуемая форма конечной точки, с помощью которой клиент может создать объект сеанса.This is a serializable form of an endpoint that the client can use to create the sessionful object.

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

    И мы реализуем эту службу WCF:And we implement this WCF service:

    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);  
            }  
        }  
    

    Эта реализация поддерживает единственную фабрику каналов для создания объектов, связанных с сеансами.This implementation maintains a singleton channel factory to create sessionful objects. При вызове метода GetInstanceAddress() создается канал и объект EndpointAddress10, указывающий на удаленный адрес, связанный с этим каналом.When GetInstanceAddress() is called, it creates a channel and creates an EndpointAddress10 object that effectively points to the remote address associated with this channel. EndpointAddress10 — это просто тип данных, который можно вернуть клиенту по значению.EndpointAddress10 is simply a data type that can be returned to the client by-value.

  4. Нам нужно изменить файл конфигурации сервера, выполнив два действия, представленные в следующем примере:We need to modify the server’s configuration file by doing the following two things as shown in the example below:

    1. Объявите <клиента > раздел, описывающий конечную точку для объекта сеанса.Declare a <client> section that describes the endpoint for the sessionful object. Это необходимо, поскольку сервер также выступает в качестве клиента в данной ситуации.This is necessary because the server also acts as a client in this situation.

    2. Объявите конечные точки для фабрики и объекта, связанного с сеансом.Declare endpoints for the factory and sessionful object. Это необходимо, чтобы клиент мог взаимодействовать с конечными точками службы для получения EndpointAddress10 и создания канала сеанса.This is necessary to allow the client to communicate with the service endpoints to acquire the EndpointAddress10 and to create the sessionful channel.

    <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>  
    

    И затем мы можем запустить эти службы:And then we can start these services:

    ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
    factoryHost.Open();  
    
    ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject));  
    sessionHost.Open();  
    
  5. Мы настраиваем клиент путем объявления тех же конечных точек в файле app.config проекта.We configure the client by declaring these same endpoints in its project’s app.config file.

    <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. Чтобы создать и использовать этот объект, связанный с сеансом, клиент должен выполнить следующие действия.In order to create and use this sessionful object, the client must do the following steps:

    1. Создать канал для службы ISessionBoundFactory.Create a channel to the ISessionBoundFactory service.

    2. Использовать этот канал для вызова данной службы в целях получения EndpointAddress10.Use that channel to invoke that service to obtain an EndpointAddress10.

    3. Использовать EndpointAddress10 для создания канала в целях получения объекта, связанного с сеансом.Use the EndpointAddress10 to create a channel to obtain a sessionful object.

    4. Взаимодействовать с объектом, связанным с сеансом, чтобы продемонстрировать сохранение одного и того же экземпляра при нескольких вызовах.Interact with the sessionful object to demonstrate it remains the same instance across multiple calls.

    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 всегда возвращает объекты по значению, но существует возможность поддержки аналога семантики возвращения по ссылке посредством EndpointAddress10.WCF always returns objects by value, but it is possible to support the equivalent of by-reference semantics through the use of EndpointAddress10. Это позволяет клиенту запрашивать экземпляр службы WCF, связанный с сеансом, после чего он может взаимодействовать с ней так же, как и с любой другой службой WCF.This permits the client to request a sessionful WCF service instance, after which it can interact with it like any other WCF service.

Сценарий 3. Клиент отправляет серверу экземпляр по значениюScenario 3: Client Sends Server a By-Value Instance

В этом сценарии показан клиент, отправляющий серверу экземпляр объекта, не являющийся примитивом, по значению.This scenario demonstrates the client sending a non-primitive object instance to the server by value. Поскольку WCF отправляет объекты только по значению, в этом сценарии показано обычное использование WCF.Because WCF only sends objects by value, this scenario demonstrates normal WCF usage.

  1. Используйте ту же службу WCF, которая использовалась в сценарии 1.Use the same WCF Service from Scenario 1.

  2. Используйте клиент для создания нового объекта по значению (Customer), создайте канал для взаимодействия со службой ICustomerService и отправьте в нее объект.Use the client to create a new by-value object (Customer), create a channel to communicate with the ICustomerService service, and send the object to it.

    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}.");
    

    Объект Customer будет сериализован и отправлен на сервер, где он десериализуется в новую копию этого объекта.The customer object will be serialized, and sent to the server, where it is deserialized into a new copy of that object.

    Примечание

    Этот код также демонстрирует отправку производного типа (PremiumCustomer).This code also illustrates sending a derived type (PremiumCustomer). Интерфейс службы ожидает получения объекта Customer, но атрибут [KnownType] класса Customer указывает, что был также разрешен тип PremiumCustomer.The service interface expects a Customer object, but the [KnownType] attribute on the Customer class indicated PremiumCustomer was also allowed. Любая попытка сериализации или десериализации любого другого типа посредством этого интерфейса службы WCF завершится ошибкой.WCF will fail any attempt to serialize or deserialize any other type through this service interface.

Стандартные обмены данными в службе WCF осуществляются по значению.Normal WCF exchanges of data are by value. Это гарантирует, что методы вызова этих объектов данных реализуются только локально без вызова кода на другом уровне.This guarantees that invoking methods on one of these data objects executes only locally – it will not invoke code on the other tier. Хотя и существует возможность аналогичному возврату по ссылке объекты, возвращаемые из сервера, он не поддерживается для клиента передать объект по ссылке для сервера.While it is possible to achieve something like by-reference objects returned from the server, it is not possible for a client to pass a by-reference object to the server. Сценарий, при котором клиенту и серверу необходимо обмениваться сообщениями друг с другом, можно реализовать в WCF с помощью дуплексной службы.A scenario that requires a conversation back and forth between client and server can be achieved in WCF using a duplex service. Дополнительные сведения см. в разделе дуплексные службы.For more information, see Duplex Services.

СводкаSummary

Удаленное взаимодействие .NET — это платформа взаимодействия, предназначенная для использования только в средах с полным доверием..NET Remoting is a communication framework intended to be used only within fully-trusted environments. Это устаревший продукт, который поддерживается только в целях обеспечения обратной совместимости.It is a legacy product and supported only for backward compatibility. Он не должен использоваться для разработки новых приложений.It should not be used to build new applications. И напротив, служба WCF была разработана с учетом безопасности, поэтому ее рекомендуется использовать для поддержки новых и существующих приложений.Conversely, WCF was designed with security in mind and is recommended for new and existing applications. Корпорация Майкрософт рекомендует выполнить перенос существующих приложений удаленного взаимодействия с переходом к использованию WCF или веб-интерфейса API ASP.NET.Microsoft recommends that existing Remoting applications be migrated to use WCF or ASP.NET Web API instead.