Julien Dollon | [WP7] Utiliser une base de données embarquée SQL CE

A goal without a plan is just a wish

My blog in english is here.

Je m’appelle Julien, j'ai 27 ans et je suis un ingénieur logiciel et secondairement manager d'Hommes/projets. Je concois les produits et services de demain. J'ai entre autre participé au developpement de Windows, XBOX, Office, Surface chez Microsoft (Redmond) et je suis maintenant chez Amazon AWS (Seattle) pour Marketplace/EC2/Workspace.

Je travaille dur pour un jour avoir un grand impact sur ce monde.

Mais comme il n’y a pas de titans à combattre, que les monstres n’existent pas, je fais du dev sur des produits, qui je l’espère, vous apporteront de la joie.

Plus d'infos sur moi ici.

 

View Julien  Dollon's profile on LinkedIn

Tous les posts de ce blog ne reflètent que mon opinion et pas celui de mes employeurs et clients.

 

Service d'evaluation de competences et de creation de site webs sur cahors.

[WP7] Utiliser une base de données embarquée SQL CE

02-15-10winphone2

Notre cher Président Américain vient de retarder mon vol pour Paris, histoire de passer pour un alien, je vais prendre le temps d’écrire un petit article sur le stockage de données sous Mango! C’est parti!

L’une des problématiques que l’on peut retrouver lors d’un développement d’application WP7 est le stockage de données (entre autre pour des scénarii offline).

L’une des solutions est l’Isolated Storage, limité à 2GO par application il se comporte comme un système de fichier sécurisé où l’application peut y enregistrer ce qu’elle veut.

Sur codeplex, nous avons pu voir fleurir des bases de données embarquée exploitant cet Isolated Storage.

Avec l’arrivé de Mango, c’est une avalanche de nouveauté qui arrive, et entre autre:

  • SQL CE for WP7.1:

Cette base de donnée embarquée est implémenté à partir de la version 4 de SQL CE et stock les données dans l’Isolated Storage de l’application.

Contrairement à ce qu’on pourrait penser, la base de données n’est pas limitée à 2GO mais à 32MO par défaut extensible à 512MO.

Eviter de stocker des grosses images, si c’est le cas, préfèrez stocker le chemin et utiliser l’Isolated Storage directement pour y mettre les images. Sinon pensez à opter pour un service sur Windows Azure avec SQL Azure.

L’implémentation de cette base de données n’est pas aussi simple qu’avec .NET lorsque nous utilisons un EDMX. Là cela s’apparente plus à une sorte de création de base donnée type ADO.NET Code First.
C’est à dire qu’on commence pas créer ses propres entités (POCOs) avec des attributs au-dessus de nos propriétés. Puis nous appelons des méthodes C# nous permettant de concevoir la base de données si elle n’existe pas.

A l’heure où j’écris ces lignes je ne sais pas si il est possible d’embarquer la base de données en tant que ressources (je ne pense pas). Dans tous les cas ceci ne serait pas une bonne solution puisqu’il ferait grossir le fichier XAP et ne profiterai pas de la sécurité qu’apporte le stockage dans l’Isolated Storage.

  • Linq To SQL (System.Data.Linq):

WHAT’S THE HELL ??!!! Triste

Moi aussi j’ai fait des bonds sur ma chaise quand j’ai lu ça. Petit historique: Linq To Sql est un framework de mapping O/R pour les bases de données SQL Server. Ancêtre de Linq To Entities, il lui manquait beaucoup de fonctionnalité, comme entre autre les relations n-à-n et le support de certains types de SQL Server.

D’après la MSDN, il s'agit bien de ce même Linq-To-SQL mais avec des restrictions supplémentaires.

On interagi donc pas directement avec des requêtes SQL mais Linq-To-SQL, elles-mêmes légèrement différentes de celle de Linq-To-Entities.

Voici mon test et ce que j’ai pu en retirer:

Créer ses entités

Avant de commencer, il faut ajouter une référence et quelques usings:

C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\Silverlight\v4.0\Profile\WindowsPhone71

image

using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.ComponentModel;
using System.Collections.ObjectModel;
Nous partons sur un exemple bateau:

  • Des étudiants qui possèdent une ou plusieurs notes (1-à-n)

Nous commençons par définir la table étudiant. Nous implémentons, comme nous le demande la MSDN, l’interface INotifyPropertyChanging pour consommer moins de mémoire (le tracking se basera sur nos déclarations). Nous ajoutons l’attribut Table au-dessus de notre classe:

[Table]
public class Etudiant : INotifyPropertyChanged, INotifyPropertyChanging
{
    public event PropertyChangedEventHandler PropertyChanged;

    public event PropertyChangingEventHandler PropertyChanging;

    public void OnNotifyPropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    public void OnNotifyPropertyChanging(string propertyName)
    {
        if(PropertyChanging != null)
            PropertyChanging(this, new PropertyChangingEventArgs(propertyName));
    }
}

Puis nous implémentons les propriétés de base de nos étudiants assorti de l’attribut Column:

private int identifiant;
[Column(IsPrimaryKey = true, IsDbGenerated = true, 
DbType = "INT NOT NULL Identity", CanBeNull = false, 
AutoSync = AutoSync.OnInsert)]
public int Identifiant
{
    get
    {
        return identifiant;
    }
    set
    {
        if (identifiant != value)
        {
            OnNotifyPropertyChanging("Identifiant");
            identifiant = value;
            OnNotifyPropertyChanged("Identifiant");
        }
    }
}

