Dela via


Migrera från .NET-fjärrkommunikation till WCF

Den här artikeln beskriver hur du migrerar ett program som använder .NET-fjärrkommunikation för att använda Windows Communication Foundation (WCF). Den jämför liknande begrepp mellan dessa produkter och beskriver sedan hur du utför flera vanliga fjärrkommunikationsscenarier i WCF.

.NET-fjärrkommunikation är en äldre produkt som endast stöds för bakåtkompatibilitet. Det är inte säkert i miljöer med blandat förtroende eftersom det inte kan upprätthålla de separata förtroendenivåerna mellan klient och server. Du bör till exempel aldrig exponera en .NET-fjärrkommunikationsslutpunkt för Internet eller för klienter som inte är betrodda. Vi rekommenderar att befintliga fjärrkommunikationsprogram migreras till nyare och säkrare tekniker. Om programmets design endast använder HTTP och är RESTful rekommenderar vi ASP.NET webb-API. Mer information finns i ASP.NET webb-API. Om programmet är baserat på SOAP eller kräver icke-Http-protokoll, till exempel TCP, rekommenderar vi WCF.

Jämföra .NET-fjärrkommunikation med WCF

I det här avsnittet jämförs de grundläggande byggstenarna i .NET-fjärrkommunikation med deras WCF-motsvarigheter. Vi använder dessa byggstenar senare för att skapa några vanliga klient-server-scenarier i WCF. I följande diagram sammanfattas de viktigaste likheterna och skillnaderna mellan .NET Remoting och WCF.

.NET-fjärrkommunikation WCF
Servertyp Underklass MarshalByRefObject Markera med [ServiceContract] attribut
Tjänståtgärder Offentliga metoder för servertyp Markera med [OperationContract] attribut
Serialization ISerializable eller [Serializable] DataContractSerializer eller XmlSerializer
Objekt som skickats Eftervärde eller bireferens Endast eftervärde
Fel/undantag Eventuella serialiserbara undantag FaultContract<TDetail>
Klientproxyobjekt Starkt inskrivna transparenta proxyservrar skapas automatiskt från MarshalByRefObjects Starkt skrivna proxyservrar genereras på begäran med ChannelFactory<TChannel>
Plattform krävs Både klient och server måste använda Microsoft OS och .NET Flera plattformar
Meddelandeformat Privat Branschstandarder (till exempel SOAP och WS-*)

Jämförelse av serverimplementering

Skapa en server i .NET-fjärrkommunikation

Servertyper för .NET-fjärrkommunikation måste härledas från MarshalByRefObject och definiera metoder som klienten kan anropa, till exempel följande:

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

Offentliga metoder av den här servertypen blir det offentliga kontraktet som är tillgängligt för klienter. Det finns ingen separation mellan serverns offentliga gränssnitt och dess implementering – en typ hanterar båda.

När servertypen har definierats kan den göras tillgänglig för klienter, till exempel i följande exempel:

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

Det finns många sätt att göra fjärrkommunikationstypen tillgänglig som en server, inklusive att använda konfigurationsfiler. Det här är bara ett exempel.

Skapa en server i WCF

Motsvarande steg i WCF innebär att skapa två typer – det offentliga "tjänstkontraktet" och den konkreta implementeringen. Den första deklareras som ett gränssnitt markerat med [ServiceContract]. Metoder som är tillgängliga för klienter markeras med [OperationContract]:

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

Serverns implementering definieras i en separat betongklass, som i följande exempel:

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

När dessa typer har definierats kan WCF-servern göras tillgänglig för klienter, till exempel i följande exempel:

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

Kommentar

TCP används i båda exemplen för att hålla dem så lika som möjligt. Se scenariots genomgångar senare i det här avsnittet för exempel som använder HTTP.

Det finns många sätt att konfigurera och vara värd för WCF-tjänster. Det här är bara ett exempel som kallas "lokalt installerad". Mer information finns i följande avsnitt:

Jämförelse av klientimplementering

Skapa en klient i .NET-fjärrkommunikation

