Delen via


Procedure: Managed-Code DCOM migreren naar WCF

Windows Communication Foundation (WCF) is de aanbevolen en veilige keuze via DCOM (Distributed Component Object Model) voor beheerde code-aanroepen tussen servers en clients in een gedistribueerde omgeving. In dit artikel wordt beschreven hoe u code migreert van DCOM naar WCF voor de volgende scenario's.

  • De externe service retourneert een object op waarde naar de client

  • De client verzendt een object op waarde naar de externe service

  • De externe service retourneert een object per verwijzing naar de client

Om veiligheidsredenen is het verzenden van een object by-reference van de client naar de service niet toegestaan in WCF. Een scenario dat een gesprek tussen client en server vereist, kan worden bereikt in WCF met behulp van een duplex-service. Zie Duplex Services voor meer informatie over duplexservices.

Voor meer informatie over het maken van WCF-services en -clients voor deze services raadpleegt u Basic WCF Programming, Designing and Implementing Services en Building Clients.

DCOM-voorbeeldcode

Voor deze scenario's hebben de DCOM-interfaces die met WCF worden geïllustreerd de volgende structuur:

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

De service retourneert een object op waarde

Voor dit scenario maakt u een aanroep naar een service en retourneert deze methode een object, dat wordt doorgegeven door de waarde van de server naar de client. Dit scenario vertegenwoordigt de volgende COM-aanroep:

public interface IRemoteService  
{  
    Customer GetObjectByValue();  
}  

In dit scenario ontvangt de client een gedeserialiseerde kopie van een object van de externe service. De client kan met deze lokale kopie communiceren zonder terug te bellen naar de service. Met andere woorden, de client is gegarandeerd dat de service niet op enige manier wordt betrokken wanneer methoden op de lokale kopie worden aangeroepen. WCF retourneert altijd objecten van de service op waarde, dus in de volgende stappen wordt beschreven hoe u een reguliere WCF-service maakt.

Stap 1: De WCF-serviceinterface definiëren

Definieer een openbare interface voor de WCF-service en markeer deze met het kenmerk [ServiceContractAttribute]. Markeer de methoden die u beschikbaar wilt maken voor clients met het kenmerk [OperationContractAttribute]. In het volgende voorbeeld ziet u hoe u deze kenmerken gebruikt om de interface en interfacemethoden aan de serverzijde te identificeren die een client kan aanroepen. De methode die voor dit scenario wordt gebruikt, wordt vetgedrukt weergegeven.

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

Stap 2: Het gegevenscontract definiëren

Vervolgens maakt u een gegevenscontract voor de service, waarin wordt beschreven hoe de gegevens worden uitgewisseld tussen de service en de bijbehorende clients. Klassen die in het gegevenscontract worden beschreven, moeten worden gemarkeerd met het kenmerk [DataContractAttribute] . De afzonderlijke eigenschappen of velden die u zichtbaar wilt maken voor zowel de client als de server, moeten worden gemarkeerd met het kenmerk [DataMemberAttribute]. Als u wilt dat typen die zijn afgeleid van een klasse in het gegevenscontract zijn toegestaan, moet u deze identificeren met het kenmerk [KnownTypeAttribute]. WCF serialiseert of deserialiseert alleen typen in de serviceinterface en typen die worden geïdentificeerd als bekende typen. Als u probeert een type te gebruiken dat geen bekend type is, treedt er een uitzondering op.

Zie Data Contracts voor meer informatie over gegevenscontracten.

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

Stap 3: de WCF-service implementeren

Vervolgens moet u de WCF-serviceklasse implementeren waarmee de interface wordt geïmplementeerd die u in de vorige stap hebt gedefinieerd.

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

Stap 4: De service en de client configureren

Als u een WCF-service wilt uitvoeren, moet u een eindpunt declareren dat die service-interface beschikbaar maakt op een specifieke URL met behulp van een specifieke WCF-binding. Een binding specificeert de transport-, coderings- en protocolgegevens voor de clients en de server om te communiceren. Normaal gesproken voegt u bindingen toe aan het configuratiebestand van het serviceproject (web.config). Hieronder ziet u een bindingsvermelding voor de voorbeeldservice:

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

