Condividi tramite


Creare e utilizzare un servizio app

Annotazioni

Gli elenchi di codice in questo argomento sono solo C#. Per un'app di esempio per i servizi app in C++/WinRT e C#, vedere gli Esempi di servizi app o ottenere il codice sorgente dei progetti di Esempi di servizi app su GitHub.

Importante

Questo argomento si applica alle app UWP (Universal Windows Platform) che usano .NET Native. Visual Studio 2022 include ora anche modelli di progetto UWP che usano .NET 9. Per questo argomento, devi usare i modelli di progetto UWP che includono ".NET Native" nei nomi, ad esempio App vuota UWP (.NET Native). I progetti UWP che usano .NET 9 non sono stati testati con questo argomento e potrebbero non funzionare come previsto.

I servizi app sono app UWP che forniscono servizi ad altre app UWP. Sono analoghi ai servizi Web, in un dispositivo. Un servizio app viene eseguito come attività in background nell'app host e può fornire il servizio ad altre app. Ad esempio, un servizio app potrebbe fornire un servizio scanner di codice a barre che altre app potrebbero usare. O forse una suite aziendale di app ha un servizio comune di controllo ortografico disponibile per le altre app nella suite. I servizi app consentono di creare servizi senza interfaccia utente che le app possono chiamare sullo stesso dispositivo e a partire da Windows 10, versione 1607, nei dispositivi remoti.

A partire da Windows 10 versione 1607, è possibile creare servizi app eseguiti nello stesso processo dell'app host. Questo articolo è incentrato sulla creazione e l'utilizzo di un servizio app eseguito in un processo in background separato. Consultare Convertire un servizio app per l'esecuzione nello stesso processo dell'app host per ulteriori dettagli su come eseguire un servizio app nello stesso processo del provider.

Creare un nuovo progetto del fornitore di servizi per app

In questa procedura si creeranno tutti gli elementi in una soluzione per semplicità.

  1. In Visual Studio 2022 o versione successiva creare un nuovo progetto di app UWP e denominarlo AppServiceProvider.
    1. Selezionare File > Nuovo progetto >...
    2. Nella finestra di dialogo Crea un nuovo progetto selezionare App vuota UWP (.NET Native). Assicurarsi di selezionare il tipo di progetto C#. Questa sarà l'app che rende il servizio app disponibile per altre app UWP.
    3. Fare clic su Avanti, quindi assegnare al progetto il nome AppServiceProvider, scegliere un percorso e quindi fare clic su Crea.
  2. Quando viene chiesto di selezionare un di destinazione e versione minima per il progetto, selezionare almeno 10.0.14393. Se vuoi usare il nuovo attributo SupportsMultipleInstances , devi avere come destinazione 10.0.15063 (Windows 10 Creators Update) o versione successiva.

Aggiungere un'estensione del servizio app a Package.appxmanifest

Nel progetto AppServiceProvider, aprire in un editor di testo il file Package.appxmanifest.

  1. Fare clic con il pulsante destro del mouse in Esplora soluzioni .
  2. Selezionare Apri con.
  3. Selezionare Editor di testo XML.

Aggiungere l'estensione AppService seguente all'interno dell'elemento <Application>. Questo esempio annuncia il servizio com.microsoft.inventory ed è ciò che identifica questa app come provider di servizi app. Il servizio effettivo verrà implementato come attività in background. Il progetto del servizio app espone il servizio ad altre app. È consigliabile usare uno stile di nome di dominio inverso per il nome del servizio.

Si noti che il prefisso dello spazio dei nomi xmlns:uap4 e l'attributo uap4:SupportsMultipleInstances sono validi solo se si usa Windows SDK versione 10.0.15063 o successiva. È possibile rimuoverli in modo sicuro se si ha come destinazione le versioni precedenti dell'SDK.

Annotazioni

Per un'app di esempio del servizio app in C++/WinRT e C#, vedere 'app di esempio del servizio app.