När ett .NET-fjärrkommunikationsserverobjekt har gjorts tillgängligt kan det användas av klienter, som i följande exempel:

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-instansen som returneras från Activator.GetObject() kallas för en "transparent proxy". Den implementerar det offentliga API:et för RemotingServer-typen på klienten, men alla metoder anropar serverobjektet som körs i en annan process eller dator.

Skapa en klient i WCF

Motsvarande steg i WCF innebär att använda en kanalfabrik för att skapa proxyn explicit. Precis som fjärrkommunikation kan proxyobjektet användas för att anropa åtgärder på servern, som i följande exempel:

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

Det här exemplet visar programmering på kanalnivå eftersom det mest liknar fjärrkommunikationsexemplet. Dessutom finns metoden Lägg till tjänstreferens i Visual Studio som genererar kod för att förenkla klientprogrammering. Mer information finns i följande avsnitt:

Serialiseringsanvändning

Både .NET Remoting och WCF använder serialisering för att skicka objekt mellan klient och server, men de skiljer sig åt på följande viktiga sätt:

  1. De använder olika serialiserare och konventioner för att ange vad som ska serialiseras.

  2. .NET-fjärrkommunikation stöder serialisering med "efter referens" som gör att metod- eller egenskapsåtkomst på en nivå kan köra kod på den andra nivån, som ligger över säkerhetsgränser. Den här funktionen exponerar säkerhetsrisker och är en av de främsta orsakerna till att fjärrkommunikationsslutpunkter aldrig ska exponeras för ej betrodda klienter.

  3. Serialisering som används av fjärrkommunikation är opt-out (undanta uttryckligen vad som inte ska serialiseras) och WCF-serialisering är opt-in (markera uttryckligen vilka medlemmar som ska serialiseras).

Serialisering i .NET-fjärrkommunikation

.NET-fjärrkommunikation stöder två sätt att serialisera och deserialisera objekt mellan klienten och servern:

  • Efter värde – objektets värden serialiseras över nivågränser och en ny instans av objektet skapas på den andra nivån. Alla anrop till metoder eller egenskaper för den nya instansen körs endast lokalt och påverkar inte det ursprungliga objektet eller nivån.

  • Som referens – en särskild "objektreferens" serialiseras över nivågränser. När en nivå interagerar med metoder eller egenskaper för objektet kommunicerar den tillbaka till det ursprungliga objektet på den ursprungliga nivån. Bireferensobjekt kan flöda i endera riktningen – server till klient eller klient till server.

Eftervärdestyper i fjärrkommunikation markeras med attributet [Serializable] eller implementerar ISerializable, som i följande exempel:

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

Efterreferenstyper härleds från klassen MarshalByRefObject, som i följande exempel:

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

Det är oerhört viktigt att förstå konsekvenserna av fjärrkommunikationens bireferensobjekt. Om antingen nivån (klienten eller servern) skickar ett bireferensobjekt till den andra nivån körs alla metodanrop tillbaka på den nivå som äger objektet. Till exempel kör en klient anropande metoder på ett bireferensobjekt som returneras av servern kod på servern. På samma sätt kör en server som anropar metoder på ett bireferensobjekt som tillhandahålls av klienten tillbaka kod på klienten. Därför rekommenderas användning av .NET-fjärrkommunikation endast i fullständigt betrodda miljöer. Om du exponerar en offentlig .NET-fjärrkommunikationsslutpunkt för ej betrodda klienter blir en fjärrkommunikationsserver sårbar för angrepp.

Serialisering i WCF

WCF stöder endast serialisering efter värde. Det vanligaste sättet att definiera en typ att byta mellan klient och server är som i följande exempel:

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

Attributet [DataContract] identifierar den här typen som en som kan serialiseras och deserialiseras mellan klient och server. Attributet [DataMember] identifierar de enskilda egenskaper eller fält som ska serialiseras.

När WCF skickar ett objekt över nivåer serialiserar det endast värdena och skapar en ny instans av objektet på den andra nivån. Alla interaktioner med objektets värden sker endast lokalt – de kommunicerar inte med den andra nivån på samma sätt som .NET-fjärrkommunikationsobjekt gör. Mer information finns i Serialisering och deserialisering.

