Avoir accès à un moteur de base de données natif dans un environnement Windows 8 Modern UI en Javascript, en C# en VB et en C++/CX : Part I

 

Télécharger les exemples et les binaires.

Lire la partie II

Introduction

Avec l’arrivée de Windows 8 et du modèle de développement WinRT (Windows Runtime), je n’ai pas trouvé à part SQLite de moyen simple pour créer un cache local qui soit efficace et performant.

Il se trouve qu’il est possible avec la WinRT d’utiliser en C++/CX les API ESE (Extensible Storage Engine) qui permettent justement de créer une base de données avec des tables, des indexes, de requêter sur ces indexes etc.. Sachez d’ailleurs que ce moteur est utilisé par Exchange Server, Active Directory, et même Desktop Search.

ESE a donc piqué ma curiosité et sans prétention, je vous dévoile aujourd’hui, la version 0.0.0.1 d’un composant développé en C++ et C++/CX qui n’est ni plus ni moins, qu’une enveloppe au dessus de ESE, et qui à pour but de simplifier l’utilisation de ESE, qui est essentiellement une API C, et assez désagréable à utiliser sans documentation digne de ce nom.

ESE étant également disponible sur architecture ARM, mon composant en profite également et les premiers tests qu’on a pu faire sur un Surface RT et sur une base de plusieurs dizaines de milliers de cartes magiques sont plutôt prometteur. Mais David Catuhe, vous en dira plus bientôt je l’espère Clignement d'œil

Ceci étant dit, dans sa version 0.0.0.1, il ne faut pas s’attendre à des miracles, mais voici en substance ce qu’il est possible de faire.

  • Créer une base de données
  • Créer des tables avec des champs et des indexes
  • Ajouter, Supprimer, Modifier des enregistrements
  • S’abonner à des évènements lors de la manipulation des enregistrements d’une table
  • Parcourir les enregistrements d’une table séquentiellement ou en fonction des indexes de la table
  • Requêter sur un ou plusieurs indexes d’une table.

   

Dans ce 1er billet, nous allons commencer par les basiques.

Initialisation du moteur

Une fois que vous avez référencé le composant JetBlue.WinRT à votre projet, la 1ère étape consiste à initialiser le moteur ESE comme illustré dans les différents extrait de code suivant :

   

  

Code VB.NET

  Dim m_engine = New JetBlue.WinRT.JetBlueEngine()
            m_engine.Error.ThrowException = True
            m_engine.BeginSession()

 

Code C#

var m_engine = new JetBlue.WinRT.JetBlueEngine();
             m_engine.Error.ThrowException = true;
             m_engine.BeginSession();

 

Code C++/CX

auto m_engine = ref new JetBlue::WinRT::JetBlueEngine();
             m_engine->Error->ThrowException = true;
             m_engine->BeginSession();

 

Code Javascript

var m_engine = new JetBlue.WinRT.JetBlueEngine(true);
            m_engine.error.throwException = true;
            m_engine.beginSession();

Tout d’abord, il faut instancier la classe JetBlueEngine(), dans cet exemple, j’indique que le moteur lèvera une exception via le paramètre ThrowException=true, ainsi Toutes les erreurs pourrons alors être traquées via une gestion classique des exceptions try/catch.  

 Par défaut le moteur lève l’évènement m_engine.Error.OnJetBlueError auquel il faut s’abonner si vous souhaitez récupérer le message d’erreur. comme illustré sur le code suivant :
 

  

Evènement OnJetBlueError

m_engine.Error.OnJetBlueError += Error_OnJetBlueError;
        void Error_OnJetBlueError(object sender, string e)
        {
            var errorMessage=e;
        }

Aujourd'hui avec le Runtime Windows 8, pour récupérer le message d’erreur approprié, il faut utiliser la méthode m_engine.GetLastErrorMessage(). Mais avec le Runtime Windows 8.1, il sera possible de retourner directement le message dans l’exception, ce que je ne manquerai pas de faire lorsque je porterai ce composant sur la version 8.1.
  

  

Exception

  1. catch (Exception ex)
  2.              {
  3.                  var error = m_engine.GetLastErrorMessage();
  4.              }

 

Dans l’exemple en Javascript, j’instancie le moteur en lui passant dans son constructeur la valeur true. C’est la stratégie que j’ai choisie aujourd’hui, pour savoir quel langage utilise le moteur. En effet certaines différences entre Javascript et les autres langages m’ont obligés à modifier certains algorithmes. J’y reviendrais par la suite.  

