Instrukcje: migrowanie zarządzanego kodu DCOM do WCF

Program Windows Communication Foundation (WCF) jest zalecanym i bezpiecznym wyborem w modelu obiektów składników rozproszonych (DCOM) dla wywołań kodu zarządzanego między serwerami i klientami w środowisku rozproszonym. W tym artykule pokazano, jak przeprowadzić migrację kodu z modelu DCOM do programu WCF w następujących scenariuszach.

  • Usługa zdalna zwraca obiekt według wartości do klienta

  • Klient wysyła obiekt według wartości do usługi zdalnej

  • Usługa zdalna zwraca obiekt przez odwołanie do klienta

Ze względów bezpieczeństwa wysyłanie obiektu przez odwołanie od klienta do usługi nie jest dozwolone w programie WCF. 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 na temat usług dwukierunkowych, zobacz Usługi dwustronne.

Aby uzyskać więcej informacji na temat tworzenia usług i klientów programu WCF dla tych usług, zobacz Podstawowe programowanie WCF, projektowanie i implementowanie usług oraz Tworzenie klientów.

Przykładowy kod MODELU DCOM

W przypadku tych scenariuszy interfejsy DCOM ilustrowane przy użyciu programu WCF mają następującą strukturę:

[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  
{  
}  

Usługa zwraca obiekt według wartości

W tym scenariuszu wykonasz wywołanie usługi, a metoda zwraca obiekt, który jest przekazywany przez wartość z serwera do klienta. Ten scenariusz reprezentuje następujące wywołanie MODELU COM:

public interface IRemoteService  
{  
    Customer GetObjectByValue();  
}  

W tym scenariuszu klient otrzymuje deserializowaną kopię obiektu z usługi zdalnej. Klient może wchodzić w interakcje z tą kopią lokalną bez ponownego wywoływania usługi. Innymi słowy, klient ma gwarancję, że usługa nie będzie w żaden sposób zaangażowana, gdy metody kopii lokalnej są wywoływane. Program WCF zawsze zwraca obiekty z usługi według wartości, więc w poniższych krokach opisano tworzenie regularnej usługi WCF.

Krok 1. Definiowanie interfejsu usługi WCF

Zdefiniuj interfejs publiczny dla usługi WCF i oznacz go za pomocą atrybutu [ServiceContractAttribute]. Oznacz metody, które chcesz uwidocznić klientom za pomocą atrybutu [OperationContractAttribute]. W poniższym przykładzie pokazano użycie tych atrybutów w celu zidentyfikowania interfejsu po stronie serwera i metod interfejsu, które klient może wywołać. Metoda używana w tym scenariuszu jest wyświetlana pogrubioną.

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

Krok 2. Definiowanie kontraktu danych

Następnie należy utworzyć kontrakt danych dla usługi, który będzie opisywać sposób wymiany danych między usługą a jej klientami. Klasy opisane w kontrakcie danych powinny być oznaczone atrybutem [DataContractAttribute]. Poszczególne właściwości lub pola, które mają być widoczne zarówno dla klienta, jak i serwera, powinny być oznaczone atrybutem [DataMemberAttribute]. Jeśli chcesz, aby typy pochodzące z klasy w umowie danych były dozwolone, musisz zidentyfikować je za pomocą atrybutu [KnownTypeAttribute]. Program WCF serializuje lub deserializuje tylko typy w interfejsie usługi i typach zidentyfikowanych jako znane typy. Jeśli spróbujesz użyć typu, który nie jest znanym typem, wystąpi wyjątek.

Aby uzyskać więcej informacji na temat kontraktów danych, zobacz Kontrakty danych.

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

Krok 3. Implementowanie usługi WCF

Następnie należy zaimplementować klasę usługi WCF, która implementuje interfejs zdefiniowany w poprzednim kroku.

public class CustomerService: ICustomerManager
{  
    public void StoreCustomer(Customer customer)  
    {  
        // write to a database  
    }  
    public Customer GetCustomer(string firstName, string lastName)  
    {  
        // read from a database  
    }  
}  

Krok 4. Konfigurowanie usługi i klienta

Aby uruchomić usługę WCF, należy zadeklarować punkt końcowy, który uwidacznia ten interfejs usługi pod określonym adresem URL przy użyciu określonego powiązania WCF. Powiązanie określa szczegóły transportu, kodowania i protokołu dla klientów i serwerów do komunikacji. Zazwyczaj do pliku konfiguracji projektu usługi (web.config) są zwykle dodawane powiązania. Poniżej przedstawiono wpis powiązania dla przykładowej usługi:

<configuration>  
  <system.serviceModel>  
    <services>  
      <service name="Server.CustomerService">  
        <endpoint address="http://localhost:8083/CustomerManager"
                  binding="basicHttpBinding"  
                  contract="Shared.ICustomerManager" />  
      </service>  
    </services>  
  </system.serviceModel>  
</configuration>  

Następnie należy skonfigurować klienta tak, aby był zgodny z informacjami powiązania określonymi przez usługę. W tym celu dodaj następujący kod do pliku konfiguracji aplikacji klienta (app.config).

<configuration>  
  <system.serviceModel>  
    <client>  
      <endpoint name="customermanager"
                address="http://localhost:8083/CustomerManager"
                binding="basicHttpBinding"
                contract="Shared.ICustomerManager"/>  
    </client>  
  </system.serviceModel>  
</configuration>  

Krok 5. Uruchamianie usługi

Na koniec możesz samodzielnie hostować ją w aplikacji konsolowej, dodając następujące wiersze do aplikacji usługi i uruchamiając aplikację. Aby uzyskać więcej informacji na temat innych sposobów hostowania aplikacji usługi WCF, Hosting Services.

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

Krok 6. Wywoływanie usługi z klienta

Aby wywołać usługę od klienta, należy utworzyć fabrykę kanałów dla usługi i zażądać kanału, który umożliwi bezpośrednie wywołanie GetCustomer metody bezpośrednio z klienta. Kanał implementuje interfejs usługi i obsługuje podstawową logikę żądania/odpowiedzi. Wartość zwracana z wywołania tej metody jest deserializowaną kopią odpowiedzi usługi.

ChannelFactory<ICustomerManager> factory =
     new ChannelFactory<ICustomerManager>("customermanager");  
ICustomerManager service = factory.CreateChannel();  
Customer customer = service.GetCustomer("Mary", "Smith");  

Klient wysyła obiekt by-value do serwera

W tym scenariuszu klient wysyła obiekt do serwera według wartości. Oznacza to, że serwer otrzyma zdeserializowaną kopię obiektu. Serwer może wywoływać metody na tej kopii i mieć gwarancję, że nie ma wywołania zwrotnego do kodu klienta. Jak wspomniano wcześniej, normalne wymiany danych w programie WCF są wartością bajtową. Gwarantuje to, że wywoływanie metod na jednym z tych obiektów wykonuje tylko lokalnie — nie wywoła kodu na kliencie.

Ten scenariusz reprezentuje następujące wywołanie metody COM:

public interface IRemoteService  
{  
    void SendObjectByValue(Customer customer);  
}  

W tym scenariuszu użyto tego samego interfejsu usługi i kontraktu danych, jak pokazano w pierwszym przykładzie. Ponadto klient i usługa zostaną skonfigurowane w taki sam sposób. W tym przykładzie tworzony jest kanał do wysyłania obiektu i uruchamiania go w taki sam sposób. Jednak w tym przykładzie utworzysz klienta, który wywołuje usługę, przekazując obiekt według wartości. Metoda usługi wywoływana przez klienta w kontrakcie usługi jest wyświetlana pogrubioną czcionką:

[ServiceContract]  
public interface ICustomerManager  
{  
    [OperationContract]     void StoreCustomer(Customer customer);  
  
    [OperationContract]  
    Customer GetCustomer(string firstName, string lastName);  
}  

Dodawanie kodu do klienta, który wysyła obiekt by-value

Poniższy kod pokazuje, jak klient tworzy nowy obiekt klienta według wartości, tworzy kanał do komunikowania się z ICustomerManager usługą i wysyła do niego obiekt klienta.

Obiekt klienta zostanie serializowany i wysłany do usługi, gdzie jest deserializowany przez usługę do nowej kopii tego obiektu. Wszystkie metody wywoływane przez usługę tego obiektu będą wykonywane tylko lokalnie na serwerze. Należy pamiętać, że ten kod ilustruje wysyłanie typu pochodnego (PremiumCustomer). Kontrakt usługi oczekuje Customer obiektu, ale kontrakt danych usługi używa atrybutu [KnownTypeAttribute] w celu wskazania, że PremiumCustomer jest to również dozwolone. Program WCF nie podejmie prób serializacji lub deserializacji dowolnego innego typu za pośrednictwem tego interfejsu usługi.

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

Usługa zwraca obiekt według odwołania

W tym scenariuszu aplikacja kliencka wykonuje wywołanie usługi zdalnej, a metoda zwraca obiekt, który jest przekazywany przez odwołanie z usługi do klienta.

Jak wspomniano wcześniej, usługi WCF zawsze zwracają obiekt według wartości. Można jednak osiągnąć podobny wynik przy użyciu EndpointAddress10 klasy . Jest EndpointAddress10 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.

Zachowanie obiektu by-reference w programie WCF pokazanym w tym scenariuszu różni się od modelu DCOM. W modelu DCOM serwer może zwrócić obiekt by-reference do klienta bezpośrednio, a klient może wywołać metody tego obiektu, które są wykonywane na serwerze. Jednak w programie WCF zwracany obiekt jest zawsze wartością. Klient musi przyjąć ten obiekt by-value reprezentowany przez EndpointAddress10 i użyć go do utworzenia własnego obiektu sesji by-reference. Metoda klienta wywołuje obiekt sesji wykonywany na serwerze. Innymi słowy, ten obiekt by-reference w programie WCF jest normalną usługą WCF, która jest skonfigurowana do 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. Sesjowe kontrakty WCF są podobne do wzorców żądań/odpowiedzi sieci zorientowanych na połączenie.

Ten scenariusz jest reprezentowany przez następującą metodę DCOM.

public interface IRemoteService  
{  
    IRemoteObject GetObjectByReference();  
}  

Krok 1. Definiowanie interfejsu usługi sesji WCF i implementacji

Najpierw zdefiniuj interfejs usługi WCF zawierający obiekt sesji.

W tym kodzie obiekt sesji jest oznaczony atrybutem ServiceContract , który identyfikuje go jako zwykły interfejs usługi WCF. Ponadto właściwość jest ustawiona SessionMode tak, aby wskazywała, że będzie to usługa sesji.

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

Poniższy kod przedstawia implementację usługi.

Usługa jest oznaczona atrybutem [ServiceBehavior] i jej właściwości InstanceContextMode ustawioną na InstanceContextMode.PerSessions, aby wskazać, że dla każdej sesji należy utworzyć unikatowe wystąpienie tego typu.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]  
    public class MySessionBoundObject : ISessionBoundObject  
    {  
        private string _value;  
  
        public string GetCurrentValue()  
        {  
            return _value;  
        }  
  
        public void SetCurrentValue(string val)  
        {  
            _value = val;  
        }  
  
    }  