<Package
    ...
    xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
    xmlns:uap4="http://schemas.microsoft.com/appx/manifest/uap/windows10/4"
    ...
    <Applications>
        <Application Id="AppServiceProvider.App"
          Executable="$targetnametoken$.exe"
          EntryPoint="AppServiceProvider.App">
          ...
          <Extensions>
            <uap:Extension Category="windows.appService" EntryPoint="MyAppService.Inventory">
              <uap3:AppService Name="com.microsoft.inventory" uap4:SupportsMultipleInstances="true"/>
            </uap:Extension>
          </Extensions>
          ...
        </Application>
    </Applications>

L'attributo Category identifica questa applicazione come provider di servizi app e l'attributo EntryPoint identifica la classe qualificata dello spazio dei nomi che implementa il servizio. Questa operazione verrà implementata successivamente.

L'attributo SupportsMultipleInstances indica che ogni volta che viene chiamato il servizio dell'app, questo deve essere eseguito in un nuovo processo. Questo non è obbligatorio, ma è disponibile per te se hai bisogno di questa funzionalità e hai come destinazione 10.0.15063 SDK (Windows 10 Creators Update) o versione successiva. Deve inoltre essere preceduto dallo spazio dei nomi uap4.

Creare il servizio app

In questa sezione verrà creato un servizio app che viene eseguito come attività in background. Il servizio app fornirà un semplice servizio di inventario che consente ad altre app di eseguire query sul nome e sul prezzo degli articoli nell'inventario.

  1. Un servizio app può essere implementato come attività in background. Ciò consente a un'applicazione in primo piano di richiamare un servizio app in un'altra applicazione. Per creare un servizio app per attività in background, aggiungere un nuovo progetto di componente Windows Runtime alla soluzione (File > Aggiungi > Nuovo progetto) chiamato MyAppService. Nella finestra di dialogo Aggiungi un nuovo progetto scegliere Componente Windows Runtime (.NET Native).

  2. Nel progetto AppServiceProvider, aggiungere un riferimento al nuovo progetto MyAppService (in Esplora soluzioni, fare clic con il pulsante destro del mouse sul progetto AppServiceProvider, >>>>, selezionare MyAppService e fare clic su >). Questo passaggio è fondamentale perché se non si aggiunge il riferimento, il servizio app non si connetterà in fase di esecuzione.

  3. Nel progetto MyAppService aggiungere le seguenti usando istruzioni all'inizio di Class1.cs:

    using Windows.ApplicationModel.AppService;
    using Windows.ApplicationModel.Background;
    using Windows.Foundation.Collections;
    
  4. Rinominare Class1.cs in Inventory.cse sostituire il codice stub per Class1 con una nuova classe di attività in background denominata Inventory:

    public sealed class Inventory : IBackgroundTask
    {
        private BackgroundTaskDeferral backgroundTaskDeferral;
        private AppServiceConnection appServiceconnection;
        private String[] inventoryItems = new string[] { "Robot vacuum", "Chair" };
        private double[] inventoryPrices = new double[] { 129.99, 88.99 };
    
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Get a deferral so that the service isn't terminated.
            this.backgroundTaskDeferral = taskInstance.GetDeferral();
    
            // Associate a cancellation handler with the background task.
            taskInstance.Canceled += OnTaskCanceled;
    
            // Retrieve the app service connection and set up a listener for incoming app service requests.
            var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            appServiceconnection = details.AppServiceConnection;
            appServiceconnection.RequestReceived += OnRequestReceived;
        }
    
        private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            // This function is called when the app service receives a request.
        }
    
        private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            if (this.backgroundTaskDeferral != null)
            {
                // Complete the service deferral.
                this.backgroundTaskDeferral.Complete();
            }
        }
    }
    

    Questa classe è la posizione in cui il servizio app eseguirà il proprio lavoro.

    Run viene chiamato quando viene creata l'attività in background. Poiché le attività in background vengono terminate dopo il completamento di Run, il codice prende un rinvio affinché l'attività in background rimanga attiva per soddisfare le richieste. Un servizio app implementato come attività in background rimarrà attivo per circa 30 secondi dopo che riceve una chiamata, a meno che non venga chiamato di nuovo entro tale intervallo di tempo o che venga estratto un rinvio. Se il servizio app viene implementato nello stesso processo del chiamante, la durata del servizio app è associata alla durata del chiamante.

    La durata del servizio app dipende dal chiamante:

    • Se il chiamante è in primo piano, il ciclo di vita del servizio app è lo stesso del chiamante.
    • Se il chiamante è in background, il servizio app ha 30 secondi per funzionare. Richiedere un differimento concede un'ulteriore volta di 5 secondi.

    OnTaskCanceled viene chiamato quando l'attività viene annullata. L'attività è annullata quando l'app client elimina l'AppServiceConnection, l'app client viene sospesa, il sistema operativo è arrestato o va in sospensione, oppure il sistema operativo esaurisce le risorse per eseguire l'attività.