Note : Pour arrêter le moteur de base de données, il suffit d’utiliser la méthode  m_engine.EndSession().
Lors de la création de la base de données il est important de bien terminer le processus et de fermer la base de données afin que cette dernière ne soit pas dans un état instable.

 

Création de la base de données

Pour créer une base de données, nous allons utiliser la méthode

JetBlueDatabase^ CreateDatabase(Platform::String^ dbname,CreateDatabaseFlags flags), de la classe JetBlueEngine

Code VB.NET

Dim db = m_engine.CreateDatabase("TestDB.EDB", JetBlue.WinRT.Enum.CreateDatabaseFlags.OverwriteExisting)

Code C#

var db = m_engine.CreateDatabase("TestDB.EDB", JetBlue.WinRT.Enum.CreateDatabaseFlags.OverwriteExisting);

Code C++/CX

auto db = m_engine->CreateDatabase("TestDB.EDB", JetBlue::WinRT::Enum::CreateDatabaseFlags::OverwriteExisting);

Code Javascript

var db = m_engine.createDatabase("TestDB.EDB", JetBlue.WinRT.Enum.CreateDatabaseFlags.overwriteExisting);

La variable db contient une instance de la classe JetBlueDatabase, que nous utiliserons pour créer une table.

Il existe également la méthode IAsyncOperation<JetBlueDatabase^>^ CreateDatabaseAsync(Platform::String^ dbname,CreateDatabaseFlags flags) que vous pouvez utiliser pour créer une base de données de manière asynchrone.

Code C++/CX

task<JetBlueDatabase^> taskDb(m_engine->CreateDatabaseAsync("TestDB.EDB", CreateDatabaseFlags::OverwriteExisting));
                 taskDb.then([this,m_engine,group1](JetBlueDatabase^db)
                 {
                    

Code Javascript

m_engine.createDatabaseAsync("Test.edb", JetBlue.WinRT.Enum.CreateDatabaseFlags.overwriteExisting).then(function (db) {

Avec C# et VB.NET il suffit de l’utiliser en conjonction avec les mots clés async/await.

Création d’une table

Pour créer une table, vous avez à votre disposition une des méthodes surchargées


JetBlueTable^ CreateTable(Platform::String^ tablename, IVector<JetBlueColumn^>^ columns)

de l’objet base de données, ou son pendant asynchrone

IAsyncOperation<JetBlueTable^>^ CreateTableAsync(Platform::String^ tablename, IVector<JetBlueColumn^>^ columns);

Dans notre exemple , CreateTable, prend comme paramètre le nom de la table sous forme d’une chaine de caractères, ainsi qu’une collection d’objets JetBlueCoumn.  

Cette collection d’objets JetBlueColumn, comme nous le verrons par la suite, est une construction importante, car elle sera le pivot, pour créer les champs d’une table, pour stocker des valeurs dans une table, et sert de réceptacle lors de la navigation et de la recherche des données dans une table.

Code VB.NET

Dim listcolumns = New List(Of JetBlue.WinRT.JetBlueColumn)()
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("Nom", ColumnType.Text, ColumnFlags.NotNull))
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("Prnom", ColumnType.Text, ColumnFlags.NotNull))
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("Age", ColumnType.Int32, ColumnFlags.NotNull))
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("PicturePath", ColumnType.Text, ColumnFlags.NotNull))
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("Description", ColumnType.Text, ColumnFlags.NotNull))
                listcolumns.Add(New JetBlue.WinRT.JetBlueColumn("itemContent", ColumnType.LongText, ColumnFlags.MaybeNull))               
                Dim table = db.CreateTable("TETable", listcolumns)
               

 

Code C#

var listcolumns = new List<JetBlue.WinRT.JetBlueColumn>();                
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("Nom", ColumnType.Text, ColumnFlags.NotNull));
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("Prnom", ColumnType.Text, ColumnFlags.NotNull));
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("Age", ColumnType.Int32, ColumnFlags.NotNull));
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("PicturePath", ColumnType.Text, ColumnFlags.NotNull));
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("Description", ColumnType.Text, ColumnFlags.NotNull));
                 listcolumns.Add(new JetBlue.WinRT.JetBlueColumn("itemContent", ColumnType.LongText, ColumnFlags.MaybeNull));
                 var table = db.CreateTable("TETable", listcolumns);

 

