Push notifications: realizziamo il servizio WCF

Print Content | More

Dopo aver gettato le basi teoriche necessarie per capire il progetto che andremo a realizzare, siamo pronti per iniziare a costruire il primo “pezzo”: il servizio WCF che farà da ponte tra l’applicazione server (che invierà le notifiche) e l’applicazione Windows Phone (che le riceverà). Come abbiamo detto l’altra volta, il servizio si occuperà di memorizzare tutti i canali aperti verso i device in un database SQL CE e di restituirli all’applicazione server quando richiesto.

Partiamo perciò aprendo Visual Studio e creando un nuovo progetto di tipo WCF Service Application, che troviamo sotto la categoria WCF. Il progetto includerà in automatico due file, chiamati IService.cs e Service.cs. Trattare qui tutta la teoria che sta dietro ai servizi WCF è praticamente impossibile: non basterebbe un libro di 1000 pagine per esaurire l’argomento.

Per i nostri scopi ci basti sapere che:

  • IService.cs è l’interfaccia che definisce i metodi che verranno esposti dal servizio,  che vengono identificati grazie all’attributo [OperationContract], con il quale andremo a decorare la firma dei nostri metodi.
  • Service.cs è l’implementazione concreta dell’interfaccia, che definisce cosa faranno effettivamente i nostri metodi.

Possiamo rinominare a piacimento queste due classi, oppure anche buttare via i file e ricrearli: nell’esempio da me realizzato le ho rinominate come IPushService.cs e PushService.cs.

E’ arrivato il momento di definire il dominio del nostro servizio, ovvero gli oggetti con cui lavoreremo e che verranno scambiati tra il servizio e l’applicazione server. Per questo progetto il dominio è molto semplice: quello che ci serve è semplicemente memorizzare ogni device che si registrerà per ricevere le notifiche push, ognuno dei quali sarà identificato dalle seguenti caratteristiche:

  • Un id univoco, che utilizziamo per identificare il device in maniera univoca. Dobbiamo ricordarci infatti che l’URL del canale è sì univoco, ma non è detto che rimanga sempre lo stesso per tutto il ciclo di vita della nostra applicazione. In una applicazione reale avrebbe senso utilizzare come id l’Anonymous Live Id oppure l’ID univoco del device: per il nostro progetto di esempio utilizzeremo un Guid, dato che l’emulatore non è in grado di restituire queste informazioni (in quanto non è associabile ad un Live Id).
  • L’URL del canale, a cui dovremo inviare le notifiche push.
  • Un semplice DateTime con la data e l’ora in cui è stato registrato il device.

Ecco la semplice classe cha mappa queste caratteristiche:

public class Device
{
    public Guid Id { get; set; }
    public string ChannelUri { get; set; }
    public DateTime RegistrationDate { get; set; }
}

E’ il momento di definire cosa farà il nostro servizio: andiamo nel file che definisce l’interfaccia IService.cs ed aggiungiamo la firma dei tre metodi che ci serviranno per gestire le notifiche push.

[ServiceContract]
public interface IPushService
{
    [OperationContract]
    string RetrieveChannelUri(Guid id);

    [OperationContract]
    void SaveChannelUri(string channelUri, Guid id);

    [OperationContract]
    List<Device> RetrieveAllDevices();
}
  • string RetrieveChannelUri (Guid id): questo metodo, dato l’id di un device, restituirà l’URL del canale a cui spedire la notifica.
  • void SaveChannelUri (string channelUri, Guid id): questo metodo provvederà a salvare sul database l’id del device e l’url del canale e verrà chiamato dall’applicazione Windows Phone.
  • List<Device> RetrieveAllDevices(): questo metodo restituisce una collezione di Device e verrà utilizzato dall’applicazione server per spedire una notifica a tutti i dispositivi registrati.

Prepariamo il database

Ora che abbiamo predisposto tutto il necessario, è il momento di mappare la nostra classe Device sul database grazie a Entity Framework. Abbiamo già definito la classe Device, che corrisponderà idealmente ad una riga della nostra tabella. Per far capire a Entity Framework che vogliamo trasformare questi oggetti in righe e tabelle, dobbiamo sfruttare la classe DbContext. Dobbiamo perciò creare una nuova classe che erediterà da DbContext e nella quale andremo ad inserire come sarà strutturato il nostro database, grazie alla classe DbSet, che rappresenta una collezione di oggetti mappata su una tabella. Ecco come apparirà il nostro context:

public class Context: DbContext
{
    public DbSet<Device> Devices { get; set; }
}

Utilizzando la sintassi tipica dei generics (DbSet<Device>) creiamo una collezione DbSet composta da oggetti di tipo Device. E’ con questa collezione che andremo a interagire all’interno del nostro servizio per recuperare, inserire o aggiornare dati sul database. Ma come facciamo a creare il database vero e proprio? Potremmo farlo manualmente tramite gli strumenti messi a disposizione da Visual Studio, ma Entity Framework Code First, grazie ad alcune convenzioni, è in grado di generare da solo il database se questo non esiste. Vediamo come fare.

Innanzitutto dobbiamo inserire nel file di configurazione del servizio (App.config) la stringa di connessione al database: nel nostro caso, essendo un database SQL CE 4.0 (quindi disconnesso e memorizzato su singolo file), sarà qualcosa di questo genere:

<connectionStrings>
  <add name="Context" connectionString="Data Source=C:\PushNotificationsDemo\PushNotificationDemo.Service\Data\Notifications.sdf" providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