Scrivere il codice per il servizio app

OnRequestReceived è la posizione in cui viene inserito il codice per il servizio app. Sostituire lo stub OnRequestReceived in MyAppServiceInventory.cs con il codice fornito in questo esempio. Questo codice ottiene un indice per un articolo di inventario e lo passa, insieme a una stringa di comando, al servizio per recuperare il nome e il prezzo dell'articolo di inventario specificato. Per i progetti personalizzati, aggiungere il codice di gestione degli errori.

private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
    // Get a deferral because we use an awaitable API below to respond to the message
    // and we don't want this call to get canceled while we are waiting.
    var messageDeferral = args.GetDeferral();

    ValueSet message = args.Request.Message;
    ValueSet returnData = new ValueSet();

    string command = message["Command"] as string;
    int? inventoryIndex = message["ID"] as int?;

    if (inventoryIndex.HasValue &&
        inventoryIndex.Value >= 0 &&
        inventoryIndex.Value < inventoryItems.GetLength(0))
    {
        switch (command)
        {
            case "Price":
            {
                returnData.Add("Result", inventoryPrices[inventoryIndex.Value]);
                returnData.Add("Status", "OK");
                break;
            }

            case "Item":
            {
                returnData.Add("Result", inventoryItems[inventoryIndex.Value]);
                returnData.Add("Status", "OK");
                break;
            }

            default:
            {
                returnData.Add("Status", "Fail: unknown command");
                break;
            }
        }
    }
    else
    {
        returnData.Add("Status", "Fail: Index out of range");
    }

    try
    {
        // Return the data to the caller.
        await args.Request.SendResponseAsync(returnData);
    }
    catch (Exception e)
    {
        // Your exception handling code here.
    }
    finally
    {
        // Complete the deferral so that the platform knows that we're done responding to the app service call.
        // Note for error handling: this must be called even if SendResponseAsync() throws an exception.
        messageDeferral.Complete();
    }
}

Si noti che OnRequestReceived è un metodo asincrono perché in questo esempio effettuiamo una chiamata di metodo awaitable a SendResponseAsync.

Viene eseguito un rinvio in modo che il servizio possa usare metodi asincroni nel gestore OnRequestReceived. Garantisce che la chiamata a OnRequestReceived non venga completata finché non viene eseguita l'elaborazione del messaggio. SendResponseAsync invia il risultato al chiamante. SendResponseAsync non segnala il completamento della chiamata. È il completamento del differimento che segnala a SendMessageAsync che OnRequestReceived sia completato. La chiamata a SendResponseAsync è inclusa in un blocco try/finally perché è necessario completare il rinvio anche se SendResponseAsync genera un'eccezione.