Code C++/CX

Platform::Collections::Vector<JetBlueColumn^>^ listcolumns = ref new Platform::Collections::Vector<JetBlueColumn^>();                                     
                     listcolumns->Append(ref new JetBlueColumn("Nom", ColumnType::Text, ColumnFlags::NotNull));
                     listcolumns->Append(ref new JetBlueColumn("Prnom", ColumnType::Text, ColumnFlags::NotNull));
                     listcolumns->Append(ref new JetBlueColumn("Age", ColumnType::Int32, ColumnFlags::NotNull));
                     listcolumns->Append(ref new JetBlueColumn("PicturePath", ColumnType::Text, ColumnFlags::NotNull));
                     listcolumns->Append(ref new JetBlueColumn("Description", ColumnType::Text, ColumnFlags::NotNull));
                     listcolumns->Append(ref new JetBlueColumn("itemContent", ColumnType::LongText, ColumnFlags::MaybeNull));                    
                     auto table = db->CreateTable("TETable", listcolumns);

 

Code Javascript

var listcolumns = [];              
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("Nom", JetBlue.WinRT.Enum.ColumnType.text, JetBlue.WinRT.Enum.ColumnFlags.notNull));
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("Prnom", JetBlue.WinRT.Enum.ColumnType.text, JetBlue.WinRT.Enum.ColumnFlags.notNull));
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("Age", JetBlue.WinRT.Enum.ColumnType.int16, JetBlue.WinRT.Enum.ColumnFlags.notNull));
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("PicturePath", JetBlue.WinRT.Enum.ColumnType.text, JetBlue.WinRT.Enum.ColumnFlags.notNull));
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("Description", JetBlue.WinRT.Enum.ColumnType.text, JetBlue.WinRT.Enum.ColumnFlags.notNull));
                listcolumns.push(new JetBlue.WinRT.JetBlueColumn("itemContent", JetBlue.WinRT.Enum.ColumnType.longText, JetBlue.WinRT.Enum.ColumnFlags.maybeNull));                   
                var table = db.createTable("TETable", listcolumns);
               

 

Pour définir une colonne d’une table on utilise la classe JetBlueColumn, et les propriétés suivantes :

    • Name : Chaine de caractères pour indiquer son nom
    • ColumnType : Enumération qui indique le type de la colonne.
      Aujourd’hui sont supportés les types :
      • Bit : Pour stocker les valeurs Booléenne (true/false) (peut également contenir null)
      • Int16 : Entier signé codé sur 2 octets : -32768 à 32767.
      • Int32 : Entier signé codé sur 4 octets : - 2147483648 à 2147483647
      • Int64 : Entier signé codé sur 8 octets : - 9223372036854775808 à 9223372036854775807.
      • UInt16 : Entier non signé codé sur 2 octets : 0 à 65535
      • UInt32 : Entier non signé codé sur 4 octets : 0 à 4294967295
      • IEEESingle : Flottant simple précision
      • IEEEDouble : Flottant double précision
      • Text : Chaine de caractères fixe ou variable qui peut aller jusqu’à de 255 Caractères Ascii ou 127 caractères unicode.
      • LongText : Chaine de caractères variable qui peut aller jusqu’à de 2147483647 Caractères Ascii, ou 1073741823 caractères unicode.
      • LongBinary : Champ d’octets variable pouvant contenir jusqu’à 2147483647 octets.
      • DateTime : Flottant double précision qui représente la date depuis 1900.

Remarque : Seront supportés dans la prochaine version les types currency et GUID.            

    • ColumnFlags : Un groupe de drapeaux qui contient les options à utiliser lors du stockage des données dans la colonne, qui incluent des valeurs de zéro ou plusieurs. C’est à dire qu’on peut les combiner avec l’opérateur unaire |
      • Fixed : La taille de la colonne est de longueur fixe, à utiliser en conjonction avec ColumnSizeMax. Ce drapeau ne peut être utilisé avec les types LongText ou LongBinary.
      • Tagged : Ce type de colonne ne prend pas d’espace dans la base de données, si la colonne ne contient pas de données. Ne peut être utilisé en conjonction avec le drapeau Fixed.
      • MultiValued : Une colonne multi-value, peut avoir zéro ou plusieurs valeurs associées. C’est utile, par exemple, lorsque vous souhaitez stocker des données dans une même table. Par exemple une table auteurs qui contiendrait un champ multi-value des noms de livres associées, évitant ainsi d’ouvrir la table livres pour retrouver les noms de livres associés à un auteur.
      • NotNull : La colonne ne doit jamais être nulle. Lors de l’insertion de données dans la table, le champ doit être obligatoirement renseigné.
      • MaybeNull : La colonne peut être nulle.
      • Compressed : Les données dans la colonne peuvent être compressée.

 