La proprietà Data Source, nel nostro progetto di esempio, contiene semplicemente il percorso completo al file fisico dove vorremo memorizzare il database: non abbiamo aggiunto criteri di protezione, perciò non abbiamo specificato credenziali di accesso. Se fate attenzione, noterete una cosa importante: il nome della stringa di connessione è uguale al nome della classe che abbiamo creato in precedenza e che eredita da DbContext. Questa è la convenzione che fa sì che, in automatico, il nostro servizio, all’avvio, controlli se il database esiste o meno e, in caso contrario, lo crei.

Il servizio vero e proprio

Ora possiamo passare all’implementazione concreta del servizio, ovvero cosa devono fare i metodi esposti. Grazie a Entity Framework, a LINQ e all’uso delle lambda expression, il codice è molto semplice:

public class PushService : IPushService
{
    public string RetrieveChannelUri(Guid id)
    {
        using (Context context = new Context())
        {
            return context.Devices.SingleOrDefault(x => x.Id == id).ChannelUri;
        }            
    }

    public void SaveChannelUri(string channelUri, Guid id)
    {
        using (Context context = new Context())
        {
            if (context.Devices.Any(x => x.Id == id))
                context.Devices.SingleOrDefault(x => x.Id == id).ChannelUri = channelUri;
            else
            {
                Device device = new Device
                                    {
                                        ChannelUri = channelUri,
                                        Id = id,
                                        RegistrationDate = DateTime.Now
                                    };
                context.Devices.Add(device);
            }

            context.SaveChanges();
        }
    }

    public List<Device> RetrieveAllDevices()
    {
        using (Context context = new Context())
        {
            return context.Devices.ToList();
        }
    }
}
  • Il metodo RetrieveChannelUri ritorna l’URL del canale che è stato assegnato un device dato l’ID univoco dello stesso.
  • Il metodo SaveChannelUri verifica innanzitutto se esiste già un device registrato con l’ID assegnato. In caso affermativo, si limita ad aggiornare il record già esistente con il nuovo URL del canale; in caso negativo, crea un nuovo oggetto di tipo Device e lo aggiunge alla collection Devices esposta dal nostro context.
  • Il metodo RetrieveAllDevice ritorna una collection di tipo List di tutti gli oggetti di tipo Device memorizzati sul database.

Se notate, abbiamo racchiuso il codice di tutti e tre i metodi all’interno di un costrutto using: questo perchè ogni qualvolta eseguiamo delle operazioni tramite l’oggetto context apriamo una connessione al database, che dobbiamo ricordarci di chiudere nel momento in cui abbiamo terminato le operazioni, altrimenti lasceremmo delle connessioni “fantasma” aperte che andrebbero a causare problemi di performance non indifferenti. Il costrutto using (che possiamo utilizzare grazie al fatto che la classe DbContext implementa l’interfaccia IDisposable) fa si che la connessione al database venga automaticamente chiusa ogni qualvolta le operazioni contenute all’interno del costrutto siano terminate (sia con successo che con errori).

Un po’ di sano testing

Siamo pronti per fare una prova e verificare che il tutto vada correttamente: premiamo F5 in Visual Studio e lanciamo il servizio. Per il debug verà utilizzato in automatico Wcf Test Client, un tool di Microsoft molto comodo che vi permette di invocare i vari metodi esposti dal servizio e di vederne i valori di ritorno, senza la necessità di costruire ad esempio una applicazione a linea di comando per testare se il tutto funziona correttamente.

wcf test client

Nell’elenco di sinistra troverete tutti i metodi esposti dal servizio: facendo doppio clic su uno di essi, si aprirà un pannello (quello di destra) che vi permetterà di valorizzare i parametri di input del metodo e di vedere la risposta della vostra richiesta.

Fate doppio clic sul metodo SaveChannelUri() e date due valori fittizzi ai parametri id e channelUri, dopodichè premete il pulsante Invoke. Nella sezione Response non comparirà nessun risultato: è normale, dato che il metodo non ritorna nessun tipo di informazione. Se però è andato tutto a buon fine, nel percorso che avete specificato come Data Source nella connection string dovreste trovare un nuovo file, che rappresenta il vostro database. Come prova del nove, potete usare il tool di Visual Studio (che abbiamo installato nel post precedente) per vedere il contenuto del database:

  • Aprite Server Explorer (se non lo vedete, cliccate su View, Server Explorer nei menu di Visual Studio).
  • Ci sarà la voce Data Connections: fateci clic con il tasto destro e scegliete Add Connection.
  • Alla voce Data Source, premete Change e dall’elenco selezionate Microsoft SQL Server Compact 4.0.
  • Alla voce Connection Properties, premete Browse e andate a scegliere il database appena creato.
  • A questo punto premete Ok: se tutto è andato a buon fine, in Server Explorer sarà disponibile la nuova connessione dati e avrete modo di lavorare sul database come fareste con SQL Server Managament Studio. Se il codice che abbiamo scritto prima era corretto e il test del servizio non ha dato problemi, dovremmo trovare una nuova tabella chiamata Devices, con all’interno un nuovo record: quello che è stato creato quando abbiamo premuto il tasto Invoke nel Wcf Test Client.

connection

database

In conclusione

Abbiamo realizzato il nostro servizio e ci siamo “sporcati” le mani con Entity Framework e SQL CE 4: nel prossimo post costruiremo l’applicazione server che invierà le notifiche push, aiutandoci con la Push Notifications Server Side Library di Microsoft.


Windows Phone , Microsoft , Entity Framework , WCF , Push notifications

0 comments

Related Post


(will not be published)
(es: http://www.mysite.com)