I servizi delle app usano oggetti ValueSet per scambiare informazioni. Le dimensioni dei dati che è possibile passare sono limitate solo dalle risorse di sistema. Non sono disponibili chiavi predefinite da usare nel ValueSet. È necessario determinare quali valori chiave si useranno per definire il protocollo per il servizio app. Il chiamante deve essere scritto tenendo presente tale protocollo. In questo esempio è stata scelta una chiave denominata Command con un valore che indica se si vuole che il servizio app fornisca il nome dell'articolo di inventario o il relativo prezzo. L'indice del nome dell'inventario viene archiviato nella chiave ID. Il valore restituito viene archiviato nella chiave Result.

Al chiamante viene restituita un'enumerazione AppServiceClosedStatus per indicare se la chiamata al servizio app ha avuto esito positivo o negativo. Un esempio di come la chiamata al servizio app potrebbe non riuscire è se il sistema operativo interrompe l'endpoint del servizio perché le risorse sono state superate. È possibile restituire informazioni aggiuntive sull'errore tramite ValueSet. In questo esempio viene usata una chiave denominata Status per restituire informazioni più dettagliate sull'errore al chiamante.

La chiamata a SendResponseAsync restituisce il ValueSet al chiamante.

Distribuire l'app del servizio e ottenere il nome della famiglia di pacchetti

Il provider del servizio app deve essere distribuito prima di poterlo chiamare da un client. È possibile distribuirlo selezionando Compila > Distribuisci Soluzione in Visual Studio.

È necessario anche il nome della famiglia di pacchetti del provider di servizi app per chiamarlo. È possibile ottenerlo seguendo questa procedura:

  1. Aprire il file Package.appxmanifest del progetto AppServiceProvider nella visualizzazione della finestra di progettazione (fare doppio clic su di esso in Esplora soluzioni).
  2. Selezionare la scheda Packaging, copiare il valore accanto a nome della famiglia di pacchettie incollarlo in un posto come Blocco note per adesso.

Scrivere un client per chiamare il servizio app