Note : La classe JetBlueColumn, peut contenir d’autres paramètres comme la création d’un index sur une colonne, que nous aborderons dans notre prochaine billet.

    

Insertion des données dans une table

Pour insérer des données, nous allons utiliser encore une fois une collection d’objets JetBlueColumn, qui dans notre exemple, sera la même que celle utilisée lors de la création de la table.

Mais cette fois-ci, nous allons renseigner la propriété Value de la classe JetBlueColumn, avec la valeur appropriée.

Code VB

listcolumns(0).Value = "Catuhe"
                listcolumns(1).Value = "David"
                listcolumns(2).Value = 36  'par dfaut, le CLR .NET encode un Object en INT32
                listcolumns(3).Value = "https://i.msdn.microsoft.com/ms348103.davidPic(fr-fr,MSDN.10).jpg"
                listcolumns(4).Value = "Responsable des experts techniques"
                listcolumns(5).Value = "Expert dans les technologies 3D, DirectX OpenGL et tout le toutime"
                m_engine.BeginTransation()
                table.Records.Add(listcolumns)

                listcolumns(0).Value = "Verni"
                listcolumns(1).Value = "Eric"
                listcolumns(2).Value = 48
                listcolumns(3).Value = "https://i.msdn.microsoft.com/ms348103.ericPic(fr-fr,MSDN.10).jpg"
                listcolumns(4).Value = "Expert Technique"
                listcolumns(5).Value = "Expert dans les technologies Windows, par trs bon en Javascript"
             
                table.Records.Add(listcolumns)
                m_engine.CommitTransaction(CommitTransactionFlags.CommitLazyFlush)

Code C#

   listcolumns[0].Value = "Catuhe";
                 listcolumns[1].Value = "David";
                 listcolumns[2].Value = 36;
                 listcolumns[3].Value = "https://i.msdn.microsoft.com/ms348103.davidPic(fr-fr,MSDN.10).jpg";
                 listcolumns[4].Value = "Responsable des experts techniques";
                 listcolumns[5].Value = "Expert dans les technologies 3D, DirectX OpenGL et tout le toutime";
                 m_engine.BeginTransation();
                 table.Records.Add(listcolumns);

                 listcolumns[0].Value = "Verni";
                 listcolumns[1].Value = "Eric";
                 listcolumns[2].Value = 48;
                 listcolumns[3].Value = "https://i.msdn.microsoft.com/ms348103.ericPic(fr-fr,MSDN.10).jpg";
                 listcolumns[4].Value = "Expert Technique";
                 listcolumns[5].Value = "Expert dans les technologies Windows, par trs bon en Javascript";
                
                 table.Records.Add(listcolumns);

                 m_engine.CommitTransaction(CommitTransactionFlags.CommitLazyFlush);

 

Code C++/CX

    listcolumns->GetAt(0)->Value = "Catuhe";
                     listcolumns->GetAt(1)->Value = "David";
                     listcolumns->GetAt(2)->Value = 36;
                     listcolumns->GetAt(3)->Value = "https://i.msdn.microsoft.com/ms348103.davidPic(fr-fr,MSDN.10).jpg";
                     listcolumns->GetAt(4)->Value = "Responsable des experts techniques";
                     listcolumns->GetAt(5)->Value = "Expert dans les technologies 3D, DirectX OpenGL et tout le toutime";
                     m_engine->BeginTransation();
                     table->Records->Add(listcolumns);

                     listcolumns->GetAt(0)->Value = "Verni";
                     listcolumns->GetAt(1)->Value = "Eric";
                     listcolumns->GetAt(2)->Value = 48;
                     listcolumns->GetAt(3)->Value = "https://i.msdn.microsoft.com/ms348103.ericPic(fr-fr,MSDN.10).jpg";
                     listcolumns->GetAt(4)->Value = "Expert Technique";
                     listcolumns->GetAt(5)->Value = "Expert dans les technologies Windows, par trs bon en Javascript";
                    
                     table->Records->Add(listcolumns);

                     m_engine->CommitTransaction(CommitTransactionFlags::CommitLazyFlush);

 