private string nom;
[Column()]
public string Nom
{
    get
    {
        return nom;
    }
    set
    {
        if (nom != value)
        {
            OnNotifyPropertyChanging("Nom");
            nom = value;
            OnNotifyPropertyChanged("Nom");
        }
    }
}

Pour tout ce qui est limitation, champs requis, taille tout se passe dans les paramètres de l’attribut Column voir la MSDN.

Se connecter/Créer la base de données

Après avoir créé les tables, il faut créer une classe héritant de DataContext qui permettra les échanges avec notre base.

Ici nous mettons la connexionstring en dur, si vous souhaitez approfondir ce sujet direction la MSDN.

public class MyDataContext : DataContext
{
    public static string DBConnectionString 
		= "Data Source=isostore:/BDD.sdf";

    public MyDataContext(string connectionString)
        : base(connectionString)
    {
    }

    public Table<Etudiant> Etudiants;
    public Table<Note> Notes;
}

Cette classe va à la fois nous permettre de nous connecter, mais aussi de créer la base de données si elle n’existe déjà pas:

MyDataContext dt = new MyDataContext(MyDataContext.DBConnectionString);
if(!dt.DatabaseExists())
    dt.CreateDatabase();

CRUD sur la base de données

Rapidement, voici le code pour faire des crud sur le type étudiant dans notre base:

private void button1_Click(object sender, RoutedEventArgs e)
{
    MyDataContext dt = new MyDataContext(MyDataContext.DBConnectionString);
    if(!dt.DatabaseExists())
        dt.CreateDatabase();

    Etudiant monetudiant = new Etudiant() {Nom = "Jacky"};
    dt.Etudiants.InsertOnSubmit(monetudiant);
    dt.SubmitChanges();

    var etudiants = from Etudiant etudiant in dt.Etudiants
                    select etudiant;

    ObservableCollection<Etudiant> mesEtudiants 
	= new ObservableCollection<Etudiant>(etudiants);

    mesEtudiants[0].Nom = "Jojo";
    dt.SubmitChanges();

    dt.Etudiants.DeleteOnSubmit(mesEtudiants[0]);
    dt.SubmitChanges();
}

Si vous voulez utiliser les types anonymes en Windows  Phone 7 (entre autre pour éviter de rajouter le type Etudiant dans la requête), il faut ajouter cette ligne au fichier AssemblyInfo.cs:

[assembly: InternalsVisibleTo("System.Windows")]

Relation 1-n

Nous avons vu comment créer et gérer nos étudiants mais quand est-il de leurs notes?

Nous allons, du côté de la note, lui affecter un ID représentant l’étudiant:

private int etudiantId;
[Column]
public int EtudiantId
{
    get
    {
        return etudiantId;
    }
    set
    {
        if (identifiant != value)
        {
            OnNotifyPropertyChanging("EtudiantId");
            etudiantId = value;
            OnNotifyPropertyChanged("EtudiantId");
        }
    }
}

Ainsi que propriété permettant de remonter jusqu’à l’objet étudiant:

private EntityRef<Etudiant> etudiant;
[Association(OtherKey = "Identifiant", ThisKey = "EtudiantId", 
Storage = "etudiant")]
public Etudiant Etudiant
{
    get { return etudiant.Entity; } 
	set 
	{ etudiant.Entity = value; EtudiantId = value.Identifiant; }
}

Après la regénération de la base (supprimer l’application de l’émulateur), voici un exemple d’insertion/requêtage:

Etudiant monetudiant = new Etudiant() {Nom = "Jacky"};
dt.Etudiants.InsertOnSubmit(monetudiant);
dt.SubmitChanges();


Note noteEtudiant = new Note() 
{Valeur = 20, EtudiantId = monetudiant.Identifiant};
dt.Notes.InsertOnSubmit(noteEtudiant);
dt.SubmitChanges();


var notes = from note in dt.Notes
            where note.EtudiantId == monetudiant.Identifiant
                select note;
MessageBox.Show(notes.First().Etudiant.Nom);

Et nous remarquons que l’on n’a pas besoin de passer par du eagerloading car le lazyloading est automatiquement activé.

Si vous souhaitez le désactiver:

dt.DeferredLoadingEnabled = false;

Conclusion

La première chose qu’il ne faut pas oublier c’est que le logiciel peut évoluer et donc la base de données aussi. Pensez donc à vérifier dès le départ la version de la base de données pour éventuellement créer de nouvelles colonnes:

int dbVersion = dbUpdater.DatabaseSchemaVersion;

De plus, si vous devez augmenter la taille de la base, il faut le prévoir dans la connexion string en rajoutant un ssce:taille max.

Concrètement, cette base de données était juste indispensable et je l’attendais !! Je suis donc très content qu’ils l’aient implémenté.

Cependant, j’aurai aimé une syntaxe type Linq To Entities, un designer également plutôt que du code-first et le support des relations n-to-n.

Qui parie que dans le WP7 prochain on nous demande comme à l’époque du .NET 3.0->3.5 de laisser tomber Linq-To-SQL pour Entities?

En tout cas en attendant, cela nous dépannera !

Posté le: May 27 2011, 22:11 | Commentaires
Catégorie(s): .NET | WP7

blog comments powered by Disqus
Anciens commentaires (archive):