In questa sezione, creeremo un'applicazione client che chiama il servizio dell'app che abbiamo appena creato. L'app client sarà una semplice app UWP con una casella di testo e un pulsante. Quando l'utente immette un indice nella casella di testo e fa clic sul pulsante, l'app chiamerà il servizio app per ottenere il nome e il prezzo dell'articolo di inventario in tale indice.

  1. Aggiungi alla soluzione un nuovo progetto vuoto di app universale di Windows con File > Aggiungi > Nuovo progetto. Nella finestra di dialogo Aggiungi un nuovo progetto scegliere App vuota UWP (.NET Native) e denominarla ClientApp.

  2. Nel progetto ClientApp aggiungere le seguenti usando l'istruzione all'inizio di MainPage.xaml.cs:

    using Windows.ApplicationModel.AppService;
    
  3. Modificare la griglia nella pagina principale in un oggetto StackPanel in modo da poter aggiungere una casella di testo e un pulsante.

  4. Aggiungere un controllo TextBox denominato textBox e un pulsante a MainPage.xaml.

  5. Aggiungere un pulsante con un gestore eventi click denominato button_Click e un testo per il contenuto, ad esempio "Click me".

  6. Aggiungere la parola chiave async alla firma del gestore del pulsante in MainPage.xaml.cs.

  7. Sostituire lo stub del gestore del clic sul pulsante con il codice seguente. Assicurarsi di includere la inventoryService dichiarazione di campo nella classe .

    private AppServiceConnection inventoryService;
    
    private async void button_Click(object sender, RoutedEventArgs e)
    {
        // Add the connection.
        if (this.inventoryService == null)
        {
            this.inventoryService = new AppServiceConnection();
    
            // Here, we use the app service name defined in the app service 
            // provider's Package.appxmanifest file in the <Extension> section.
            this.inventoryService.AppServiceName = "com.microsoft.inventory";
    
            // Use Windows.ApplicationModel.Package.Current.Id.FamilyName 
            // within the app service provider to get this value.
            this.inventoryService.PackageFamilyName = "Replace with the package family name";
    
            var status = await this.inventoryService.OpenAsync();
    
            if (status != AppServiceConnectionStatus.Success)
            {
                textBox.Text= "Failed to connect";
                this.inventoryService = null;
                return;
            }
        }
    
        // Call the service.
        int idx = int.Parse(textBox.Text);
        var message = new ValueSet();
        message.Add("Command", "Item");
        message.Add("ID", idx);
        AppServiceResponse response = await this.inventoryService.SendMessageAsync(message);
        string result = "";
    
        if (response.Status == AppServiceResponseStatus.Success)
        {
            // Get the data  that the service sent to us.
            if (response.Message["Status"] as string == "OK")
            {
                result = response.Message["Result"] as string;
            }
        }
    
        message.Clear();
        message.Add("Command", "Price");
        message.Add("ID", idx);
        response = await this.inventoryService.SendMessageAsync(message);
    
        if (response.Status == AppServiceResponseStatus.Success)
        {
            // Get the data that the service sent to us.
            if (response.Message["Status"] as string == "OK")
            {
                result += " : Price = " + response.Message["Result"] as string;
            }
        }
    
        textBox.Text = result;
    }
    

    Sostituire il nome della famiglia di pacchetti nella riga this.inventoryService.PackageFamilyName = "Replace with the package family name"; con quello del progetto AppServiceProvider, ottenuto precedentemente nella fase Distribuire l'app del servizio e ottenere il nome della famiglia di pacchetti.

    Annotazioni

    Assicurarsi di incollare la stringa letterale anziché metterla in una variabile. Non funzionerà se si usa una variabile.

    Il codice stabilisce prima una connessione con il servizio app. La connessione rimarrà aperta fino a quando non si elimina this.inventoryService. Il nome del servizio app deve corrispondere all'attributo AppService dell'elemento Name che hai aggiunto al file Package.appxmanifest del progetto AppServiceProvider. In questo esempio è <uap3:AppService Name="com.microsoft.inventory"/>.

    Viene creato un ValueSet denominato message per specificare il comando da inviare al servizio app. Il servizio app di esempio prevede un comando per indicare quali di due azioni eseguire. Otteniamo l'indice dalla casella di testo nell'app client e quindi avviamo il servizio con il comando Item per ottenere la descrizione dell'elemento. Quindi, facciamo la chiamata con il comando Price per ottenere il prezzo dell'articolo. Il testo del pulsante è impostato sul risultato.

    Poiché AppServiceResponseStatus indica solo se il sistema operativo è riuscito a connettere la chiamata al servizio app, viene verificata la chiave Status nel ValueSet ricevuto dal servizio app per assicurarsi che sia stato in grado di soddisfare la richiesta.

  8. Imposta il progetto ClientApp come progetto di avvio (fai clic con il pulsante destro del mouse su Esplora soluzioni e seleziona>Imposta come progetto di avvio) ed esegui la soluzione. Immettere il numero 1 nella casella di testo e fare clic sul pulsante. Dovresti ricevere "Sedia: Prezzo = 88,99" dal servizio.

    app di esempio che visualizza il prezzo della sedia=88,99

Risolvere i problemi comuni

Se la chiamata al servizio app non riesce, controllare quanto segue nel progetto ClientApp:

  1. Verificare che il nome della famiglia di pacchetti assegnato alla connessione al servizio di inventario corrisponda al nome della famiglia di pacchetti dell'app AppServiceProvider. Osserva la riga in button_Click con this.inventoryService.PackageFamilyName = "...";.
  2. In button_Clickverificare che il nome del servizio app assegnato alla connessione al servizio di inventario corrisponda al nome del servizio app nel file AppServiceProviderPackage.appxmanifest. Vedere: this.inventoryService.AppServiceName = "com.microsoft.inventory";.
  3. Assicuratevi che l'app AppServiceProvider sia stata distribuita. Nell'Esplora soluzioni , cliccare con il tasto destro sulla soluzione e scegliere Deploy Solution.

Eseguire il debug del servizio app