Code Javascript

     listcolumns[0].value = "Catuhe";
                listcolumns[1].value = "David";
                listcolumns[2].value = -36;
                listcolumns[3].value = "https://i.msdn.microsoft.com/ms348103.davidPic(fr-fr,MSDN.10).jpg";
                listcolumns[4].value = "Responsable des experts techniques";
                listcolumns[5].value = "Expert dans les technologies 3D, DirectX OpenGL et tout le toutime";
                m_engine.beginTransation();
                table.records.add(listcolumns);

                listcolumns[0].value = "Verni";
                listcolumns[1].value = "Eric";
                listcolumns[2].value = 48;
                listcolumns[3].value = "https://i.msdn.microsoft.com/ms348103.ericPic(fr-fr,MSDN.10).jpg";
                listcolumns[4].value = "Expert Technique";
                listcolumns[5].value = "Expert dans les technologies Windows, par trs bon en Javascript";
               
                table.records.add(listcolumns);

                m_engine.commitTransaction(JetBlue.WinRT.Enum.CommitTransactionFlags.commitLazyFlush);

 

Vous noterez que la propriété Value, accepte des valeurs de n’importe quel type, que se soit de l’entier ou une chaine de caractères (mais même les types DateTime, flottant, ou valeur booléenne), à l’exception d’un tableau d’octets pour lequel nous utiliserons le champ particulier BinaryValue, comme nous le verrons également dans un prochain billet.

Ceci est du au faite que la propriété Value est de type Object, et que le composant prend en charge automatiquement toute la mécanique de conversion du type Object vers le type défini pour chaque colonne.

J’ai pris la décision d’utiliser le type Object, afin d’éviter de créer pour la classe JetBlueColumn une propriété pour chaque type de valeur et ceci afin de faciliter son utilisation en ne surchargeant pas trop la classe. Néanmoins ceci ne va pas sans contre partie.

La 1ère contre partie, pourrait être la performance, pour l’instant je n’ai pas constaté de problèmes et les objectifs que je me suis fixés sont atteint donc je garde cette stratégie.

La seconde est un peu plus ennuyeuse pour les colonnes de types entière ou flottante. En effet, le CLR (C# et VB.NET) ou le WinRT (C++/CX), stocke par défaut lors de l’affectation listcolumns[2].value=48, un Int32.

Si dans ce cas la colonne avait été définie comme étant d’un autre type, par exemple Int16, Int64, une erreur est levée par mon composant. Pour le résoudre vous pouvez “caster” la valeur dans le type de la colonne, afin de forcer le CLR ou le WinRT à stocker le bon type.

Pour Javascript, c’est une peu le même principe, si ce n’est qu’il stocke un Double, et qu’il n’y a pas moyen de caster dans un type particulier. (Avec le peu de connaissance que j’ai de ce langage, je pense qu’il n’est pas possible de caster, si c’est possible, n’hésitez pas à me l’indiquer ;-)), c’est la raison pour laquelle, l’instanciation du moteur se fait avec le paramètre usewithjavascript=true. En positionnant ce drapeau, j’utilise un mécanisme interne pour stocker la bonne valeur dans le bon type.

Une fois les champs de la colonne renseignés, il suffit d’ajouter les données à la table à l’aide de la méthode void Add(IVector<JetBlueColumn^> ^record) de l’objet Records.

Note : Il ne vous aura pas échappé que l’ajout des enregistrements sont inclus dans une Transaction, que l’on peut valider avec la méthode JetBlueEngine.CommiTransaction() ou invalider avec JetBlueEngine. Rollback(). Il n’est pas nécessaire d’utiliser des transactions, à l’exception si vous utilisez des champs de type LongText ou LongBinary. De toute manière le moteur lèvera une exception pour le cas ou la transaction est obligatoire.

Il bien évidement possible de modifier void Update(IVector<JetBlueColumn ^> ^record) ou de supprimer void Remove() un enregistrement. Il est également possible de s’abonner à des évènements lors de la manipulation des enregistrements d’une table. Mais je vous le réserve pour un prochain billet. Néanmoins voici un extrait de code en C# pour le faire.

Code Snippet