Funktioner för undantagshantering

Undantag i .NET-fjärrkommunikation

Undantag som genereras av en fjärrkommunikationsserver serialiseras, skickas till klienten och genereras lokalt på klienten som andra undantag. Anpassade undantag kan skapas genom att underklassa undantagstypen och markera den med [Serializable]. De flesta ramverksfel är redan markerade på det här sättet, vilket gör att de flesta kan genereras av servern, serialiseras och genereras på nytt på klienten. Även om den här designen är praktisk under utvecklingen kan information på serversidan oavsiktligt lämnas ut till klienten. Detta är en av många orsaker till att fjärrkommunikation endast ska användas i fullständigt betrodda miljöer.

Undantag och fel i WCF

WCF tillåter inte att godtyckliga undantagstyper returneras från servern till klienten eftersom det kan leda till oavsiktligt avslöjande av information. Om en tjänståtgärd utlöser ett oväntat undantag utlöses en allmän felundantagsåtgärd för klienten. Det här undantaget innehåller ingen information om varför eller var problemet uppstod, och för vissa program räcker det. Program som behöver förmedla mer detaljerad felinformation till klienten gör detta genom att definiera ett felkontrakt.

För att göra detta skapar du först en [DataContract]-typ för att bära felinformationen.

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

Ange det felkontrakt som ska användas för varje tjänståtgärd.

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

Servern rapporterar feltillstånd genom att utlösa en FaultException.

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

Och när klienten skickar en begäran till servern kan den fånga fel som normala undantag.

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

Mer information om felkontrakt finns i FaultException.

Säkerhetsöverväganden

Säkerhet i .NET-fjärrkommunikation

Vissa .NET-fjärrkommunikationskanaler stöder säkerhetsfunktioner som autentisering och kryptering på kanallagret (IPC och TCP). HTTP-kanalen förlitar sig på IIS (Internet Information Services) för både autentisering och kryptering. Trots det här stödet bör du överväga att använda .NET Remoting som ett osäkert kommunikationsprotokoll och endast använda det i fullständigt betrodda miljöer. Exponera aldrig en offentlig fjärrkommunikationsslutpunkt för Internet eller ej betrodda klienter.

Säkerhet i WCF

WCF utformades med säkerhet i åtanke, delvis för att hantera de typer av sårbarheter som finns i .NET Remoting. WCF erbjuder säkerhet på både transport- och meddelandenivå och erbjuder många alternativ för autentisering, auktorisering, kryptering och så vidare. Mer information finns i följande avsnitt:

Migrera till WCF

Varför migrera från fjärrkommunikation till WCF?

  • .NET-fjärrkommunikation är en äldre produkt. Som beskrivs i .NET-fjärrkommunikation anses den vara en äldre produkt och rekommenderas inte för ny utveckling. WCF eller ASP.NET webb-API rekommenderas för nya och befintliga program.

  • WCF använder plattformsoberoende standarder. WCF har utformats med plattformsoberoende samverkan i åtanke och stöder många branschstandarder (SOAP, WS-Security, WS-Trust osv.). En WCF-tjänst kan samverka med klienter som körs på andra operativsystem än Windows. Fjärrkommunikation utformades främst för miljöer där både server- och klientprogram körs med hjälp av .NET Framework på ett Windows-operativsystem.

  • WCF har inbyggd säkerhet. WCF har utformats med säkerhet i åtanke och erbjuder många alternativ för autentisering, säkerhet på transportnivå, säkerhet på meddelandenivå osv. Fjärrkommunikation har utformats för att göra det enkelt för program att samverka men har inte utformats för att vara säkra i icke-betrodda miljöer. WCF har utformats för att fungera i både betrodda och icke-betrodda miljöer.

Migrering Rekommendationer