Per eseguire il debug del servizio app, è necessario configurare la soluzione in modo che il provider del servizio app venga distribuito e che il servizio app possa essere chiamato dall'app client. Segui questi passaggi:

  1. Assicurarsi che la soluzione venga distribuita prima del debug perché l'app del fornitore del servizio deve essere distribuita prima di chiamare il servizio. In Visual Studio Compila > Distribuisci soluzione).
  2. In Esplora soluzioni, fare clic con il pulsante destro del mouse sul progetto AppServiceProvider e scegliere Proprietà. Nella scheda debug modificare l'azione start di in Non avviare, ma eseguire il debug del codice all'avvio. Si noti che, se si utilizza C++ per implementare il provider di servizi dell'app, dalla scheda Debug si cambierebbe Launch Application in No).
  3. Nel progetto MyAppService, nel file Inventory.cs impostare un punto di interruzione in OnRequestReceived.
  4. Impostare il progetto AppServiceProvider come progetto di avvio e premere F5.
  5. Avviare clientApp dal menu Start (non da Visual Studio).
  6. Immettere il numero 1 nella casella di testo e premere il pulsante. Il debugger si fermerà nella chiamata al servizio dell'app sul punto di interruzione del tuo servizio dell'app.

Eseguire il debug del client

Per eseguire il debug dell'app client che chiama il servizio app, è necessario collegare il debugger al processo dell'app client. Segui questi passaggi:

  1. Seguire le istruzioni del passaggio precedente per effettuare il debug del client che chiama il servizio dell'app.
  2. Avviare ClientApp dal menu Start.
  3. Collegare il debugger al processo di ClientApp.exe (non al processo di ApplicationFrameHost.exe). In Visual Studio scegliere Debug > Collega al processo....
  4. Nel progetto ClientApp impostare un punto di interruzione in button_Click.
  5. I punti di interruzione nel client e nel servizio app verranno ora raggiunti quando si immette il numero 1 nella casella di testo di ClientApp e si fa clic sul pulsante.

Risoluzione dei problemi generali del servizio app

Se riscontri uno stato AppUnavailable dopo aver provato a connetterti a un servizio app, verifica quanto segue:

  • Assicurarsi che il progetto del fornitore del servizio di app e il progetto del servizio di app siano distribuiti. Entrambi devono essere distribuiti prima di eseguire il client perché in caso contrario il client non avrà nulla a cui connettersi. È possibile eseguire la distribuzione da Visual Studio usando Build>Deploy Solution.
  • Nella Esplora soluzioni, assicurarsi che il progetto del fornitore di servizi app abbia un riferimento tra progetti al progetto che implementa il servizio app.
  • Verificare che la voce e i relativi elementi figlio siano stati aggiunti al file Package.appxmanifest appartenente al progetto del fornitore di servizi app come specificato in precedenza in Aggiungere un'estensione del servizio app a Package.appxmanifest.
  • Assicurarvi che la stringa AppServiceConnection.AppServiceName nel client che chiama il provider del servizio di app corrisponda al <uap3:AppService Name="..." /> specificato nel file Package.appxmanifest del progetto del provider del servizio di app.
  • Assicurati che AppServiceConnection.PackageFamilyName corrisponda al nome della famiglia di pacchetti del componente del provider del servizio app, come specificato in precedenza in Aggiungi un'estensione del servizio app a Package.appxmanifest.
  • Per i servizi app out-of-process, ad esempio quello di questo esempio, verificare che il EntryPoint specificato nell'elemento <uap:Extension ...> del progetto del provider di servizi app Package.appxmanifest corrisponda allo spazio dei nomi e al nome della classe pubblica che implementa IBackgroundTask nel progetto del servizio app.

Risolvere i problemi di debug