table.RegisterCallback(ActionFlags.BeforeInsert | ActionFlags.AfterInsert |
                                        ActionFlags.BeforeDelete | ActionFlags.AfterDelete |
                                        ActionFlags.BeforeReplace | ActionFlags.AfterReplace
                                        );
                 table.OnAction += (object sender, ActionFlags e)=>
                                    {
                                        var action = e;
                                    };

 

Navigation séquentielle

Maintenant que nous avons quelques données dans notre base de données, il temps de naviguer dedans.

Dans un 1er temps, nous allons faire une navigation simple en parcourant les données de manière séquentielle.

Code VB

Dim t1 = db.OpenTable("TETable", JetBlue.WinRT.Enum.OpenTableFlags.ReadOnly)
                Dim columns = New List(Of JetBlueColumn)

                columns.Add(New JetBlueColumn("Nom"))
                columns.Add(New JetBlueColumn("Prnom"))
                columns.Add(New JetBlueColumn("Age"))
                columns.Add(New JetBlueColumn("PicturePath"))
                columns.Add(New JetBlueColumn("Description"))
                columns.Add(New JetBlueColumn("itemContent"))

                Dim count As Integer = 1000         
                Do
                    Dim record = t1.Records.Get(columns)
                    Dim name = record(1).Value + " " + record(0).Value
                    Dim subtitle = record(4).Value.ToString()
                    Dim picturePath = record(3).Value.ToString()
                    Dim content = record(5).Value.ToString()
                    group1.Items.Add(New SampleDataItem("Group-1-Item-1" + count.ToString(),
                       name,
                       subtitle,
                       picturePath,
                      content,
                      content,
                      group1))
                    count += 1
                Loop While t1.Records.Move(JetBlue.WinRT.Enum.MoveDirection.Next) = JetBlue.WinRT.Enum.MoveResult.Success

 

Code C#

var t1 = db.OpenTable("TETable", OpenTableFlags.ReadOnly);
                 var columns = new List<JetBlueColumn>();
                 columns.Add(new JetBlueColumn("Nom"));
                 columns.Add(new JetBlueColumn("Prnom"));
                 columns.Add(new JetBlueColumn("Age"));
                 columns.Add(new JetBlueColumn("PicturePath"));
                 columns.Add(new JetBlueColumn("Description"));
                 columns.Add(new JetBlueColumn("itemContent"));
                 int count = 1000;
                 do
                 {
                     var record = t1.Records.Get(columns);
                     var name = record[1].Value + " " + record[0].Value;
                     var subtitle = record[4].Value.ToString();
                     var picturePath = record[3].Value.ToString();
                     var content = record[5].Value.ToString();
                     group1.Items.Add(new SampleDataItem("Group-1-Item-1" + count.ToString(),
                        name,
                        subtitle,
                        picturePath,
                       content,
                       content,
                       group1));
                     count += 1;
                 }
                 while (t1.Records.Move(MoveDirection.Next) != MoveResult.NoCurrentRecord);

 

 

Code C++/CX

auto t1 = db->OpenTable("TETable", JetBlue::WinRT::Enum::OpenTableFlags::ReadOnly);
                     auto columns=ref new  Platform::Collections::Vector<JetBlueColumn^>();       
                     columns->Append(ref new JetBlueColumn("Nom"));
                     columns->Append(ref new JetBlueColumn("Prnom"));
                     columns->Append(ref new JetBlueColumn("Age"));
                     columns->Append(ref new JetBlueColumn("PicturePath"));
                     columns->Append(ref new JetBlueColumn("Description"));
                     columns->Append(ref new JetBlueColumn("itemContent"));       
                     int count = 1000;
                     do
                    {
                         auto record = t1->Records->Get(columns);
                         auto name = record->GetAt(1)->Value + " " + record->GetAt(0)->Value;
                         auto subtitle = record->GetAt(4)->Value->ToString();
                         auto picturePath = record->GetAt(3)->Value->ToString();
                         auto content = record->GetAt(5)->Value->ToString();
                         group1->Items->Append(ref new SampleDataItem("Group-1-Item-1" + count.ToString(),
                            name,
                            subtitle,
                            picturePath,
                           content,
                           content,
                           group1));
                         count += 1;
                     }
                     while (t1->Records->Move(JetBlue::WinRT::Enum::MoveDirection::Next) != JetBlue::WinRT::Enum::MoveResult::NoCurrentRecord);

 