Följande är de rekommenderade stegen för att migrera från .NET-fjärrkommunikation till WCF:

  • Skapa tjänstkontraktet. Definiera dina typer av tjänstgränssnitt och markera dem med attributet [ServiceContract]. Markera alla metoder som klienterna får anropa med [OperationContract].

  • Skapa datakontraktet. Definiera de datatyper som ska utbytas mellan server och klient och markera dem med attributet [DataContract]. Markera alla fält och egenskaper som klienten får använda med [DataMember].

  • Skapa felkontraktet (valfritt). Skapa de typer som ska utbytas mellan server och klient när fel påträffas. Markera dessa typer med [DataContract] och [DataMember] för att göra dem serialiserbara. För alla tjänståtgärder som du har markerat med [OperationContract] markerar du dem också med [FaultContract] för att ange vilka fel de kan returnera.

  • Konfigurera och vara värd för tjänsten. När tjänstkontraktet har skapats är nästa steg att konfigurera en bindning för att exponera tjänsten vid en slutpunkt. Mer information finns i Slutpunkter: Adresser, bindningar och kontrakt.

När ett fjärrkommunikationsprogram har migrerats till WCF är det fortfarande viktigt att ta bort beroenden för .NET-fjärrkommunikation. Detta säkerställer att eventuella säkerhetsrisker för fjärrkommunikation tas bort från programmet. De här stegen omfattar följande:

  • Avbryt användningen av MarshalByRefObject. Typen MarshalByRefObject finns bara för fjärrkommunikation och används inte av WCF. Alla programtyper som underklassen MarshalByRefObject ska tas bort eller ändras.

  • Avbryt användningen av [Serializable] och ISerializable. Attributet [Serializable] och ISerializable-gränssnittet utformades ursprungligen för att serialisera typer i betrodda miljöer, och de används av fjärrkommunikation. WCF-serialisering är beroende av att typer markeras med [DataContract] och [DataMember]. Datatyper som används av ett program bör ändras för att använda [DataContract] och inte använda ISerializable eller [Serializable].

Migreringsscenarier

Nu ska vi se hur du utför följande vanliga fjärrkommunikationsscenarier i WCF:

  1. Servern returnerar ett objekt efter värde till klienten

  2. Servern returnerar ett objekt med referens till klienten

  3. Klienten skickar ett objekt efter värde till servern

Kommentar

Det är inte tillåtet att skicka ett objekt med referens från klienten till servern i WCF.

När du läser igenom dessa scenarier antar du att våra baslinjegränssnitt för .NET-fjärrkommunikation ser ut som i följande exempel. Implementeringen av .NET-fjärrkommunikation är inte viktig här eftersom vi bara vill illustrera hur du använder WCF för att implementera motsvarande funktioner.

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

Scenario 1: Tjänsten returnerar ett objekt efter värde