Vervolgens moet u de client zo configureren dat deze overeenkomt met de bindingsgegevens die door de service zijn opgegeven. Voeg hiervoor het volgende toe aan het toepassingsconfiguratiebestand van de client (app.config).

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

Stap 5: de service uitvoeren

Ten slotte kunt u deze zelf hosten in een consoletoepassing door de volgende regels toe te voegen aan de service-app en de app te starten. Voor meer informatie over andere manieren om een WCF-servicetoepassing te hosten, hostingservices.

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

Stap 6: De service aanroepen vanaf de client

Als u de service van de client wilt aanroepen, moet u een kanaalfactory voor de service maken en een kanaal aanvragen, zodat u de GetCustomer methode rechtstreeks vanuit de client kunt aanroepen. Het kanaal implementeert de interface van de service en verwerkt de onderliggende aanvraag-/antwoordlogica voor u. De retourwaarde van die methodeaanroep is de gedeserialiseerde kopie van het serviceantwoord.

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

De client verzendt een by-value-object naar de server

In dit scenario verzendt de client een object naar de server, op waarde. Dit betekent dat de server een gedeserialiseerde kopie van het object ontvangt. De server kan methoden voor die kopie aanroepen en garanderen dat er geen callback naar clientcode is. Zoals eerder vermeld, zijn normale WCF-uitwisselingen van gegevens per waarde. Dit garandeert dat het aanroepen van methoden op een van deze objecten alleen lokaal wordt uitgevoerd. Er wordt geen code op de client aangeroepen.

Dit scenario vertegenwoordigt de volgende COM-methode-aanroep:

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

In dit scenario worden dezelfde serviceinterface en hetzelfde gegevenscontract gebruikt, zoals wordt weergegeven in het eerste voorbeeld. Bovendien worden de client en service op dezelfde manier geconfigureerd. In dit voorbeeld wordt een kanaal gemaakt om het object te verzenden en op dezelfde manier uit te voeren. In dit voorbeeld maakt u echter een client die de service aanroept, waarbij een object op waarde wordt doorgegeven. De servicemethode die de client aanroept in het servicecontract, wordt vetgedrukt weergegeven:

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

Code toevoegen aan de client die een by-value-object verzendt

De volgende code laat zien hoe de client een nieuw by-value-klantobject maakt, een kanaal maakt om met de ICustomerManager service te communiceren en het klantobject naar het object verzendt.

Het klantobject wordt geserialiseerd en verzonden naar de service, waar het door de service wordt gedeserialiseerd in een nieuwe kopie van dat object. Alle methoden die de service aanroept op dit object, worden alleen lokaal uitgevoerd op de server. Het is belangrijk om te weten dat deze code het verzenden van een afgeleid type (PremiumCustomer) illustreert. Het servicecontract verwacht een Customer object, maar het servicegegevenscontract gebruikt het kenmerk [KnownTypeAttribute] om aan te geven dat PremiumCustomer dit ook is toegestaan. WCF mislukt pogingen om een ander type te serialiseren of deserialiseren via deze service-interface.

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

De service retourneert een object per verwijzing

Voor dit scenario roept de client-app de externe service aan en retourneert de methode een object, dat wordt doorgegeven door verwijzing van de service naar de client.

Zoals eerder vermeld, retourneren WCF-services altijd objecten op waarde. U kunt echter een vergelijkbaar resultaat bereiken met behulp van de EndpointAddress10 klasse. Het EndpointAddress10 is een serialiseerbaar by-value-object dat door de client kan worden gebruikt om een sessievol by-reference-object op de server te verkrijgen.

Het gedrag van het by-reference-object in WCF dat in dit scenario wordt weergegeven, verschilt van DCOM. In DCOM kan de server rechtstreeks een by-reference-object naar de client retourneren en kan de client de methoden van dat object aanroepen, die worden uitgevoerd op de server. In WCF is het geretourneerde object echter altijd op waarde. De client moet dat by-value-object nemen, vertegenwoordigd door EndpointAddress10 en gebruiken om een eigen sessieful by-reference-object te maken. De clientmethode roept het sessieful-object aan dat wordt uitgevoerd op de server. Met andere woorden, dit by-reference-object in WCF is een normale WCF-service die is geconfigureerd voor sessieful.