Code Javascript

var t1 = db.openTable("TETable", JetBlue.WinRT.Enum.OpenTableFlags.readOnly);
                var columns = [];
                columns.push(new JetBlue.WinRT.JetBlueColumn("Nom"));
                columns.push(new JetBlue.WinRT.JetBlueColumn("Prnom"));
                columns.push(new JetBlue.WinRT.JetBlueColumn("Age"));
                columns.push(new JetBlue.WinRT.JetBlueColumn("PicturePath"));
                columns.push(new JetBlue.WinRT.JetBlueColumn("Description"));
                columns.push(new JetBlue.WinRT.JetBlueColumn("itemContent"));
                do {
                    var record = t1.records.get(columns);
                    var name = record[1].value + " " + record[0].value;
                    sampleItems.push({ group: sampleGroups[0], title: name, subtitle: record[4].value, description: record[5].value, content: record[2].value, backgroundImage: record[3].value });

                }
                while (t1.records.move(JetBlue.WinRT.Enum.MoveDirection.next) != JetBlue.WinRT.Enum.MoveResult.noCurrentRecord);

Tout d’abord on va ouvrir la table à l’aide de la méthode JetBlueTable^ OpenTable(Platform::String^ tablename,OpenTableFlags flags) qui prend comme paramètre le nom de la table, ainsi qu’un drapeau précisant la manière de l’ouvrir. Cette méthode retourne une instance de la classe JetBlueTable. Ici nous l’ouvrons en mode ReadOnly, mais il existe d’autres paramètres, comme celui comme celui qui permet d’ouvrir la table en mode “Updatable”, si vous souhaitez faire des ajouts ou des mises à jour.

Ensuite il faut préciser dans une collection d’objets JetBlueColumn toujours , les colonnes que vous souhaitez requêter.

Pour récupérer les données on passera la collection de colonnes, à la méthode IVector<JetBlueColumn^>^ Get(IVector<JetBlueColumn ^>^ columns) de l’objet Records. Cette méthode, retourne la même collection de colonnes, mais avec leur propriété Value renseignée par la valeur contenue dans les champs de la table.

Puis on se déplace dans la table ou dans le curseur, avec la méthode MoveResult Move(MoveDirection direction) de l’objet Records.

Note : Comme dans une requête SQL classique, plus il y a de colonnes moins c’est performant. Soyez donc vigilant sur ce point. Dans notre exemple cela correspond à un “Select * from TETable”.

Lorsqu’il y a beaucoup d’enregistrements dans une table, parcourir l’intégralité de la table, peut s’avérer peut efficace. Il pourrait être judicieux, de gérer un mécanisme de chargement incrémental comme c’est possible de le faire avec XAML. Pour ce faire, vous avez à votre disposition des méthodes qui prennent en paramètre, le nombre d’enregistrements à retrouver. Ceci est d’autant plus efficace, car le déplacement dans le curseur (Move(Next)), reste dans les frontières du composant et évite les allez et retour entre les frontières des langages Javascript, VB et C#.

La méthode IVector<IVector<JetBlueColumn^>^>^ Get(IVector<JetBlueColumn^> ^columns,unsigned int recordperpage) , par exemple peut être utilisée à cette effet comme dans les différents extraits de code suivant :

Code VB

Dim records = t1.Records.Get(columns, 5)
                For Each record In records
                    Dim name = record(1).Value + " " + record(0).Value
                    Dim subtitle = record(4).Value.ToString()
                    Dim picturePath = record(3).Value.ToString()
                    Dim content = record(5).Value.ToString()
                    group1.Items.Add(New SampleDataItem("Group-1-Item-1" + count.ToString(),
                       name,
                       subtitle,
                       picturePath,
                      content,
                      content,
                      group1))
                    count += 1
                Next

Code C#

    var records = t1.Records.Get(columns, 5);
                 foreach (var record in records)
                 {
                     var name = record[1].Value + " " + record[0].Value;
                     var subtitle = record[4].Value.ToString();
                     var picturePath = record[3].Value.ToString();
                     var content = record[5].Value.ToString();
                     group1.Items.Add(new SampleDataItem("Group-1-Item-1" + count.ToString(),
                        name,
                        subtitle,
                        picturePath,
                       content,
                       content,
                       group1));
                     count += 1;
                 }

 

