Migreren van .NET Remoting naar WCF
In dit artikel wordt beschreven hoe u een toepassing migreert die gebruikmaakt van .NET Remoting om WCF (Windows Communication Foundation) te gebruiken. Het vergelijkt vergelijkbare concepten tussen deze producten en beschrijft vervolgens hoe u verschillende algemene externe scenario's in WCF kunt uitvoeren.
.NET Remoting is een verouderd product dat alleen wordt ondersteund voor achterwaartse compatibiliteit. Het is niet veilig in omgevingen met gemengde vertrouwensrelaties, omdat het de afzonderlijke vertrouwensniveaus tussen client en server niet kan onderhouden. U moet bijvoorbeeld nooit een .NET Remoting-eindpunt beschikbaar maken voor internet of voor niet-vertrouwde clients. We raden bestaande externe toepassingen aan te migreren naar nieuwere en veiligere technologieën. Als het ontwerp van de toepassing alleen HTTP gebruikt en RESTful is, raden we ASP.NET web-API aan. Zie ASP.NET Web-API voor meer informatie. Als de toepassing is gebaseerd op SOAP of niet-Http-protocollen zoals TCP vereist, raden we WCF aan.
Externe communicatie van .NET vergelijken met WCF
In deze sectie worden de basisbouwstenen van .NET Remoting vergeleken met hun WCF-equivalenten. We gebruiken deze bouwstenen later om enkele algemene clientserverscenario's in WCF te maken. De volgende grafiek bevat een overzicht van de belangrijkste overeenkomsten en verschillen tussen .NET Remoting en WCF.
Externe communicatie van .NET | WCF | |
---|---|---|
Servertype | Subklasse MarshalByRefObject |
Markeren met [ServiceContract] kenmerk |
Servicebewerkingen | Openbare methoden op servertype | Markeren met [OperationContract] kenmerk |
Serialization | ISerializable of [Serializable] |
DataContractSerializer of XmlSerializer |
Objecten doorgegeven | By-value of by-reference | Alleen op waarde |
Fouten/uitzonderingen | Eventuele serialiseerbare uitzondering | FaultContract<TDetail> |
Clientproxyobjecten | Sterk getypte transparante proxy's worden automatisch gemaakt op basis van MarshalByRefObjects | Sterk getypte proxy's worden op aanvraag gegenereerd met behulp van ChannelFactory<TChannel> |
Platform vereist | Zowel client als server moeten Microsoft OS en .NET gebruiken | Platformonafhankelijk |
Berichtindeling | Privé | Industriestandaarden (bijvoorbeeld SOAP en WS-*) |
Vergelijking van server-implementatie
Een server maken in .NET Remoting
.NET Externe servertypen moeten zijn afgeleid van MarshalByRefObject en methoden definiëren die de client kan aanroepen, zoals de volgende:
public class RemotingServer : MarshalByRefObject
{
public Customer GetCustomer(int customerId) { … }
}
De openbare methoden van dit servertype worden het openbare contract dat beschikbaar is voor clients. Er is geen scheiding tussen de openbare interface van de server en de implementatie. Eén type verwerkt beide.
Zodra het servertype is gedefinieerd, kan het beschikbaar worden gesteld aan clients, zoals in het volgende voorbeeld:
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();
Er zijn veel manieren om het externe type beschikbaar te maken als een server, waaronder het gebruik van configuratiebestanden. Dit is slechts één voorbeeld.
Een server maken in WCF
De equivalente stap in WCF omvat het maken van twee typen: het openbare "servicecontract" en de concrete implementatie. De eerste wordt gedeclareerd als een interface die is gemarkeerd met [ServiceContract]. Methoden die beschikbaar zijn voor clients worden gemarkeerd met [OperationContract]:
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
Customer GetCustomer(int customerId);
}
De implementatie van de server wordt gedefinieerd in een afzonderlijke concrete klasse, zoals in het volgende voorbeeld:
public class WCFServer : IWCFServer
{
public Customer GetCustomer(int customerId) { … }
}
Zodra deze typen zijn gedefinieerd, kan de WCF-server beschikbaar worden gesteld aan clients, zoals in het volgende voorbeeld:
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();
}
Notitie
TCP wordt in beide voorbeelden gebruikt om ze zo vergelijkbaar mogelijk te houden. Raadpleeg het scenario-overzicht verderop in dit onderwerp voor voorbeelden met behulp van HTTP.
Er zijn veel manieren om WCF-services te configureren en te hosten. Dit is slechts één voorbeeld, ook wel 'zelf-hostend'. Zie de volgende onderwerpen voor meer informatie:
Vergelijking van client-implementatie
Een client maken in .NET Remoting
Zodra een .NET Remoting-serverobject beschikbaar is gemaakt, kan het worden gebruikt door clients, zoals in het volgende voorbeeld:
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.");
Het RemotingServer-exemplaar dat wordt geretourneerd door Activator.GetObject() wordt een 'transparante proxy' genoemd. Het implementeert de openbare API voor het type RemotingServer op de client, maar alle methoden roepen het serverobject aan dat wordt uitgevoerd in een ander proces of computer.
Een client maken in WCF
De equivalente stap in WCF omvat het gebruik van een kanaalfactory om de proxy expliciet te maken. Net als externe toegang kan het proxyobject worden gebruikt om bewerkingen op de server aan te roepen, zoals in het volgende voorbeeld:
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.");
In dit voorbeeld ziet u programmeren op kanaalniveau, omdat dit het meest lijkt op het voorbeeld van externe communicatie. Ook beschikbaar is de benadering Servicereferentie toevoegen in Visual Studio waarmee code wordt gegenereerd om clientprogrammering te vereenvoudigen. Zie de volgende onderwerpen voor meer informatie:
Serialisatiegebruik
Zowel externe communicatie van .NET als WCF maken gebruik van serialisatie om objecten tussen client en server te verzenden, maar ze verschillen op deze belangrijke manieren:
Ze gebruiken verschillende serializers en conventies om aan te geven wat er moet worden geserialiseerd.
.NET Remoting ondersteunt serialisatie 'by reference' waarmee methode- of eigenschapstoegang op de ene laag code kan worden uitgevoerd op de andere laag, die zich buiten de beveiligingsgrenzen bevindt. Deze mogelijkheid stelt beveiligingsproblemen bloot en is een van de belangrijkste redenen waarom externe eindpunten nooit zichtbaar moeten worden gemaakt voor niet-vertrouwde clients.
Serialisatie die wordt gebruikt door Remoting is opt-out (expliciet uitsluiten wat niet moet worden geserialiseerd) en WCF-serialisatie is opt-in (expliciet markeren welke leden moeten worden geserialiseerd).
Serialisatie in .NET Remoting
.NET Remoting ondersteunt twee manieren om objecten tussen de client en server te serialiseren en deserialiseren:
Op waarde : de waarden van het object worden geserialiseerd over de grenzen van de lagen en er wordt een nieuw exemplaar van dat object gemaakt op de andere laag. Aanroepen naar methoden of eigenschappen van dat nieuwe exemplaar worden alleen lokaal uitgevoerd en hebben geen invloed op het oorspronkelijke object of de oorspronkelijke laag.
Ter referentie : een speciale 'objectverwijzing' wordt geserialiseerd over de grenzen van de laag. Wanneer één laag communiceert met methoden of eigenschappen van dat object, communiceert deze terug naar het oorspronkelijke object op de oorspronkelijke laag. By-reference-objecten kunnen in beide richtingen stromen: server naar client of client naar server.
By-value types in Remoting zijn gemarkeerd met het kenmerk [Serializable] of implementeer ISerializable, zoals in het volgende voorbeeld:
[Serializable]
public class RemotingCustomer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
By-reference types zijn afgeleid van de klasse MarshalByRefObject, zoals in het volgende voorbeeld:
public class RemotingCustomerReference : MarshalByRefObject
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerId { get; set; }
}
Het is uiterst belangrijk om inzicht te krijgen in de implicaties van externe objecten van Externe toegang. Als een laag (client of server) een by-reference-object naar de andere laag verzendt, worden alle methode-aanroepen uitgevoerd op de laag die eigenaar is van het object. Een client die methoden aanroept op een by-reference-object dat door de server wordt geretourneerd, voert bijvoorbeeld code uit op de server. Op dezelfde manier voert een server die methoden aanroept voor een by-reference-object van de client code terug op de client uit. Daarom wordt het gebruik van .NET Remoting alleen aanbevolen in volledig vertrouwde omgevingen. Als u een openbaar .NET Remoting-eindpunt beschikbaar maakt voor niet-vertrouwde clients, wordt een externe server kwetsbaar voor aanvallen.
Serialisatie in WCF
WCF ondersteunt alleen serialisatie per waarde. De meest voorkomende manier om een type te definiëren dat moet worden uitgewisseld tussen client en server, is zoals in het volgende voorbeeld:
[DataContract]
public class WCFCustomer
{
[DataMember]
public string FirstName { get; set; }
[DataMember]
public string LastName { get; set; }
[DataMember]
public int CustomerId { get; set; }
}
Het kenmerk [DataContract] identificeert dit type als een kenmerk dat kan worden geserialiseerd en gedeserialiseerd tussen de client en de server. Het kenmerk [DataMember] identificeert de afzonderlijke eigenschappen of velden die moeten worden geserialiseerd.
Wanneer WCF een object naar meerdere lagen verzendt, worden alleen de waarden geserialiseerd en wordt een nieuw exemplaar van het object op de andere laag gemaakt. Interacties met de waarden van het object vinden alleen lokaal plaats. Ze communiceren niet met de andere laag zoals .NET Remoting by-reference-objecten. Zie Serialisatie en deserialisatie voor meer informatie.
Mogelijkheden voor afhandeling van uitzonderingen
Uitzonderingen in .NET Remoting
Uitzonderingen die worden gegenereerd door een externe server, worden geserialiseerd, naar de client verzonden en lokaal op de client gegenereerd, net als elke andere uitzondering. Aangepaste uitzonderingen kunnen worden gemaakt door het uitzonderingstype te subklasseren en te markeren met [Serializable]. De meeste frameworkuitzonderingen zijn al op deze manier gemarkeerd, zodat de meeste worden gegenereerd door de server, geserialiseerd en opnieuw worden gegenereerd op de client. Hoewel dit ontwerp handig is tijdens de ontwikkeling, kan informatie aan de serverzijde per ongeluk worden vrijgegeven aan de client. Dit is een van de vele redenen waarom externe communicatie alleen in volledig vertrouwde omgevingen moet worden gebruikt.
Uitzonderingen en fouten in WCF
WCF staat niet toe dat willekeurige uitzonderingstypen van de server naar de client worden geretourneerd omdat dit kan leiden tot onbedoelde openbaarmaking van informatie. Als een servicebewerking een onverwachte uitzondering genereert, wordt er een Foutuitzondering voor algemeen gebruik op de client gegenereerd. Deze uitzondering bevat geen informatie waarom of waar het probleem is opgetreden. Voor sommige toepassingen is dit voldoende. Toepassingen die uitgebreidere foutinformatie aan de client moeten doorgeven door een foutcontract te definiëren.
Hiervoor maakt u eerst het type [DataContract] om de foutgegevens op te nemen.
[DataContract]
public class CustomerServiceFault
{
[DataMember]
public string ErrorMessage { get; set; }
[DataMember]
public int CustomerId {get;set;}
}
Geef het foutcontract op dat moet worden gebruikt voor elke servicebewerking.
[ServiceContract]
public interface IWCFServer
{
[OperationContract]
[FaultContract(typeof(CustomerServiceFault))]
Customer GetCustomer(int customerId);
}
De server rapporteert foutvoorwaarden door een FaultException te genereren.
throw new FaultException<CustomerServiceFault>(
new CustomerServiceFault() {
CustomerId = customerId,
ErrorMessage = "Illegal customer Id"
});
En wanneer de client een aanvraag naar de server indient, kan deze fouten als normale uitzonderingen ondervangen.
try
{
Customer customer = server.GetCustomer(-1);
}
catch (FaultException<CustomerServiceFault> fault)
{
Console.WriteLine($"Fault received: {fault.Detail.ErrorMessage}");
}
Zie voor meer informatie over foutcontracten FaultException.
Beveiligingsoverwegingen
Beveiliging in .NET Remoting
Sommige .NET Remoting-kanalen ondersteunen beveiligingsfuncties zoals verificatie en versleuteling op de kanaallaag (IPC en TCP). Het HTTP-kanaal is afhankelijk van IIS (Internet Information Services) voor zowel verificatie als versleuteling. Ondanks deze ondersteuning moet u overwegen om een onbeveiligd communicatieprotocol te verwijderen en dit alleen in volledig vertrouwde omgevingen te gebruiken. Maak nooit een openbaar remoting-eindpunt beschikbaar voor internet of niet-vertrouwde clients.
Beveiliging in WCF
WCF is ontworpen met het oog op beveiliging, deels om de soorten beveiligingsproblemen in .NET Remoting aan te pakken. WCF biedt beveiliging op zowel transport- als berichtniveau en biedt veel opties voor verificatie, autorisatie, versleuteling, enzovoort. Zie de volgende onderwerpen voor meer informatie:
Migreren naar WCF
Waarom migreren van externe toegang naar WCF?
.NET Remoting is een verouderd product. Zoals beschreven in .NET Remoting, wordt het beschouwd als een verouderd product en wordt het niet aanbevolen voor nieuwe ontwikkeling. WCF of ASP.NET Web-API wordt aanbevolen voor nieuwe en bestaande toepassingen.
WCF maakt gebruik van platformoverschrijdende standaarden. WCF is ontworpen met platformoverschrijdende interoperabiliteit in gedachten en ondersteunt veel industriestandaarden (SOAP, WS-Security, WS-Trust, enzovoort). Een WCF-service kan samenwerken met clients die worden uitgevoerd op andere besturingssystemen dan Windows. Externe communicatie is voornamelijk ontworpen voor omgevingen waar zowel de server- als clienttoepassingen worden uitgevoerd met behulp van .NET Framework op een Windows-besturingssysteem.
WCF heeft ingebouwde beveiliging. WCF is ontworpen met de beveiliging in het achterhoofd en biedt veel opties voor verificatie, transportniveaubeveiliging, berichtniveaubeveiliging, enzovoort. Externe communicatie is ontworpen om het gemakkelijk te maken voor toepassingen om te werken, maar is niet ontworpen om veilig te zijn in niet-vertrouwde omgevingen. WCF is ontworpen om te werken in zowel vertrouwde als niet-vertrouwde omgevingen.
Migratie Aanbevelingen
Hier volgen de aanbevolen stappen voor het migreren van .NET Remoting naar WCF:
Maak het servicecontract. Definieer de typen service-interface en markeer deze met het kenmerk [ServiceContract]. Markeer alle methoden die de clients mogen aanroepen met [OperationContract].
Maak het gegevenscontract. Definieer de gegevenstypen die worden uitgewisseld tussen de server en de client en markeer deze met het kenmerk [DataContract].. Markeer alle velden en eigenschappen die de client mag gebruiken met [DataMember].
Maak het foutcontract (optioneel). Maak de typen die worden uitgewisseld tussen server en client wanneer er fouten optreden. Markeer deze typen met [DataContract] en [DataMember] om ze serialiseerbaar te maken. Voor alle servicebewerkingen die u hebt gemarkeerd met [OperationContract], markeert u deze ook met [FaultContract] om aan te geven welke fouten ze kunnen retourneren.
Configureer en host de service. Zodra het servicecontract is gemaakt, is de volgende stap het configureren van een binding om de service beschikbaar te maken op een eindpunt. Zie Eindpunten: Adressen, Bindingen en Contracten voor meer informatie.
Zodra een externe toepassing is gemigreerd naar WCF, is het nog steeds belangrijk om afhankelijkheden van .NET Remoting te verwijderen. Dit zorgt ervoor dat eventuele beveiligingsproblemen met externe toegang worden verwijderd uit de toepassing. Deze stappen omvatten het volgende:
Stop het gebruik van MarshalByRefObject. Het type MarshalByRefObject bestaat alleen voor externe toegang en wordt niet gebruikt door WCF. Alle toepassingstypen die marshalByRefObject subklassen bevatten, moeten worden verwijderd of gewijzigd.
Stop het gebruik van [Serializable] en ISerializable. Het kenmerk [Serializable] en de ISerializable interface zijn oorspronkelijk ontworpen voor het serialiseren van typen binnen vertrouwde omgevingen, en ze worden gebruikt door Remoting. WCF-serialisatie is afhankelijk van typen die worden gemarkeerd met [DataContract] en [DataMember]. Gegevenstypen die door een toepassing worden gebruikt, moeten worden gewijzigd om [DataContract] te gebruiken en niet om ISerializable of [Serializable] te gebruiken.
Migratiescenario's
Laten we nu eens kijken hoe u de volgende algemene externe scenario's in WCF kunt uitvoeren:
Server retourneert een object op waarde naar de client
Server retourneert een object per verwijzing naar de client
Client verzendt een object op waarde naar de server
Notitie
Het verzenden van een object by-reference van de client naar de server is niet toegestaan in WCF.
Bij het lezen van deze scenario's wordt ervan uitgegaan dat onze basisinterfaces voor .NET Remoting eruitzien als in het volgende voorbeeld. De .NET Remoting-implementatie is hier niet belangrijk, omdat we alleen willen illustreren hoe we WCF gebruiken om equivalente functionaliteit te implementeren.
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: De service retourneert een object op waarde
In dit scenario ziet u hoe een server een object naar de client retourneert op waarde. WCF retourneert altijd objecten van de server op waarde, dus in de volgende stappen wordt eenvoudig beschreven hoe u een normale WCF-service bouwt.
Begin met het definiëren van een openbare interface voor de WCF-service en markeer deze met het kenmerk [ServiceContract].. We gebruiken [OperationContract] om de methoden aan de serverzijde te identificeren die door onze client worden aangeroepen.
[ServiceContract] public interface ICustomerService { [OperationContract] Customer GetCustomer(int customerId); [OperationContract] bool UpdateCustomer(Customer customer); }
De volgende stap is het maken van het gegevenscontract voor deze service. Dit doen we door klassen (geen interfaces) te maken die zijn gemarkeerd met het kenmerk [DataContract].. De afzonderlijke eigenschappen of velden die we zichtbaar willen maken voor zowel de client als de server, zijn gemarkeerd met [DataMember]. Als we willen dat afgeleide typen worden toegestaan, moeten we het kenmerk [KnownType] gebruiken om ze te identificeren. De enige typen WCF kunnen worden geserialiseerd of gedeserialiseerd voor deze service zijn die in de service-interface en deze 'bekende typen'. Het uitwisselen van een ander type dat niet in deze lijst voorkomt, wordt geweigerd.
[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; } }
Vervolgens bieden we de implementatie voor de service-interface.
public class CustomerService : ICustomerService { public Customer GetCustomer(int customerId) { // read from database } public bool UpdateCustomer(Customer customer) { // write to database } }
Als u de 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. Dit wordt meestal gedaan door de volgende secties toe te voegen aan het web.config-bestand van het serverproject.
<configuration> <system.serviceModel> <services> <service name="Server.CustomerService"> <endpoint address="http://localhost:8083/CustomerService" binding="basicHttpBinding" contract="Shared.ICustomerService" /> </service> </services> </system.serviceModel> </configuration>
De WCF-service kan vervolgens worden gestart met de volgende code:
ServiceHost customerServiceHost = new ServiceHost(typeof(CustomerService)); customerServiceHost.Open();
Wanneer deze ServiceHost wordt gestart, wordt het web.config-bestand gebruikt om het juiste contract, de juiste binding en het juiste eindpunt tot stand te brengen. Zie Services configureren met behulp van configuratiebestanden voor meer informatie over configuratiebestanden. Deze stijl van het starten van de server wordt ook wel zelfhosting genoemd. Zie Hostingservices voor meer informatie over andere opties voor het hosten van WCF-services.
De app.config van het clientproject moet overeenkomende bindingsgegevens declareren voor het eindpunt van de service. De eenvoudigste manier om dit te doen in Visual Studio is het gebruik van Add Service Reference, waarmee het bestand app.config automatisch wordt bijgewerkt. U kunt dezelfde wijzigingen ook handmatig toevoegen.
<configuration> <system.serviceModel> <client> <endpoint name="customerservice" address="http://localhost:8083/CustomerService" binding="basicHttpBinding" contract="Shared.ICustomerService"/> </client> </system.serviceModel> </configuration>
Zie Instructies voor het toevoegen, bijwerken of verwijderen van een servicereferentie voor meer informatie over het gebruik van servicereferenties.
Nu kunnen we de WCF-service aanroepen vanaf de client. We doen dit door een kanaalfactory voor die service te maken, het om een kanaal te vragen en de gewenste methode rechtstreeks op dat kanaal aan te roepen. We kunnen dit doen omdat het kanaal de interface van de service implementeert en de onderliggende aanvraag-/antwoordlogica voor ons verwerkt. De retourwaarde van die methodeaanroep is de gedeserialiseerde kopie van het antwoord van de server.
ChannelFactory<ICustomerService> factory = new ChannelFactory<ICustomerService>("customerservice"); ICustomerService service = factory.CreateChannel(); Customer customer = service.GetCustomer(42); Console.WriteLine($" Customer {customer.FirstName} {customer.LastName} received.");
Objecten die door WCF van de server naar de client worden geretourneerd, zijn altijd op waarde. De objecten zijn gedeserialiseerde kopieën van de gegevens die door de server worden verzonden. De client kan methoden aanroepen op deze lokale kopieën zonder gevaar voor het aanroepen van servercode via callbacks.
Scenario 2: Server retourneert een object op verwijzing
In dit scenario ziet u hoe de server een object aan de client levert op basis van referentie. In .NET Remoting wordt dit automatisch verwerkt voor elk type dat is afgeleid van MarshalByRefObject, dat per verwijzing wordt geserialiseerd. Een voorbeeld van dit scenario is dat meerdere clients onafhankelijke sessieful serverobjecten kunnen hebben. Zoals eerder vermeld, zijn objecten die worden geretourneerd door een WCF-service altijd op waarde, dus er is geen direct equivalent van een by-reference-object, maar het is mogelijk om iets te bereiken dat lijkt op by-reference semantiek met behulp van een EndpointAddress10 object. Dit is een serialiseerbaar by-value-object dat door de client kan worden gebruikt om een sessievol by-reference-object op de server te verkrijgen. Dit maakt het scenario mogelijk om meerdere clients met onafhankelijke sessieful serverobjecten te hebben.
Eerst moeten we een WCF-servicecontract definiëren dat overeenkomt met het sessieful object zelf.
[ServiceContract(SessionMode = SessionMode.Allowed)] public interface ISessionBoundObject { [OperationContract] string GetCurrentValue(); [OperationContract] void SetCurrentValue(string value); }
Tip
U ziet dat het sessieful object is gemarkeerd met [ServiceContract], waardoor het een normale WCF-service-interface is. Het instellen van de eigenschap SessionMode geeft aan dat het een sessieful service is. 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.
Vervolgens moeten we de implementatie van deze service-interface bieden. Door deze aan te geven met [ServiceBehavior] en de InstanceContextMode in te stellen, laten we WCF weten dat we voor elke sessie een uniek exemplaar van dit type willen gebruiken.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)] public class MySessionBoundObject : ISessionBoundObject { private string _value; public string GetCurrentValue() { return _value; } public void SetCurrentValue(string val) { _value = val; } }
We hebben nu een manier nodig om een exemplaar van dit sessieful object te verkrijgen. We doen dit door een andere WCF-serviceinterface te maken die een EndpointAddress10-object retourneert. Dit is een serialiseerbare vorm van een eindpunt dat de client kan gebruiken om het sessieful object te maken.
[ServiceContract] public interface ISessionBoundFactory { [OperationContract] EndpointAddress10 GetInstanceAddress(); }
En we implementeren deze WCF-service:
public class SessionBoundFactory : ISessionBoundFactory { public static ChannelFactory<ISessionBoundObject> _factory = new ChannelFactory<ISessionBoundObject>("sessionbound"); public SessionBoundFactory() { } public EndpointAddress10 GetInstanceAddress() { IClientChannel channel = (IClientChannel)_factory.CreateChannel(); return EndpointAddress10.FromEndpointAddress(channel.RemoteAddress); } }
Deze implementatie onderhoudt een singleton-kanaalfactory om sessieful objecten te maken. Wanneer GetInstanceAddress() wordt aangeroepen, wordt er een kanaal gemaakt en wordt een EndpointAddress10-object gemaakt dat effectief verwijst naar het externe adres dat aan dit kanaal is gekoppeld. EndpointAddress10 is gewoon een gegevenstype dat per waarde naar de client kan worden geretourneerd.
We moeten het configuratiebestand van de server wijzigen door de volgende twee dingen uit te voeren, zoals wordt weergegeven in het onderstaande voorbeeld:
Declareer een <clientsectie> die het eindpunt voor het sessieful object beschrijft. Dit is nodig omdat de server in deze situatie ook fungeert als een client.
Declareer eindpunten voor het factory- en sessionful-object. Dit is nodig om de client te laten communiceren met de service-eindpunten om het EndpointAddress10 te verkrijgen en het sessieful kanaal te maken.
<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>
En vervolgens kunnen we deze services starten:
ServiceHost factoryHost = new ServiceHost(typeof(SessionBoundFactory)); factoryHost.Open(); ServiceHost sessionHost = new ServiceHost(typeof(MySessionBoundObject)); sessionHost.Open();
We configureren de client door dezelfde eindpunten in het bestand app.config van het project te declareren.
<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>
Als u dit sessieful object wilt maken en gebruiken, moet de client de volgende stappen uitvoeren:
Maak een kanaal naar de ISessionBoundFactory-service.
Gebruik dat kanaal om die service aan te roepen om een EndpointAddress10 te verkrijgen.
Gebruik EndpointAddress10 om een kanaal te maken om een sessionful object te verkrijgen.
Interactie met het sessieful object om aan te tonen dat het hetzelfde exemplaar blijft voor meerdere aanroepen.
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 retourneert altijd objecten op waarde, maar het is mogelijk om het equivalent van by-reference semantiek te ondersteunen via het gebruik van EndpointAddress10. Hierdoor kan de client een sessieful WCF-service-exemplaar aanvragen, waarna deze met de client kan communiceren, net als elke andere WCF-service.
Scenario 3: Client verzendt server een by-value-exemplaar
In dit scenario ziet u hoe de client een niet-primitief objectexemplaren naar de server verzendt op waarde. Omdat WCF alleen objecten per waarde verzendt, wordt in dit scenario het normale WCF-gebruik gedemonstreerd.
Gebruik dezelfde WCF-service uit Scenario 1.
Gebruik de client om een nieuw by-value-object (Customer) te maken, een kanaal te maken om te communiceren met de ICustomerService-service en het object naar het object te verzenden.
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}.");
Het klantobject wordt geserialiseerd en verzonden naar de server, waar het wordt gedeserialiseerd in een nieuwe kopie van dat object.
Notitie
Deze code illustreert ook het verzenden van een afgeleid type (PremiumCustomer). De service-interface verwacht een klantobject, maar het kenmerk [KnownType] in de klasse Klant heeft aangegeven dat PremiumCustomer ook is toegestaan. WCF mislukt elke poging om een ander type te serialiseren of deserialiseren via deze service-interface.
Normale WCF-uitwisselingen van gegevens zijn op waarde. Dit garandeert dat het aanroepen van methoden op een van deze gegevensobjecten alleen lokaal wordt uitgevoerd. Er wordt geen code op de andere laag aangeroepen. Hoewel het mogelijk is om iets te bereiken als by-reference-objecten die worden geretourneerd van de server, is het niet mogelijk voor een client om een by-reference-object door te geven aan de server. 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.
Samenvatting
.NET Remoting is een communicatieframework dat alleen binnen volledig vertrouwde omgevingen moet worden gebruikt. Het is een verouderd product en wordt alleen ondersteund voor achterwaartse compatibiliteit. Het mag niet worden gebruikt om nieuwe toepassingen te bouwen. WcF is daarentegen ontworpen met beveiliging in het achterhoofd en wordt aanbevolen voor nieuwe en bestaande toepassingen. Microsoft raadt aan om bestaande externe toepassingen te migreren voor het gebruik van WCF of ASP.NET Web-API.