In WCF is een sessie een manier om meerdere berichten te correleren die tussen twee eindpunten worden verzonden. Dit betekent dat wanneer een client een verbinding met deze service verkrijgt, er een sessie tot stand wordt gebracht tussen de client en de server. De client gebruikt één uniek exemplaar van het object aan de serverzijde voor alle interacties binnen deze sessie. Sessionful WCF-contracten zijn vergelijkbaar met verbindingsgeoriënteerde netwerkaanvraag-/antwoordpatronen.

Dit scenario wordt vertegenwoordigd door de volgende DCOM-methode.

public interface IRemoteService  
{  
    IRemoteObject GetObjectByReference();  
}  

Stap 1: De Sessionful WCF-serviceinterface en -implementatie definiëren

Definieer eerst een WCF-serviceinterface die het sessionful-object bevat.

In deze code wordt het sessieful object gemarkeerd met het ServiceContract kenmerk, dat het identificeert als een reguliere WCF-service-interface. Daarnaast is de SessionMode eigenschap ingesteld om aan te geven dat deze een sessieful service is.

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

De volgende code toont de service-implementatie.

De service is gemarkeerd met het kenmerk [ServiceBehavior] en de eigenschap InstanceContextMode is ingesteld op InstanceContextMode.PerSessions om aan te geven dat er voor elke sessie een uniek exemplaar van dit type moet worden gemaakt.

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

Stap 2: De WCF Factory-service voor het sessieful object definiëren

De service die het sessieful object maakt, moet worden gedefinieerd en geïmplementeerd. In de volgende code ziet u hoe u dit doet. Met deze code maakt u een andere WCF-service die een EndpointAddress10 object retourneert. Dit is een serialiseerbare vorm van een eindpunt dat kan worden gebruikt om het sessie-volledige object te maken.

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

Hier volgt de implementatie van deze service. Deze implementatie onderhoudt een singleton-kanaalfactory om sessieful objecten te maken. Wanneer GetInstanceAddress wordt aangeroepen, wordt er een kanaal gemaakt en wordt er een EndpointAddress10 object gemaakt dat verwijst naar het externe adres dat aan dit kanaal is gekoppeld. EndpointAddress10 is een gegevenstype dat kan worden geretourneerd naar de client op waarde.

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

Stap 3: de WCF-services configureren en starten

Als u deze services wilt hosten, moet u de volgende toevoegingen aan het configuratiebestand van de server (web.config) aanbrengen.

  1. Voeg een <client> sectie toe waarin het eindpunt voor het sessieful object wordt beschreven. In dit scenario fungeert de server ook als een client en moet deze worden geconfigureerd om dit in te schakelen.

  2. Declareer in de <services> sectie service-eindpunten voor het factory- en sessionful-object. Hierdoor kan de client communiceren met de service-eindpunten, het EndpointAddress10 sessieful kanaal verkrijgen en maken.

Hier volgt een voorbeeld van een configuratiebestand met deze instellingen:

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

Voeg de volgende regels toe aan een consoletoepassing, om de service zelf te hosten en de app te starten.

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

Stap 4: de client configureren en de service aanroepen

Configureer de client om te communiceren met de WCF-services door de volgende vermeldingen te maken in het toepassingsconfiguratiebestand van het project (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>  

Als u de service wilt aanroepen, voegt u de code toe aan de client om het volgende te doen:

  1. Maak een kanaal naar de ISessionBoundFactory service.

  2. Gebruik het kanaal om de ISessionBoundFactory service aan te roepen en een EndpointAddress10 object te verkrijgen.

  3. Gebruik de EndpointAddress10 functie om een kanaal te maken om een sessieful object te verkrijgen.

  4. Roep de SetCurrentValue en GetCurrentValue methoden aan om aan te tonen dat het hetzelfde objectexemplaren blijft gebruikt voor meerdere aanroepen.

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

Zie ook