Code C++/CX

     auto records=t1->Records->Get(columns,5);
                     int count=0;
                             for each (auto    record  in records)
                             {
                                 auto name = record->GetAt(1)->Value + " " + record->GetAt(0)->Value;
                                 auto subtitle = record->GetAt(4)->Value->ToString();
                                 auto picturePath = record->GetAt(3)->Value->ToString();
                                 auto content = record->GetAt(5)->Value->ToString();
                                 group1->Items->Append(ref new SampleDataItem("Group-1-Item-1" + count.ToString(),
                                    name,
                                    subtitle,
                                    picturePath,
                                   content,
                                   content,
                                   group1));
                                 count += 1;
                             }

Code Javascript

  var records = t1.records.get(columns, 5);
                    records.forEach(function (record) {
                                var name = record[1].value + " " + record[0].value;
                                sampleItems.push({ group: sampleGroups[0], title: name, subtitle: record[4].value, description: record[5].value, content: record[5].value, backgroundImage: record[3].value });
                            });
                    DataArrived(sampleItems);

Pour obtenir les enregistrements suivants, il suffit de relancer la même procédure et ainsi de suite jusqu'à épuisement.

Le Windows Runtime étant fortement asynchrone, donc pour éviter de figer l’interface, vous avez la possibilité d’utiliser

IAsyncOperation<IVector<IVector<JetBlueColumn^>^>^>^ GetAsync(IVector<JetBlueColumn^> ^columns, unsigned int recordperpage)

avec les mêmes paramètres

Code Javascript

   t1.records.getAsync(columns, 10).then(function (records) {

                        records.forEach(function (record) {
                            var name = record[1].value + " " + record[0].value;
                            sampleItems.push({ group: sampleGroups[0], title: name, subtitle: record[4].value, description: record[5].value, content: record[5].value, backgroundImage: record[3].value });
                        });
                        DataArrived(sampleItems);
                            
                        t1.close;
                        db.close;
                        m_engine.endSession();
                    });

 

Code C++/CX

task<IVector<IVector<JetBlueColumn^>^>^> taskGet(t1->Records->GetAsync(columns,100));
                         taskGet.then([this,group1,t1,db,m_engine](IVector<IVector<JetBlueColumn^>^>^ records)
                         {
                             int count=0;
                             for each (auto    record  in records)
                             {
                                 auto name = record->GetAt(1)->Value + " " + record->GetAt(0)->Value;
                                 auto subtitle = record->GetAt(4)->Value->ToString();
                                 auto picturePath = record->GetAt(3)->Value->ToString();
                                 auto content = record->GetAt(5)->Value->ToString();
                                 group1->Items->Append(ref new SampleDataItem("Group-1-Item-1" + count.ToString(),
                                    name,
                                    subtitle,
                                    picturePath,
                                   content,
                                   content,
                                   group1));
                                 count += 1;
                             }
                             t1->Close();
                             db->Close();
                             m_engine->EndSession();
                         },task_continuation_context::use_current());                    

Il existe d’autres méthodes pour récupérer les données en fonction des critères de recherches, que j’aborderais dans mon prochain billet.

Mais je finirai ce billet par une dernière méthode asynchrone,

IAsyncActionWithProgress <IVector<JetBlueColumn^>^>^ GetWithProgressionAsync(IVector<JetBlueColumn^> ^columns)

qui permet de récupérer enregistrement par enregistrement en mode asynchrone, dont voici un extrait de code en Javascript et qui peut être utilisé pour afficher enregistrement par enregistrement sans figer l’interface.

Code Javascript

var index = 0;
                    t1.records.getWithProgressionAsync(columns).then(function () {
                        t1.close;
                        db.close;
                        m_engine.endSession();
                    }, function (error) {
                        
                    }, function (record) {
                        var name = record[1].value + " " + record[0].value;
                        sampleItems.push({ group: sampleGroups[0], title: name, subtitle: record[4].value, description: record[5].value, content: record[5].value, backgroundImage: record[3].value });
                        ItemArrived(sampleItems, index);
                        index += 1;
                    });

 

Dans la partie II, nous abordons

  • La création d’une table avec des colonnes multi-valeur, et la manière de récupérer les données de cette colonne
  • La sauvegarde d’images sous formes de BLOB dans une table
  • L’utilisation des champs de type DateTime
  • La création de table avec des indexes
  • L’utilisation des indexes pour une recherche multicritères

Télécharger les exemples et les binaires.

Eric