Det här scenariot visar en server som returnerar ett objekt till klienten efter värde. WCF returnerar alltid objekt från servern efter värde, så följande steg beskriver helt enkelt hur du skapar en vanlig WCF-tjänst.

  1. Börja med att definiera ett offentligt gränssnitt för WCF-tjänsten och markera det med attributet [ServiceContract]. Vi använder [OperationContract] för att identifiera de metoder på serversidan som vår klient anropar.

    [ServiceContract]  
    public interface ICustomerService  
    {  
        [OperationContract]  
        Customer GetCustomer(int customerId);  
    
        [OperationContract]  
        bool UpdateCustomer(Customer customer);  
    }  
    
  2. Nästa steg är att skapa datakontraktet för den här tjänsten. Vi gör detta genom att skapa klasser (inte gränssnitt) markerade med attributet [DataContract]. De enskilda egenskaper eller fält som vi vill ska vara synliga för både klienten och servern är markerade med [DataMember]. Om vi vill att härledda typer ska tillåtas måste vi använda attributet [KnownType] för att identifiera dem. De enda typer som WCF tillåter kan serialiseras eller deserialiseras för den här tjänsten är de i tjänstgränssnittet och dessa "kända typer". Försök att byta ut någon annan typ som inte finns i den här listan kommer att avvisas.

    [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. Därefter tillhandahåller vi implementeringen för tjänstgränssnittet.

    public class CustomerService : ICustomerService  
    {  
        public Customer GetCustomer(int customerId)  
        {  
            // read from database  
        }  
    
        public bool UpdateCustomer(Customer customer)  
        {  
            // write to database  
        }  
    }  
    
  4. För att kunna köra WCF-tjänsten måste vi deklarera en slutpunkt som exponerar tjänstgränssnittet på en specifik URL med hjälp av en specifik WCF-bindning. Detta görs vanligtvis genom att lägga till följande avsnitt i serverprojektets web.config-fil.

    <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-tjänsten kan sedan startas med följande kod:

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

    När den här ServiceHost startas använder den filen web.config för att upprätta rätt kontrakt, bindning och slutpunkt. Mer information om konfigurationsfiler finns i Konfigurera tjänster med hjälp av konfigurationsfiler. Det här formatet för att starta servern kallas självvärd. Mer information om andra alternativ för att vara värd för WCF-tjänster finns i Värdtjänster.

  6. Klientprojektets app.config måste deklarera matchande bindningsinformation för tjänstens slutpunkt. Det enklaste sättet att göra detta i Visual Studio är att använda Lägg till tjänstreferens, som automatiskt uppdaterar filen app.config. Du kan också lägga till samma ändringar manuellt.

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

    Mer information om hur du använder Lägg till tjänstreferens finns i Så här lägger du till, uppdaterar eller tar bort en tjänstreferens.

  7. Nu kan vi anropa WCF-tjänsten från klienten. Vi gör detta genom att skapa en kanalfabrik för den tjänsten, be den om en kanal och direkt anropa den metod som vi vill använda i kanalen. Vi kan göra detta eftersom kanalen implementerar tjänstens gränssnitt och hanterar den underliggande logiken för begäran/svar åt oss. Returvärdet från metodanropet är den deserialiserade kopian av serverns svar.

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

Objekt som returneras av WCF från servern till klienten är alltid av värde. Objekten är deserialiserade kopior av data som skickas av servern. Klienten kan anropa metoder på dessa lokala kopior utan risk för att anropa serverkod via återanrop.

Scenario 2: Servern returnerar ett objekt efter referens

Det här scenariot visar servern som tillhandahåller ett objekt till klienten med referens. I .NET-fjärrkommunikation hanteras detta automatiskt för alla typer som härleds från MarshalByRefObject, som serialiseras med referens. Ett exempel på det här scenariot är att tillåta att flera klienter har oberoende sessionskänsliga objekt på serversidan. Som tidigare nämnts är objekt som returneras av en WCF-tjänst alltid efter värde, så det finns ingen direkt motsvarighet till ett bireferensobjekt, men det är möjligt att uppnå något som liknar bireferenssemantik med hjälp av ett EndpointAddress10 objekt. Det här är ett serialiserbart by-value-objekt som kan användas av klienten för att hämta ett sessionful by-reference-objekt på servern. Detta möjliggör scenariot att ha flera klienter med oberoende sessionskänsliga objekt på serversidan.

  1. Först måste vi definiera ett WCF-tjänstkontrakt som motsvarar själva sessionsobjektet.

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

    Dricks

    Observera att det sessionskänsliga objektet är markerat med [ServiceContract], vilket gör det till ett normalt WCF-tjänstgränssnitt. Om du anger egenskapen SessionMode blir det en sessionskänslig tjänst. I WCF är en session ett sätt att korrelera flera meddelanden som skickas mellan två slutpunkter. Det innebär att när en klient får en anslutning till den här tjänsten upprättas en session mellan klienten och servern. Klienten använder en enda unik instans av objektet på serversidan för alla dess interaktioner i den här sessionen.

  2. Därefter måste vi tillhandahålla implementeringen av det här tjänstgränssnittet. Genom att ange den med [ServiceBehavior] och ange InstanceContextMode meddelar vi WCF att vi vill använda en unik instans av den här typen för varje 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. Nu behöver vi ett sätt att hämta en instans av det här sessionskänsliga objektet. Det gör vi genom att skapa ett annat WCF-tjänstgränssnitt som returnerar ett EndpointAddress10-objekt. Det här är en serialiserbar form av en slutpunkt som klienten kan använda för att skapa det sessionskänsliga objektet.

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

    Och vi implementerar den här WCF-tjänsten:

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

    Den här implementeringen underhåller en singleton-kanalfabrik för att skapa sessionskänsliga objekt. När GetInstanceAddress() anropas skapar den en kanal och skapar ett EndpointAddress10-objekt som effektivt pekar på den fjärradress som är associerad med den här kanalen. EndpointAddress10 är helt enkelt en datatyp som kan returneras till klientens eftervärde.

  4. Vi måste ändra serverns konfigurationsfil genom att göra följande två saker som visas i exemplet nedan:

    1. Deklarera ett <klientavsnitt> som beskriver slutpunkten för det sessionskänsliga objektet. Detta är nödvändigt eftersom servern också fungerar som en klient i den här situationen.

    2. Deklarera slutpunkter för det fabriks- och sessionskänsliga objektet. Detta är nödvändigt för att klienten ska kunna kommunicera med tjänstslutpunkterna för att hämta EndpointAddress10 och skapa den sessionskänsliga kanalen.

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

    Och sedan kan vi starta dessa tjänster:

    ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory));  
    factoryHost.Open();  
    
    ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject));  
    sessionHost.Open();  
    
  5. Vi konfigurerar klienten genom att deklarera samma slutpunkter i projektets app.config-fil.

    <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. För att kunna skapa och använda det här sessionskänsliga objektet måste klienten utföra följande steg:

    1. Skapa en kanal till ISessionBoundFactory-tjänsten.

    2. Använd den kanalen för att anropa tjänsten för att hämta en EndpointAddress10.

    3. Använd EndpointAddress10 för att skapa en kanal för att hämta ett sessionskänsligt objekt.

    4. Interagera med det sessionskänsliga objektet för att visa att det förblir samma instans över flera anrop.

    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 returnerar alltid objekt efter värde, men det är möjligt att stödja motsvarigheten till bireferenssemantik med hjälp av EndpointAddress10. Detta gör att klienten kan begära en sessionskänslig WCF-tjänstinstans, varefter den kan interagera med den som alla andra WCF-tjänster.