Krok 2. Definiowanie usługi fabryki WCF dla obiektu sesji

Usługa, która tworzy obiekt sesji, musi być zdefiniowana i zaimplementowana. Poniższy kod pokazuje, jak to zrobić. Ten kod tworzy inną usługę EndpointAddress10 WCF, która zwraca obiekt. Jest to zsererowalna forma punktu końcowego, za pomocą których można utworzyć obiekt pełny sesji.

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

Poniżej przedstawiono implementację tej usługi. Ta implementacja obsługuje fabrykę pojedynczego kanału w celu tworzenia obiektów sesji. Po GetInstanceAddress wywołaniu tworzy kanał i tworzy EndpointAddress10 obiekt wskazujący adres zdalny skojarzony z tym kanałem. EndpointAddress10 to typ danych, który może zostać zwrócony do klienta według wartości.

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

Krok 3. Konfigurowanie i uruchamianie usług WCF

Aby hostować te usługi, należy wprowadzić następujące dodatki do pliku konfiguracji serwera (web.config).

  1. Dodaj sekcję opisjącą <client> punkt końcowy dla obiektu sesji. W tym scenariuszu serwer działa również jako klient i należy go skonfigurować, aby to włączyć.

  2. W sekcji zadeklaruj <services> punkty końcowe usługi dla obiektu fabryki i obiektu sesji. Dzięki temu klient może komunikować się z punktami końcowymi usługi, uzyskiwać EndpointAddress10 i tworzyć kanał sesji.

Poniżej przedstawiono przykładowy plik konfiguracji z następującymi ustawieniami:

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

Dodaj następujące wiersze do aplikacji konsolowej, aby samodzielnie hostować usługę i uruchomić aplikację.

ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
factoryHost.Open();  
  
ServiceHost sessionBoundServiceHost = new ServiceHost(  
typeof(MySessionBoundObject));  
sessionBoundServiceHost.Open();  

Krok 4. Konfigurowanie klienta i wywoływanie usługi

Skonfiguruj klienta tak, aby komunikował się z usługami WCF, wykonując następujące wpisy w pliku konfiguracji aplikacji projektu (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>  

Aby wywołać usługę, dodaj kod do klienta, aby wykonać następujące czynności:

  1. Utwórz kanał w usłudze ISessionBoundFactory .

  2. Użyj kanału, aby wywołać ISessionBoundFactory usługę EndpointAddress10 , aby uzyskać obiekt.

  3. Użyj elementu EndpointAddress10 , aby utworzyć kanał, aby uzyskać obiekt sesji.

  4. Wywołaj SetCurrentValue metody i GetCurrentValue , aby zademonstrować, że pozostaje to samo wystąpienie obiektu jest używane w wielu wywołaniach.

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

Zobacz też