Procedura: utilizzare il trasporto HTTP
Aggiornamento: novembre 2007
È possibile utilizzare il trasporto HTTP per creare applicazioni su un dispositivo che verrà connesso a un servizio Windows Communication Foundation (WCF) sul desktop.
In questo argomento viene descritto come configurare il servizio WCF per la gestione dei dispositivi di connessione e come creare l'applicazione client. Vengono illustrate le differenze tra la configurazione del servizio WCF e la codifica sul lato client di cui tener conto quando si abilitano i dispositivi mobili per la connessione al servizio. Per ulteriori informazioni sulla creazione di applicazioni WCF per il desktop, vedere la sezione Esercitazione introduttiva nella documentazione WCF.
Per creare il servizio WCF per il desktop
Creare un nuovo progetto di servizio Web.
Modificare il file Web.config come illustrato nell'esempio seguente. Modificare gli elementi e gli attributi seguenti nel file:
Modificare il valore dell'attributo <endpoint>binding con "basicHttpBinding". .NET Compact Framework supporta la codifica del testo, ma non quella binaria.
Modificare l'attributo behaviorConfiguration in modo che faccia riferimento al nuovo nome di comportamento.
Sostituire l'elemento <behavior> come illustrato nell'esempio.
Se è necessario registrare il gestore dei servizi HTTP nel file Web.config, aggiungere un nuovo elemento <system.WebServer> con le informazioni riportate nell'esempio.
<?xml version="1.0"?> <configuration xmlns="https://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.serviceModel> <services> <service name="CalculatorService" behaviorConfiguration="MyServiceTypeBehaviors"> <endpoint contract="ICalculatorService" binding="basicHttpBinding"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="MyServiceTypeBehaviors"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> <system.web> <compilation debug="true"/> </system.web> <system.webServer> <handlers> <add name="HttpSvcHandler" path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" resourceType="Unspecified" /> </handlers> </system.webServer> </configuration>
Nel codice sorgente per il servizio WCF rimuovere i parametri specificati per gli attributi ServiceContract e OperationContract.
Nota: Nell'esempio non viene implementato il supporto per i parametri specificati nei contratti quali ServiceContract e OperationContract. Se per tali contratti è necessario il supporto per i parametri, è possibile utilizzare lo strumento WCF ServiceModel Utility (NetCFSvcUtil.exe) di .NET Compact Framework per generare il codice client. Tale strumento consente di compilare il supporto per molti di questi parametri nelle applicazioni basate su .NET Compact Framework. NetCFSvcUtil.exe è disponibile in Power Toys per .NET Compact Framework. Per ulteriori informazioni, vedere Power Toys for .NET Compact Framework (informazioni in lingua inglese).
Nell'esempio riportato di seguito viene illustrato il codice sorgente del servizio WCF per un'applicazione calcolatrice semplificata.
<ServiceContract()> _ Public Interface ICalculatorService <OperationContract()> _ Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double '<OperationContract()> _ Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double End Interface Public Class CalculatorService Implements ICalculatorService Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add Return n1 + n2 End Function Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Subtract Return n1 - n2 End Function End Class
[ServiceContract()] public interface ICalculatorService { [OperationContract] double Add(double n1, double n2); [OperationContract] double Subtract(double n1, double n2); } public class CalculatorService : ICalculatorService { public double Add(double n1, double n2) { return n1 + n2; } public double Subtract(double n1, double n2) { return n1 - n2; } }
Impostare il servizio WCF su una porta specifica sul server Web.
Utilizzare qualsiasi numero di porta compreso tra 10000 e 650000. Nell'esempio viene utilizzata la porta 50505.
Avviare il server Web.
Se si desidera visualizzare l'output WSDL (Web Services Description Language) ed eseguire il servizio su localhost, immettere https://localhost:50505/CalculatorService/Service.svc?wsdl. Utilizzare lo stesso numero di porta e nome di progetto Web specificati per il servizio WCF.
Se si intende connettersi al server Web da un dispositivo o un computer remoto, impostare una directory virtuale in modo che punti alla directory contenente il progetto Web.
Nota: Il server di sviluppo ASP.NET di Visual Studio risponde solo alle richieste generate dal computer di sviluppo locale. Per specificare una directory virtuale, è consigliabile utilizzare Internet Information Services (IIS). In questo modo sarà possibile connettersi al server Web da un dispositivo remoto purché il server sia raggiungibile.
Assicurarsi di poter accedere alla directory da un browser desktop e da un browser per dispositivi.
Per creare il client .NET Compact Framework
Durante l'esecuzione del servizio, aprire una riga di comando e passare alla directory in cui si trova il servizio WCF.
Dalla riga di comando eseguire lo strumento WCF Service Model Desktop Utility (SvcUtil.exe) per generare un proxy client WCF. Di seguito è riportato un esempio di chiamata a SvcUtil dalla riga di comando in cui il servizio è ospitato su localhost:
svcutil.exe /language:c# https://localhost:50505/CalculatorService/Service.svc
Nell'esempio riportato di seguito è mostrato un proxy client generato da SvcUtil che si basa sull'esempio di calcolatrice semplificata.
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.42 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] [System.ServiceModel.ServiceContractAttribute(ConfigurationName="ICalculatorService")] public interface ICalculatorService { [System.ServiceModel.OperationContractAttribute(Action="https://fabrikam.com/ICalculatorService/Add", ReplyAction="https://fabrikam.com/ICalculatorService/AddResponse")] double Add(double n1, double n2); [System.ServiceModel.OperationContractAttribute(Action="https://fabrikam.com/ICalculatorService/Subtract", ReplyAction="https://fabrikam.com/ICalculatorService/SubtractResponse")] double Subtract(double n1, double n2); } [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public interface ICalculatorServiceChannel : ICalculatorService, System.ServiceModel.IClientChannel { } [System.Diagnostics.DebuggerStepThroughAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")] public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService { public CalculatorServiceClient() { } public CalculatorServiceClient(string endpointConfigurationName) : base(endpointConfigurationName) { } public CalculatorServiceClient(string endpointConfigurationName, string remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : base(endpointConfigurationName, remoteAddress) { } public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public double Add(double n1, double n2) { return base.Channel.Add(n1, n2); } public double Subtract(double n1, double n2) { return base.Channel.Subtract(n1, n2); } }
Rimuovere gli attributi e gli elementi non supportati dal codice proxy client generato, inclusi gli elementi seguenti:
Tutti gli attributi System.ServiceModel.
I riferimenti alla classe IClientChannel.
I riferimenti ai nomi di configurazione <endpoint>.
Le implementazioni di metodo che chiamano i metodi dell'interfaccia ServiceContract sul canale interno.
Nell'esempio riportato di seguito viene illustrato il codice generato in seguito a queste modifiche.
'------------------------------------------------------------------------------ ' <auto-generated> ' This code was generated by a tool. ' Runtime Version:2.0.50727.312 ' ' Changes to this file may cause incorrect behavior and will be lost if ' the code is regenerated. ' </auto-generated> '------------------------------------------------------------------------------ Option Strict Off Option Explicit On Public Interface ICalculatorService Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double End Interface Partial Public Class CalculatorServiceClient Inherits System.ServiceModel.ClientBase(Of ICalculatorService) Implements ICalculatorService ' Add a variable containing the endpoint address. Public Shared ServiceEndPoint As New System.ServiceModel.EndpointAddress("https://fabrikam.com/CalcService/CalculatorService/Service.svc") Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress) MyBase.New(binding, remoteAddress) End Sub Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add Return MyBase.Channel.Add(n1, n2) End Function Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Subtract Return MyBase.Channel.Subtract(n1, n2) End Function End Class
//------------------------------------------------------------------------------ // <auto-generated> // This code was generated by a tool. // Runtime Version:2.0.50727.42 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // </auto-generated> //------------------------------------------------------------------------------ public interface ICalculatorService { double Add(double n1, double n2); double Subtract(double n1, double n2); } public partial class CalculatorServiceClient : System.ServiceModel.ClientBase<ICalculatorService>, ICalculatorService { // Add a variable to specify the server address. public static System.ServiceModel.EndpointAddress ServiceEndPoint = new System.ServiceModel.EndpointAddress("https://fabrikam.com/CalcService/CalculatorService/Service.svc"); public CalculatorServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : base(binding, remoteAddress) { } public double Add(double n1, double n2) { return base.Channel.Add(n1, n2); } public double Subtract(double n1, double n2) { return base.Channel.Subtract(n1, n2); } }
Creare un progetto client.
Aggiungere il proxy client generato al progetto.
Nel codice proxy generato modificare il riferimento completo a ClientBase<TChannel> con la classe ClientBase definita dall'utente.
Nel codice proxy generato aggiungere le implementazioni di metodo richiamando il metodo Call nella classe ClientBase definita dall'utente.
Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add Return System.Convert.ToDouble(MyBase.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", New String() {"n1", "n2"}, New Object() {n1, n2}, GetType(Double))) End Function
public double Add(double n1, double n2) { return (double)base.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", new string[] { "n1", "n2" }, new object[] { n1, n2 }, typeof(double)); }
Aggiungere la classe base per il proxy nel progetto. Tale classe si chiama ClientBase.
Modificare il riferimento alla classe base del proxy client in modo che punti all'implementazione di ClientBase.
Nota: In questo esempio la classe CustomBodyWriter di ClientBase supporta solo tipi primitivi. Per supportare tipi non primitivi è necessario estendere il metodo OnWriteBodyContents. È ad esempio possibile chiamare un serializzatore personalizzato per serializzare i dati dei messaggi. In questo caso è necessario convertire gli attributi del codice presenti nel proxy client generato in attributi utilizzabili dal serializzatore XML. In questo scenario è innanzitutto necessario aggiungere l'opzione /serializer:xmlserializer http://endpoint quando si esegue SvcUtil.
Nel codice riportato di seguito viene illustrato un esempio di classe ClientBase.
Public Class ClientBase(Of TChannel As Class) Private requestChannel As IRequestChannel Private messageVersion As MessageVersion Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress) 'this.remoteAddress = remoteAddress; Me.messageVersion = binding.MessageVersion Dim channelFactory As IChannelFactory(Of IRequestChannel) channelFactory = binding.BuildChannelFactory(Of IRequestChannel)(New BindingParameterCollection()) channelFactory.Open() Me.requestChannel = channelFactory.CreateChannel(remoteAddress) End Sub Public Function [Call](ByVal op As String, ByVal action As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal returntype As Type) As Object requestChannel.Open(TimeSpan.MaxValue) Dim msg As Message = Message.CreateMessage(Me.messageVersion, action, New CustomBodyWriter(op, varnames, varvals, "<ns passed in from Proxy>")) Dim reply As Message = requestChannel.Request(msg, TimeSpan.MaxValue) Dim reader As XmlDictionaryReader = reply.GetReaderAtBodyContents() reader.ReadToFollowing(op + "Result") Return reader.ReadElementContentAs(returntype, Nothing) End Function End Class Friend Class CustomBodyWriter Inherits BodyWriter Private op As String Private varnames() As String Private varvals() As Object Private ns As String Public Sub New(ByVal op As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal ns As String) MyBase.New(True) Me.op = op Me.varnames = varnames Me.varvals = varvals Me.ns = ns End Sub Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter) writer.WriteStartElement(op, ns) Dim i As Integer For i = 0 To varnames.Length writer.WriteElementString(varnames(i), varvals(i).ToString()) Next i writer.WriteEndElement() End Sub End Class
public class ClientBase<TChannel> where TChannel : class { private IRequestChannel requestChannel; private MessageVersion messageVersion; public ClientBase(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) { //this.remoteAddress = remoteAddress; this.messageVersion = binding.MessageVersion; IChannelFactory<IRequestChannel> channelFactory = binding.BuildChannelFactory<IRequestChannel>( new BindingParameterCollection()); channelFactory.Open(); this.requestChannel = channelFactory.CreateChannel(remoteAddress); } public object Call(string op, string action, string[] varnames, object[] varvals, Type returntype) { requestChannel.Open(TimeSpan.MaxValue); //Message msg = //Message.CreateMessage(MessageVersion.<FromBinding>, // action, // new CustomBodyWriter(op, varnames, varvals, //"<ns passed in from Proxy>")); Message msg = Message.CreateMessage(this.messageVersion, action, new CustomBodyWriter(op, varnames, varvals, "<ns passed in from Proxy>")); Message reply = requestChannel.Request(msg, TimeSpan.MaxValue); XmlDictionaryReader reader = reply.GetReaderAtBodyContents(); reader.ReadToFollowing(op + "Result"); return reader.ReadElementContentAs(returntype, null); } } internal class CustomBodyWriter : BodyWriter { private string op; private string[] varnames; private object[] varvals; private string ns; public CustomBodyWriter(string op, string[] varnames, object[] varvals, string ns) : base(true) { this.op = op; this.varnames = varnames; this.varvals = varvals; this.ns = ns; } protected override void OnWriteBodyContents(XmlDictionaryWriter writer) { writer.WriteStartElement(op, ns); for (int i = 0; i < varnames.Length; i++) writer.WriteElementString(varnames[i], varvals[i].ToString()); writer.WriteEndElement(); } }
Aggiungere una classe per creare un'istanza del proxy client e utilizzarlo.
Nell'esempio di codice riportato di seguito viene illustrato il codice che consente di richiamare il proxy client.
Shared Sub Main(ByVal args() As String) Dim serverAddress As String = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri ' Using basic http connection. WS binding should be also available. Dim proxy As ICalculatorService = New CalculatorServiceClient(New BasicHttpBinding, New EndpointAddress(serverAddress)) MessageBox.Show("Add 3 + 6...") MessageBox.Show(proxy.Add(3, 6).ToString) MessageBox.Show("Subtract 8 - 3...") MessageBox.Show(proxy.Subtract(8, 3).ToString) End Sub
static void Main() { string serverAddress = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri; // Using basic http connection. WS binding should be also available. ICalculatorService proxy = new CalculatorServiceClient(new BasicHttpBinding(), new EndpointAddress(serverAddress)); MessageBox.Show("Add 3 + 6..."); MessageBox.Show((proxy.Add(3, 6)).ToString()); MessageBox.Show("Subtract 8 - 3..."); MessageBox.Show((proxy.Subtract(8, 3)).ToString()); }
Compilare l'applicazione client e distribuirla nel dispositivo.
Mentre è in corso l'esecuzione del servizio WCF e il dispositivo è connesso in rete, avviare l'applicazione client sul dispositivo.
Compilazione del codice
Il codice sorgente per il servizio WCF richiede riferimenti agli spazi dei nomi seguenti:
Il codice sorgente per la classe ClientBase richiede riferimenti agli spazi dei nomi seguenti:
Il codice sorgente per la classe contenente il metodo Main nell'applicazione client richiede riferimenti agli spazi dei nomi seguenti:
Sicurezza
In questo esempio non sono implementate le funzionalità di sicurezza WCF. Per ulteriori informazioni sulle funzionalità di sicurezza supportate, vedere Messaggistica in .NET Compact Framework.
Vedere anche
Concetti
Messaggistica in .NET Compact Framework
Altre risorse
Sviluppo di Windows Communication Foundation (WCF) e .NET Compact Framework