Se il debugger non si arresta ai punti di interruzione nei progetti del fornitore di servizi app o nei progetti relativi al servizio app, verificare quanto segue:

  • Assicurarsi che il progetto del fornitore del servizio di app e il progetto del servizio di app siano distribuiti. Entrambi devono essere distribuiti prima di eseguire il client. È possibile distribuirli da Visual Studio usando Compila>Distribuisci soluzione.
  • Assicurarsi che il progetto di cui si vuole eseguire il debug sia impostato come progetto di avvio e che le proprietà di debug per tale progetto siano impostate per non eseguire il progetto quando viene premuto F5. Fare clic con il pulsante destro del mouse sul progetto, quindi scegliere proprietà e quindi debug (o debug in C++). In C# modificare il 'azione Avvia in Non avviare, ma eseguire il debug del codice all'avvio di. In C++, configurare Avvia applicazione su No.

Osservazioni:

Questo esempio fornisce un'introduzione alla creazione di un servizio app che viene eseguito come attività in background e alla chiamata da un'altra app. Le cose chiave da notare sono:

  • Creare un'attività in background per ospitare il servizio dell'app.
  • Aggiungere l'estensione windows.appService al file Package.appxmanifest del provider di servizi app.
  • Ottenere il nome della famiglia di pacchetti del provider del servizio app per potervi connettere dall'app client.
  • Aggiungere un riferimento da progetto a progetto dal progetto di fornitura del servizio app al progetto del servizio app.
  • Usare Windows.ApplicationModel.AppService.AppServiceConnection per chiamare il servizio.

Codice completo per MyAppService

Di seguito è riportato il codice completo per il progetto MyAppService , che implementa il servizio app come attività in background. Questo codice deve essere inserito nel file Inventory.cs del progetto MyAppService .

using System;
using Windows.ApplicationModel.AppService;
using Windows.ApplicationModel.Background;
using Windows.Foundation.Collections;

namespace MyAppService
{
    public sealed class Inventory : IBackgroundTask
    {
        private BackgroundTaskDeferral backgroundTaskDeferral;
        private AppServiceConnection appServiceconnection;
        private String[] inventoryItems = new string[] { "Robot vacuum", "Chair" };
        private double[] inventoryPrices = new double[] { 129.99, 88.99 };

        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Get a deferral so that the service isn't terminated.
            this.backgroundTaskDeferral = taskInstance.GetDeferral();

            // Associate a cancellation handler with the background task.
            taskInstance.Canceled += OnTaskCanceled;

            // Retrieve the app service connection and set up a listener for incoming app service requests.
            var details = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            appServiceconnection = details.AppServiceConnection;
            appServiceconnection.RequestReceived += OnRequestReceived;
        }

        private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            // Get a deferral because we use an awaitable API below to respond to the message
            // and we don't want this call to get canceled while we are waiting.
            var messageDeferral = args.GetDeferral();

            ValueSet message = args.Request.Message;
            ValueSet returnData = new ValueSet();

            string command = message["Command"] as string;
            int? inventoryIndex = message["ID"] as int?;

            if (inventoryIndex.HasValue &&
                 inventoryIndex.Value >= 0 &&
                 inventoryIndex.Value < inventoryItems.GetLength(0))
            {
                switch (command)
                {
                    case "Price":
                        {
                            returnData.Add("Result", inventoryPrices[inventoryIndex.Value]);
                            returnData.Add("Status", "OK");
                            break;
                        }

                    case "Item":
                        {
                            returnData.Add("Result", inventoryItems[inventoryIndex.Value]);
                            returnData.Add("Status", "OK");
                            break;
                        }

                    default:
                        {
                            returnData.Add("Status", "Fail: unknown command");
                            break;
                        }
                }
            }
            else
            {
                returnData.Add("Status", "Fail: Index out of range");
            }

            // Return the data to the caller.
            await args.Request.SendResponseAsync(returnData);

            // Complete the deferral so that the platform knows that we're done responding to the app service call.
            // Note for error handling: this must be called even if SendResponseAsync() throws an exception.
            messageDeferral.Complete();
        }


        private void OnTaskCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            if (this.backgroundTaskDeferral != null)
            {
                // Complete the service deferral.
                this.backgroundTaskDeferral.Complete();
            }
        }
    }
}