Migrowanie z programu .NET Remoting do programu WCF
W tym artykule opisano sposób migrowania aplikacji korzystającej z komunikacji wirtualnej platformy .NET do korzystania z programu Windows Communication Foundation (WCF). Porównuje podobne pojęcia między tymi produktami, a następnie opisuje sposób realizacji kilku typowych scenariuszy komunikacji zdalniej w programie WCF.
Komunikacja zdalna platformy .NET to starszy produkt, który jest obsługiwany tylko w przypadku zgodności z poprzednimi wersjami. Nie jest ona bezpieczna w środowiskach zaufania mieszanego, ponieważ nie może utrzymywać oddzielnych poziomów zaufania między klientem a serwerem. Na przykład nigdy nie należy uwidaczniać punktu końcowego komunikacji wirtualnej platformy .NET z Internetem lub niezaufanych klientów. Zalecamy migrowanie istniejących aplikacji remoting do nowszych i bezpieczniejszych technologii. Jeśli projekt aplikacji używa tylko protokołu HTTP i ma wartość RESTful, zalecamy ASP.NET internetowy interfejs API. Aby uzyskać więcej informacji, zobacz ASP.NET internetowy interfejs API. Jeśli aplikacja jest oparta na protokole SOAP lub wymaga protokołów innych niż HTTP, takich jak TCP, zalecamy korzystanie z programu WCF.
Porównanie komunikacji wirtualnej platformy .NET z usługą WCF
W tej sekcji porównaliśmy podstawowe bloki konstrukcyjne komunikacji równorzędnej platformy .NET z ich odpowiednikami WCF. Użyjemy tych bloków konstrukcyjnych później do utworzenia niektórych typowych scenariuszy klient-serwer w programie WCF. Na poniższym wykresie podsumowano główne podobieństwa i różnice między komunikacji wirtualnej platformy .NET a usługą WCF.
Wywołaniem funkcji zdalnych .NET | WCF | |
---|---|---|
Typ serwera | Podklasy MarshalByRefObject |
Oznaczanie za pomocą [ServiceContract] atrybutu |
Operacje usługi | Metody publiczne w typie serwera | Oznaczanie za pomocą [OperationContract] atrybutu |
Serializacja | ISerializable lub [Serializable] |
DataContractSerializer lub XmlSerializer |
Przekazane obiekty | By-value lub by-reference | Tylko według wartości |
Błędy/wyjątki | Każdy wyjątek z możliwością serializacji | FaultContract<TDetail> |
Obiekty serwera proxy klienta | Silnie typizowane przezroczyste serwery proxy są tworzone automatycznie na podstawie obiektów MarshalByRefObjects | Silnie typizowane serwery proxy są generowane na żądanie przy użyciu kanału ChannelFactory<TChannel> |
Wymagana platforma | Zarówno klient, jak i serwer muszą używać systemu operacyjnego Microsoft i platformy .NET | Między platformami |
Format wiadomości | Prywatne | Standardy branżowe (na przykład SOAP i WS-*) |
Porównanie implementacji serwera
Tworzenie serwera na platformie .NET — komunikacja zdalna
Typy serwerów komunikacji wirtualnej platformy .NET muszą pochodzić z klasy MarshalByRefObject i definiować metody, które klient może wywołać, tak jak poniżej:
public class RemotingServer : MarshalByRefObject
{
public Customer GetCustomer(int customerId) { … }
}
Publiczne metody tego typu serwera stają się umową publiczną dostępną dla klientów. Nie ma separacji między interfejsem publicznym serwera a jego implementacją — jeden typ obsługuje oba te elementy.
Po zdefiniowaniu typu serwera można udostępnić go klientom, tak jak w poniższym przykładzie:
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();
Istnieje wiele sposobów udostępniania typu komunikacji wirtualnej jako serwera, w tym przy użyciu plików konfiguracji. To tylko jeden przykład.
Tworzenie serwera w programie WCF
Równoważny krok w programie WCF obejmuje utworzenie dwóch typów — publicznego "kontraktu usług" i konkretnego wdrożenia. Pierwszy jest zadeklarowany jako interfejs oznaczony jako [ServiceContract]. Metody dostępne dla klientów są oznaczone elementem [OperationContract]:
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
Customer GetCustomer(int customerId);
}
Implementacja serwera jest definiowana w oddzielnej klasie betonowej, na przykład w poniższym przykładzie:
public class WCFServer : IWCFServer
{
public Customer GetCustomer(int customerId) { … }
}
Po zdefiniowaniu tych typów serwer WCF można udostępnić klientom, jak w poniższym przykładzie:
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();
}
Uwaga
Protokół TCP jest używany w obu przykładach, aby zachować je tak samo, jak to możliwe. Zapoznaj się z przewodnikami scenariusza w dalszej części tego tematu, aby zapoznać się z przykładami korzystającymi z protokołu HTTP.
Istnieje wiele sposobów konfigurowania i hostowania usług WCF. Jest to tylko jeden przykład znany jako "self-hosted". Aby uzyskać więcej informacji, zobacz następujące tematy:
Porównanie implementacji klienta
Tworzenie klienta na potrzeby komunikacji wirtualnej platformy .NET
Po udostępnieniu obiektu serwera remoting platformy .NET można go używać przez klientów, podobnie jak w poniższym przykładzie:
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.");
Wystąpienie RemotingServer zwrócone z pliku Activator.GetObject() jest nazywane "przezroczystym serwerem proxy". Implementuje publiczny interfejs API dla typu RemotingServer na kliencie, ale wszystkie metody wywołają obiekt serwera uruchomiony w innym procesie lub maszynie.
Tworzenie klienta w programie WCF
Równoważny krok w programie WCF obejmuje użycie fabryki kanałów do jawnego utworzenia serwera proxy. Podobnie jak komunikacja zdalna, obiekt serwera proxy może służyć do wywoływania operacji na serwerze, jak w poniższym przykładzie:
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.");
W tym przykładzie pokazano programowanie na poziomie kanału, ponieważ jest ono najbardziej podobne do przykładu komunikacji z komunikacją zdalną. Dostępne jest również podejście Add Service Reference (Dodawanie odwołania do usługi) w programie Visual Studio, które generuje kod w celu uproszczenia programowania klientów. Aby uzyskać więcej informacji, zobacz następujące tematy:
Użycie serializacji
Komunikacja zdalna platformy .NET i program WCF używają serializacji do wysyłania obiektów między klientem a serwerem, ale różnią się one następującymi ważnymi sposobami:
Używają różnych serializatorów i konwencji, aby wskazać, co należy serializować.
Komunikacja zdalna platformy .NET obsługuje serializacji "według odwołania", która umożliwia dostęp do metody lub właściwości w jednej warstwie w celu wykonania kodu w drugiej warstwie, która znajduje się w granicach zabezpieczeń. Ta funkcja uwidacznia luki w zabezpieczeniach i jest jedną z głównych przyczyn, dla których punkty końcowe komunikacji wirtualnej nigdy nie powinny być narażone na niezaufanych klientów.
Serializacja używana przez funkcję remotingu to rezygnacja (jawnie wykluczanie elementów, które nie należy serializować), a serializacja WCF jest opt-in (jawnie oznacz, które elementy członkowskie mają być serializowane).
Serializacja w komunikacji wirtualnej platformy .NET
Komunikacja zdalna platformy .NET obsługuje dwa sposoby serializacji i deserializacji obiektów między klientem a serwerem:
Według wartości — wartości obiektu są serializowane przez granice warstwy, a nowe wystąpienie tego obiektu jest tworzone w innej warstwie. Wszystkie wywołania metod lub właściwości tego nowego wystąpienia są wykonywane tylko lokalnie i nie mają wpływu na oryginalny obiekt lub warstwę.
Przy użyciu odwołania — specjalny "odwołanie do obiektu" jest serializowany w granicach warstwy. Gdy jedna warstwa współdziała z metodami lub właściwościami tego obiektu, komunikuje się z powrotem do oryginalnego obiektu w oryginalnej warstwie. Obiekty referencyjne mogą przepływać w obu kierunkach — serwer do klienta lub klient na serwer.
Typy by-value w komunikacji zdalnie są oznaczone atrybutem [Serializable] lub implementują ISerializable, jak w poniższym przykładzie:
[Serializable]
public class RemotingCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
Typy według odwołań pochodzą z klasy MarshalByRefObject, podobnie jak w poniższym przykładzie:
public class RemotingCustomerReference : MarshalByRefObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
Niezwykle ważne jest, aby zrozumieć implikacje obiektów by-reference remotingu. Jeśli warstwa (klient lub serwer) wysyła obiekt by-reference do innej warstwy, wszystkie wywołania metody są wykonywane z powrotem w warstwie będącą właścicielem obiektu. Na przykład metody wywoływania przez klienta obiektu by-reference zwróconego przez serwer będą wykonywać kod na serwerze. Podobnie serwer wywołujący metody na obiekcie by-reference dostarczonym przez klienta wykona kod z powrotem na kliencie. Z tego powodu korzystanie z komunikacji wirtualnej platformy .NET jest zalecane tylko w środowiskach w pełni zaufanych. Uwidacznianie publicznego punktu końcowego komunikacji wirtualnej platformy .NET dla niezaufanych klientów spowoduje, że serwer komunikacji wirtualnej będzie narażony na ataki.
Serializacja w programie WCF
Program WCF obsługuje tylko serializacji według wartości. Najczęstszym sposobem definiowania typu do wymiany między klientem a serwerem jest w poniższym przykładzie:
[DataContract]
public class WCFCustomer
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public int CustomerId { get; set; }
}
Atrybut [DataContract] identyfikuje ten typ jako ten, który można serializować i deserializować między klientem a serwerem. Atrybut [DataMember] identyfikuje poszczególne właściwości lub pola do serializacji.
Gdy program WCF wysyła obiekt między warstwami, serializuje tylko wartości i tworzy nowe wystąpienie obiektu w innej warstwie. Każda interakcja z wartościami obiektu odbywa się tylko lokalnie — nie komunikują się z inną warstwą w sposób, w jaki działają obiekty referencyjne programu .NET Remoting. Aby uzyskać więcej informacji, zobacz Serializacja i Deserializacja.
Możliwości obsługi wyjątków
Wyjątki w komunikacji wirtualnej platformy .NET
Wyjątki zgłaszane przez serwer remoting są serializowane, wysyłane do klienta i zgłaszane lokalnie na kliencie, jak każdy inny wyjątek. Wyjątki niestandardowe mogą być tworzone przez podklasowanie typu Wyjątek i oznaczanie go znakiem [Serializable]. Większość wyjątków struktury jest już oznaczona w ten sposób, co pozwala na zgłaszanie większości przez serwer, serializację i ponowne zgłaszanie na kliencie. Chociaż ten projekt jest wygodny podczas programowania, informacje po stronie serwera mogą być przypadkowo ujawniane klientowi. Jest to jeden z wielu powodów, dla których komunikacja zdalna powinna być używana tylko w w pełni zaufanych środowiskach.
Wyjątki i błędy w programie WCF
Program WCF nie zezwala na zwracanie dowolnych typów wyjątków z serwera do klienta, ponieważ może to prowadzić do przypadkowego ujawnienia informacji. Jeśli operacja usługi zgłasza nieoczekiwany wyjątek, powoduje zgłoszenie wyjątku błędu ogólnego przeznaczenia na kliencie. Ten wyjątek nie zawiera żadnych informacji, dlaczego lub gdzie wystąpił problem, a w przypadku niektórych aplikacji jest to wystarczające. Aplikacje, które muszą komunikować bogatsze informacje o błędach klientowi, definiując kontrakt błędów.
Aby to zrobić, najpierw utwórz typ [DataContract], aby nosić informacje o błędzie.
[DataContract]
public class CustomerServiceFault
{
[DataMember]
public string ErrorMessage { get; set; }
[DataMember]
public int CustomerId {get;set;}
}
Określ kontrakt błędów, który ma być używany dla każdej operacji usługi.
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
[FaultContract(typeof(CustomerServiceFault))]
Customer GetCustomer(int customerId);
}
Serwer zgłasza warunki błędów, zgłaszając wyjątek FaultException.
throw new FaultException<CustomerServiceFault>(
new CustomerServiceFault() {
CustomerId = customerId,
ErrorMessage = "Illegal customer Id"
});
A za każdym razem, gdy klient wysyła żądanie do serwera, może przechwytywać błędy jako normalne wyjątki.
try
{
Customer customer = server.GetCustomer(-1);
}
catch (FaultException<CustomerServiceFault> fault)
{
Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}
Aby uzyskać więcej informacji na temat kontraktów błędów, zobacz FaultException.
Zagadnienia związane z zabezpieczeniami
Zabezpieczenia na platformie .NET — komunikacja zdalna
Niektóre kanały komunikacji wirtualnej platformy .NET obsługują funkcje zabezpieczeń, takie jak uwierzytelnianie i szyfrowanie w warstwie kanału (IPC i TCP). Kanał HTTP opiera się na usługach Internet Information Services (IIS) na potrzeby uwierzytelniania i szyfrowania. Pomimo tej obsługi należy rozważyć komunikację zdalną platformy .NET niezabezpieczonego protokołu komunikacyjnego i używać go tylko w środowiskach w pełni zaufanych. Nigdy nie uwidaczniaj publicznego punktu końcowego komunikacji wirtualnej z Internetem lub niezaufanych klientów.
Zabezpieczenia w programie WCF
Program WCF został zaprojektowany z myślą o zabezpieczeniach, częściowo w celu rozwiązania problemów z rodzajami luk w zabezpieczeniach znalezionych na platformie .NET Remoting. Program WCF oferuje zabezpieczenia zarówno na poziomie transportu, jak i komunikatów oraz oferuje wiele opcji uwierzytelniania, autoryzacji, szyfrowania itd. Aby uzyskać więcej informacji, zobacz następujące tematy:
Migrowanie do programu WCF
Dlaczego warto przeprowadzić migrację z komunikacji zdalnie do programu WCF?
Komunikacja zdalna platformy .NET to starszy produkt. Zgodnie z opisem w temacie Remoting platformy .NET jest uważany za starszy produkt i nie jest zalecany do tworzenia nowych rozwiązań. WCF lub ASP.NET internetowy interfejs API są zalecane dla nowych i istniejących aplikacji.
WCF używa standardów międzyplatformowych. Program WCF został zaprojektowany z myślą o współdziałaniu międzyplatformowym i obsługuje wiele standardów branżowych (SOAP, WS-Security, WS-Trust itp.). Usługa WCF może współdziałać z klientami działającymi w systemach operacyjnych innych niż Windows. Komunikacja zdalna została zaprojektowana głównie w środowiskach, w których zarówno serwer, jak i aplikacje klienckie działają przy użyciu programu .NET Framework w systemie operacyjnym Windows.
Program WCF ma wbudowane zabezpieczenia. Program WCF został zaprojektowany z myślą o zabezpieczeniach i oferuje wiele opcji uwierzytelniania, zabezpieczeń na poziomie transportu, zabezpieczeń na poziomie komunikatów itp. Komunikacja zdalna została zaprojektowana w celu ułatwienia współdziałania aplikacji, ale nie została zaprojektowana tak, aby była bezpieczna w środowiskach niezauwierzanych. Program WCF został zaprojektowany tak, aby działał zarówno w zaufanych, jak i nie zaufanych środowiskach.
Rekomendacje migracji
Poniżej przedstawiono zalecane kroki migracji z komunikacji wirtualnej platformy .NET do usługi WCF:
Utwórz kontrakt usługi. Zdefiniuj typy interfejsów usługi i oznacz je za pomocą atrybutu [ServiceContract]. Oznacz wszystkie metody, które klienci będą mogli wywoływać za pomocą polecenia [OperationContract].
Utwórz kontrakt danych. Zdefiniuj typy danych, które będą wymieniane między serwerem a klientem, i oznacz je atrybutem [DataContract]. Oznacz wszystkie pola i właściwości, których klient będzie mógł używać z elementem [DataMember].
Utwórz kontrakt błędów (opcjonalnie). Utwórz typy, które będą wymieniane między serwerem a klientem, gdy wystąpią błędy. Oznacz te typy ciągami [DataContract] i [DataMember], aby umożliwić ich serializację. Dla wszystkich operacji usługi oznaczonych ciągiem [OperationContract], oznacz je również za pomocą polecenia [FaultContract], aby wskazać, które błędy mogą zwracać.
Konfigurowanie i hostowanie usługi. Po utworzeniu kontraktu usługi następnym krokiem jest skonfigurowanie powiązania w celu uwidocznienia usługi w punkcie końcowym. Aby uzyskać więcej informacji, zobacz Punkty końcowe: adresy, powiązania i kontrakty.
Po zmigrowaniu aplikacji remoting do usługi WCF nadal ważne jest usunięcie zależności z komunikacji wirtualnej platformy .NET. Dzięki temu wszystkie luki w zabezpieczeniach komunikacji zdalnie zostaną usunięte z aplikacji. Te kroki obejmują następujące czynności:
Zaprzestanie korzystania z obiektu MarshalByRefObject. Typ MarshalByRefObject istnieje tylko w przypadku komunikacji zdalnie i nie jest używany przez usługę WCF. Wszelkie typy aplikacji klasy podrzędnej MarshalByRefObject powinny zostać usunięte lub zmienione.
Zaprzestanie stosowania produktu [Serializable] i ISerializable. Atrybut [Serializable] i interfejs ISerializable zostały pierwotnie zaprojektowane do serializacji typów w zaufanych środowiskach i są one używane przez komunikacji zdalną. Serializacja WCF opiera się na typach oznaczonych ciągami [DataContract] i [DataMember]. Typy danych używane przez aplikację powinny być modyfikowane do używania [DataContract] i nie do używania funkcji ISerializable lub [Serializable].
Scenariusze migracji
Teraz zobaczmy, jak wykonać następujące typowe scenariusze komunikacji zdalniej w programie WCF:
Serwer zwraca obiekt według wartości do klienta
Serwer zwraca obiekt według odwołania do klienta
Klient wysyła obiekt według wartości do serwera
Uwaga
Wysyłanie obiektu przez odwołanie od klienta do serwera nie jest dozwolone w programie WCF.
Podczas odczytywania tych scenariuszy załóżmy, że nasze interfejsy bazowe dla komunikacji wirtualnej platformy .NET wyglądają jak w poniższym przykładzie. Implementacja komunikacji równorzędnej platformy .NET nie jest tutaj ważna, ponieważ chcemy zilustrować tylko sposób implementowania równoważnych funkcji za pomocą programu WCF.
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) {…}
}
Scenariusz 1. Usługa zwraca obiekt według wartości
W tym scenariuszu pokazano, że serwer zwraca obiekt do klienta według wartości. Program WCF zawsze zwraca obiekty z serwera według wartości, więc poniższe kroki po prostu opisują sposób tworzenia normalnej usługi WCF.
Zacznij od zdefiniowania interfejsu publicznego dla usługi WCF i oznacz go za pomocą atrybutu [ServiceContract]. Użyjemy metody [OperationContract], aby zidentyfikować metody po stronie serwera, które wywoła nasz klient.
[ServiceContract] public interface ICustomerService { [OperationContract] Customer GetCustomer(int customerId); [OperationContract] bool UpdateCustomer(Customer customer); }
Następnym krokiem jest utworzenie kontraktu danych dla tej usługi. W tym celu tworzymy klasy (nie interfejsy) oznaczone atrybutem [DataContract]. Poszczególne właściwości lub pola, które chcemy zobaczyć zarówno dla klienta, jak i serwera, są oznaczone elementem [DataMember]. Jeśli chcemy, aby typy pochodne były dozwolone, musimy użyć atrybutu [KnownType], aby je zidentyfikować. Jedynymi typami WCF będzie można serializować lub deserializować dla tej usługi są te w interfejsie usługi i tych "znanych typów". Próba wymiany dowolnego innego typu, którego nie ma na tej liście, zostanie odrzucona.
[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; } }
Następnie udostępniamy implementację interfejsu usługi.
public class CustomerService : ICustomerService { public Customer GetCustomer(int customerId) { // read from database } public bool UpdateCustomer(Customer customer) { // write to database } }
Aby uruchomić usługę WCF, musimy zadeklarować punkt końcowy, który uwidacznia ten interfejs usługi pod określonym adresem URL przy użyciu określonego powiązania WCF. Zazwyczaj odbywa się to przez dodanie poniższych sekcji do pliku web.config projektu serwera.
<configuration> <system.serviceModel> <services> <service name="Server.CustomerService"> <endpoint address="http://localhost:8083/CustomerService" binding="basicHttpBinding" contract="Shared.ICustomerService" /> </service> </services> </system.serviceModel> </configuration>
Następnie można uruchomić usługę WCF przy użyciu następującego kodu:
ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService)); customerServiceHost.Open();
Po uruchomieniu elementu ServiceHost używa pliku web.config do ustanowienia odpowiedniego kontraktu, powiązania i punktu końcowego. Aby uzyskać więcej informacji na temat plików konfiguracji, zobacz Konfigurowanie usług przy użyciu plików konfiguracji. Ten styl uruchamiania serwera jest nazywany hostem własnym. Aby dowiedzieć się więcej o innych opcjach hostowania usług WCF, zobacz Hosting Services (Usługi hostingu).
Plik app.config projektu klienta musi zadeklarować pasujące informacje o powiązaniu dla punktu końcowego usługi. Najprostszym sposobem wykonania tej czynności w programie Visual Studio jest użycie polecenia Dodaj odwołanie do usługi, co spowoduje automatyczne zaktualizowanie pliku app.config. Alternatywnie te same zmiany można dodać ręcznie.
<configuration> <system.serviceModel> <client> <endpoint name="customerservice" address="http://localhost:8083/CustomerService" binding="basicHttpBinding" contract="Shared.ICustomerService"/> </client> </system.serviceModel> </configuration>
Aby uzyskać więcej informacji na temat korzystania z funkcji Add Service Reference, zobacz Instrukcje: Dodawanie, aktualizowanie lub usuwanie odwołania do usługi.
Teraz możemy wywołać usługę WCF z klienta. Robimy to, tworząc fabrykę kanałów dla tej usługi, prosząc o kanał i bezpośrednio wywołując metodę, której chcemy użyć w tym kanale. Możemy to zrobić, ponieważ kanał implementuje interfejs usługi i obsługuje dla nas podstawową logikę żądania/odpowiedzi. Wartość zwracana z tego wywołania metody jest deserializowaną kopią odpowiedzi serwera.
ChannelFactory<ICustomerService> factory = new ChannelFactory<ICustomerService>("customerservice"); ICustomerService service = factory.CreateChannel(); Customer customer = service.GetCustomer(42); Console.WriteLine($" Customer {customer.FirstName} {customer.LastName} received.");
Obiekty zwracane przez usługę WCF z serwera do klienta są zawsze według wartości. Obiekty są deserializowanymi kopiami danych wysyłanych przez serwer. Klient może wywoływać metody na tych kopiach lokalnych bez żadnego zagrożenia wywołania kodu serwera za pośrednictwem wywołań zwrotnych.
Scenariusz 2. Serwer zwraca obiekt według odwołania
W tym scenariuszu przedstawiono serwer dostarczający klientowi obiekt przy użyciu odwołania. W przypadku komunikacji wirtualnej platformy .NET jest to obsługiwane automatycznie dla dowolnego typu pochodzącego z obiektu MarshalByRefObject, który jest serializowany według odwołania. Przykładem tego scenariusza jest umożliwienie wielu klientom posiadania niezależnych obiektów po stronie serwera sesji. Jak wspomniano wcześniej, obiekty zwracane przez usługę WCF są zawsze według wartości, więc nie ma bezpośredniego odpowiednika obiektu by-reference, ale można osiągnąć coś podobnego do semantyki odwołania przy użyciu EndpointAddress10 obiektu. Jest to obiekt z możliwością serializacji według wartości, który może być używany przez klienta do uzyskania sesji obiektu by-reference na serwerze. Umożliwia to scenariusz posiadania wielu klientów z niezależnymi obiektami po stronie serwera sesji.
Najpierw musimy zdefiniować kontrakt usługi WCF, który odpowiada samemu obiektowi sesji.
[ServiceContract(SessionMode = SessionMode.Allowed)] public interface ISessionBoundObject { [OperationContract] string GetCurrentValue(); [OperationContract] void SetCurrentValue(string value); }
Napiwek
Zwróć uwagę, że obiekt sesji jest oznaczony elementem [ServiceContract], co czyni go normalnym interfejsem usługi WCF. Ustawienie właściwości SessionMode wskazuje, że będzie to usługa sesji. W programie WCF sesja jest sposobem korelowania wielu komunikatów wysyłanych między dwoma punktami końcowymi. Oznacza to, że gdy klient uzyska połączenie z tą usługą, zostanie ustanowiona sesja między klientem a serwerem. Klient będzie używać pojedynczego unikatowego wystąpienia obiektu po stronie serwera dla wszystkich jego interakcji w ramach tej pojedynczej sesji.
Następnie musimy zapewnić implementację tego interfejsu usługi. Oznaczając ją elementem [ServiceBehavior] i ustawiając wartość InstanceContextMode, informujemy WCF, że chcemy użyć unikatowego wystąpienia tego typu dla każdej sesji.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class MySessionBoundObject : ISessionBoundObject { private string _value; public string GetCurrentValue() { return _value; } public void SetCurrentValue(string val) { _value = val; } }
Teraz potrzebujemy sposobu uzyskania wystąpienia tego obiektu sesji. Robimy to przez utworzenie innego interfejsu usługi WCF, który zwraca obiekt EndpointAddress10. Jest to zsererowalna forma punktu końcowego, którego klient może użyć do utworzenia obiektu sesji.
[ServiceContract] public interface ISessionBoundFactory { [OperationContract] EndpointAddress10 GetInstanceAddress(); }
Zaimplementujemy tę usługę WCF:
public class SessionBoundFactory : ISessionBoundFactory { public static ChannelFactory<ISessionBoundObject> _factory = new ChannelFactory<ISessionBoundObject>("sessionbound"); public SessionBoundFactory() { } public EndpointAddress10 GetInstanceAddress() { IClientChannel channel = (IClientChannel)_factory.CreateChannel(); return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress); } }
Ta implementacja obsługuje fabrykę pojedynczego kanału w celu tworzenia obiektów sesji. Po wywołaniu metody GetInstanceAddress() tworzy kanał i tworzy obiekt EndpointAddress10, który skutecznie wskazuje adres zdalny skojarzony z tym kanałem. EndpointAddress10 to po prostu typ danych, który można zwrócić do klienta według wartości.
Musimy zmodyfikować plik konfiguracji serwera, wykonując następujące dwie czynności, jak pokazano w poniższym przykładzie:
Zadeklaruj sekcję <klienta> opisjącą punkt końcowy dla obiektu sesji. Jest to konieczne, ponieważ serwer działa również jako klient w tej sytuacji.
Zadeklaruj punkty końcowe dla obiektu fabryki i obiektu sesji. Jest to konieczne, aby umożliwić klientowi komunikowanie się z punktami końcowymi usługi w celu uzyskania punktu końcowegoAddress10 i utworzenia kanału sesji.
<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>
Następnie możemy uruchomić następujące usługi:
ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory)); factoryHost.Open(); ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject)); sessionHost.Open();
Konfigurujemy klienta, deklarując te same punkty końcowe w pliku app.config projektu.
<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>
Aby utworzyć i użyć tego obiektu sesji, klient musi wykonać następujące czynności:
Utwórz kanał w usłudze ISessionBoundFactory.
Użyj tego kanału, aby wywołać usługę w celu uzyskania punktu końcowegoAddress10.
Użyj elementu EndpointAddress10, aby utworzyć kanał w celu uzyskania obiektu sesji.
Interakcja z obiektem sesji w celu zademonstrowania, że pozostaje to samo wystąpienie w wielu wywołaniach.
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"); }
Program WCF zawsze zwraca obiekty według wartości, ale istnieje możliwość obsługi równoważności semantyki odwołania za pomocą funkcji EndpointAddress10. Dzięki temu klient może zażądać sesji wystąpienia usługi WCF, po którym może wchodzić z nim w interakcje, podobnie jak w przypadku każdej innej usługi WCF.
Scenariusz 3. Klient wysyła serwer do wystąpienia według wartości
W tym scenariuszu klient wysyła wystąpienie obiektu innego niż pierwotny do serwera według wartości. Ponieważ program WCF wysyła tylko obiekty według wartości, w tym scenariuszu pokazano normalne użycie programu WCF.
Użyj tej samej usługi WCF ze scenariusza 1.
Użyj klienta, aby utworzyć nowy obiekt według wartości (Klient), utworzyć kanał do komunikowania się z usługą ICustomerService i wysłać do niego obiekt.
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}.");
Obiekt klienta zostanie zserializowany i wysłany na serwer, na którym zostanie zdeserializowany do nowej kopii tego obiektu.
Uwaga
Ten kod ilustruje również wysyłanie typu pochodnego (PremiumCustomer). Interfejs usługi oczekuje obiektu Customer, ale atrybut [KnownType] w klasie Customer wskazał, że parametr PremiumCustomer również był dozwolony. Program WCF nie podejmie próby serializacji lub deserializacji dowolnego innego typu za pośrednictwem tego interfejsu usługi.
Normalne wymiany danych w programie WCF są według wartości. Gwarantuje to, że wywoływanie metod na jednym z tych obiektów danych jest wykonywane tylko lokalnie — nie wywoła kodu w innej warstwie. Chociaż istnieje możliwość osiągnięcia czegoś takiego jak obiekty referencyjne zwrócone z serwera, nie można przekazać klientowi obiektu odniesienia do serwera. Scenariusz, który wymaga konwersacji między klientem a serwerem, można osiągnąć w programie WCF przy użyciu usługi dwukierunkowej. Aby uzyskać więcej informacji, zobacz Usługi dwustronne.
Podsumowanie
Komunikacja zdalna platformy .NET to struktura komunikacji przeznaczona do użycia tylko w środowiskach w pełni zaufanych. Jest to starszy produkt i obsługiwany tylko w celu zapewnienia zgodności z poprzednimi wersjami. Nie należy jej używać do tworzenia nowych aplikacji. Z drugiej strony program WCF został zaprojektowany z myślą o zabezpieczeniach i jest zalecany dla nowych i istniejących aplikacji. Firma Microsoft zaleca migrację istniejących aplikacji remoting w celu korzystania z interfejsu API sieci Web WCF lub ASP.NET.