Scenario 3: Klienten skickar en instans av ett värde till servern

Det här scenariot visar klienten som skickar en icke-primitiv objektinstans till servern efter värde. Eftersom WCF endast skickar objekt efter värde visar det här scenariot normal WCF-användning.

  1. Använd samma WCF-tjänst från scenario 1.

  2. Använd klienten för att skapa ett nytt eftervärdesobjekt (kund), skapa en kanal för att kommunicera med ICustomerService-tjänsten och skicka objektet till den.

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

    Kundobjektet serialiseras och skickas till servern, där det deserialiseras till en ny kopia av objektet.

    Kommentar

    Den här koden visar också hur du skickar en härledd typ (PremiumCustomer). Tjänstgränssnittet förväntar sig ett kundobjekt, men attributet [KnownType] i kundklassen angav att PremiumCustomer också var tillåtet. WCF misslyckas med att serialisera eller deserialisera någon annan typ via det här tjänstgränssnittet.

Normalt WCF-utbyte av data är efter värde. Detta garanterar att anropa metoder på ett av dessa dataobjekt endast körs lokalt – det anropar inte kod på den andra nivån. Även om det är möjligt att uppnå något som liknar bireferensobjekt som returneras från servern, är det inte möjligt för en klient att skicka ett by-reference-objekt till servern. Ett scenario som kräver en konversation fram och tillbaka mellan klient och server kan uppnås i WCF med hjälp av en duplex-tjänst. Mer information finns i Duplex Services.

Sammanfattning

.NET-fjärrkommunikation är ett kommunikationsramverk som endast är avsett att användas i fullständigt betrodda miljöer. Det är en äldre produkt som endast stöds för bakåtkompatibilitet. Det bör inte användas för att skapa nya program. Omvänt har WCF utformats med säkerhet i åtanke och rekommenderas för nya och befintliga program. Microsoft rekommenderar att befintliga fjärrkommunikationsprogram migreras för att använda WCF eller ASP.NET webb-API i stället.