Paginer les données côté serveur, mettre en cache côté client



Documents pareils
.NET - Classe de Log

1. Base de données SQLite

ECR_DESCRIPTION CHAR(80), ECR_MONTANT NUMBER(10,2) NOT NULL, ECR_SENS CHAR(1) NOT NULL) ;

Création et Gestion des tables

Rapport de Mini-Projet en ArcGIS Engine

Encryptions, compression et partitionnement des données

Gestion du cache dans les applications ASP.NET

Utilitaires méconnus de StrataFrame

Les déclencheurs. Version 1.0. Grégory CASANOVA

Olivier Mondet

Cours Bases de données 2ème année IUT

SQL Server et Active Directory

ADO.NET. Ado.net propose deux modes d'accès, le mode connecté et le mode déconnecté.

Utilisation de JAVA coté Application serveur couplé avec Oracle Forms Hafed Benteftifa Novembre 2008

PHP et mysql. Code: php_mysql. Olivier Clavel - Daniel K. Schneider - Patrick Jermann - Vivian Synteta Version: 0.9 (modifié le 13/3/01 par VS)

BIRT (Business Intelligence and Reporting Tools)

CREATION WEB DYNAMIQUE

Gestion de stock pour un magasin

Bases de données Oracle Virtual Private Database (VPD) pour la gestion des utilisateurs d applications

Création d'un site dynamique en PHP avec Dreamweaver et MySQL

1. Qu'est-ce que SQL? La maintenance des bases de données Les manipulations des bases de données... 5

Langage SQL : créer et interroger une base

Comment Connecter une Base de Données MySQL via un Driver JDBC Avec OpenOffice.org

Cette application développée en C# va récupérer un certain nombre d informations en ligne fournies par la ville de Paris :

Les Utilisateurs dans SharePoint

Exploiter les statistiques d utilisation de SQL Server 2008 R2 Reporting Services

PHP 5. La base de données MySql. A. Belaïd 1

Editer un script de configuration automatique du proxy

Rafraichissement conditionné d'une page en.net

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr

Les Triggers SQL. Didier DONSEZ. Université de Valenciennes Institut des Sciences et Techniques de Valenciennes

SUPPORT DE COURS / PHP PARTIE 3

Programmer en JAVA. par Tama

Notes de cours : bases de données distribuées et repliquées

Stockage du fichier dans une table mysql:

Procédures Stockées WAVESOFT ws_sp_getidtable Exemple : ws_sp_getnextsouche Exemple :... 12

Présentation Windows Azure Hadoop Big Data - BI

Licence de MIDO - 3ème année Spécialités Informatique et Mathématiques Appliquées

Créer un rapport pour Reporting Services

Rappel. Analyse de Données Structurées - Cours 12. Un langage avec des déclaration locales. Exemple d'un programme

Utilisation d objets : String et ArrayList

Les BASES de DONNEES dans WampServer

Le Langage De Description De Données(LDD)

PHP 4 PARTIE : BASE DE DONNEES

Langage propre à Oracle basé sur ADA. Offre une extension procédurale à SQL

Création d objet imbriqué sous PowerShell.

Création d'un questionnaire (sondage)

TD3: tableaux avancées, première classe et chaînes

TD Objets distribués n 3 : Windows XP et Visual Studio.NET. Introduction à.net Remoting

Soon_AdvancedCache. Module Magento SOON. Rédacteur. Relecture & validation technique. Historique des révisions

1-Introduction 2. 2-Installation de JBPM 3. 2-JBPM en action.7

ISC Système d Information Architecture et Administration d un SGBD Compléments SQL

Travaux Pratiques de Commande par ordinateur 1 TRAVAUX PRATIQUES

Chapitre 10. Les interfaces Comparable et Comparator 1

Programmation VBA/Excel. Programmation VBA. Pierre BONNET. Masters SMaRT & GSI - Supervision Industrielle P. Bonnet

Configurer la supervision pour une base MS SQL Server Viadéis Services

Mysql avec EasyPhp. 1 er mars 2006

Construction d un EDD avec SQL 2008 R2. D. Ploix - M2 Miage - EDD - Création

Projet de programmation (IK3) : TP n 1 Correction

Tenrox. Guide d intégration Tenrox-Salesforce. Janvier Tenrox. Tous droits réservés.

Whitepaper. Méthodologie de création de rapports personnalisés SQL Server Reporting Services

Module Com231A - Web et Bases de Données Notion 5 : Formulaires et utilisation des Bases de Données avec PHP

Définition des Webservices Ordre de paiement par . Version 1.0

SYNC FRAMEWORK AVEC SQLITE POUR APPLICATIONS WINDOWS STORE (WINRT) ET WINDOWS PHONE 8

Plateforme PAYZEN. Définition de Web-services

Sage 100 CRM Guide de l Import Plus avec Talend Version 8. Mise à jour : 2015 version 8

Java DataBaseConnectivity

Module Administration BD Chapitre 1 : Surcouche procédurale dans les SGBDS

Construire des plug-ins pour SAS Management Console SAS 9.1

Partie I : Créer la base de données. Année universitaire 2008/2009 Master 1 SIIO Projet Introduction au Décisionnel, Oracle

GUIDE D INSTALLATION DE L APPLICATION GECOL SUR

La persistance des données dans les applications : DAO, JPA, Hibernate... COMPIL 2010 francois.jannin@inp-toulouse.fr 1

SSIS Implémenter un flux

OpenPaaS Le réseau social d'entreprise

Introduction à JDBC. Accès aux bases de données en Java

Accès aux bases de données

AWS avancé. Surveiller votre utilisation d EC2

La double authentification dans SharePoint 2007

Business Intelligence

Attaques applicatives

Description des pratiques à adopter pour la mise à jour du layout en utilisant le gestionnaire de conception de Sharepoint 2013

PHP. Bertrand Estellon. 26 avril Aix-Marseille Université. Bertrand Estellon (AMU) PHP 26 avril / 214

Installation d'un serveur FTP géré par une base de données MySQL

Comment se connecter au dossier partagé?

MYXTRACTION La Business Intelligence en temps réel

Hébergement et configuration de services WCF. Version 1.0

Tous les autres noms de produits ou appellations sont des marques déposées ou des noms commerciaux appartenant à leurs propriétaires respectifs.

Excel et les bases de données

Quelques patterns pour la persistance des objets avec DAO DAO. Principe de base. Utilité des DTOs. Le modèle de conception DTO (Data Transfer Object)

Classes et Objets en Ocaml.

Excel avancé. Frédéric Gava (MCF)

Bases de données et fournisseurs de contenu

Historisation des données

Gestion des utilisateurs, des groupes et des rôles dans SQL Server 2008

Tutoriel de formation SurveyMonkey

Cahier Technique. «Développer une application intranet pour la gestion des stages des étudiants» Antonin AILLET. Remi DEVES

Premiers Pas en Programmation Objet : les Classes et les Objets

Serveur d'application Client HTML/JS. Apache Thrift Bootcamp

Transcription:

Paginer les données côté serveur, mettre en cache côté client Vous voulez sélectionner des lignes dans une table, mais celle-ci comporte trop de lignes pour qu il soit réaliste de les ramener en une seule fois sur le client. Vous allez alors «paginer» votre requête, organiser celle-ci en lots successifs qui vont récupérer les lignes voulues depuis le serveur. Quand vous aurez chargé une page de données, vous voudrez peut-être la conserver localement pour pouvoir y revenir sans avoir à le recharger depuis le serveur. Vous allez alors «mettre en cache» ces pages sur le client. Dans cet exemple, nous travaillerons sur la table Sales.SalesOrderDetails de la base de données AdventureWorks2008. Commençons par une requête qui nous permette de préciser la taille de la page de données (c est-à-dire le nombre de lignes par page), et le numéro de la page voulue. -- Demandons la deuxième page de SalesOrderDetail, -- chaque page est basée (ordonnée) sur SalesOrderID, SalesOrderDetailID -- et comporte 50 lignes DECLARE @pagesize AS INT, @pagenum AS INT; SET @pagesize = 50; SET @pagenum = 2; WITH PageSalesOrder AS (SELECT ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS rownum, FROM AdventureWorks2008.Sales.SalesOrderDetail ) SELECT rownum, FROM PageSalesOrder WHERE rownum > @pagesize * (@pagenum-1) AND rownum <= @pagesize * @pagenum ORDER BY rownum; Que fait cette requête? comment est-elle construite? Pour l essentiel, elle repose sur une CTE (Common Table Expression), qui va créer dans le jeu résultant une colonne de type Entier, dont le contenu va être le numéro de la ligne de l extraction basée sur les colonnes choisies. C est ce que fait le ROW_NUMBER( ) OVER (ORDER BY La requête extrait ensuite du jeu ramené par la CTE les lignes demandées. On pourrait penser que la CTE ramène toutes les lignes, puisque rien dans son corps ne semble donner de filtre. En réalité, si on regarde le plan d exécution

de cette requête, et en particulier l opérateur «Haut» qui indique le nombre de lignes ramenées par la CTE, on voit que cette CTE ne ramène que 100 lignes, c'est-à-dire les 2 pages de 50 lignes. La requête ne demande pas plus de lignes que ce qui est déductible du WHERE! c est toute la puissance des CTE qui est ici mise en jeu Cette approche (pagination à la demande), est adaptée à une navigation principalement vers l avant (on demande une page, puis sa suivante, etc..) sur de tables de volume moyen (moins de 5 millions de lignes). Après chaque requête, les pages demandées sont mises en cache sur le serveur, ce qui permettra au scan demandé par la requête suivante d être fait en mémoire, seules les nouvelles lignes demandées nécessiteront un accès physique. Il ne nous reste plus qu à «empaqueter» cette requête sur le serveur. Nous disposons de 2 solutions : une procédure stockée, ou bien (puisqu il s agit d une seule ligne de requête), une fonction table incluse. Les deux techniques retourneront le résultat sous forme d un jeu d enregistrements. Voici le code de création de la procédure stockée : CREATE PROCEDURE [dbo].[paginationadhoc] @pagesize int = 10, @pagenum int = 1 AS BEGIN SET NOCOUNT ON; WITH PageSalesOrder AS (SELECT ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS rownum, FROM AdventureWorks2008.Sales.SalesOrderDetail ) SELECT rownum,

END FROM PageSalesOrder WHERE rownum > @pagesize * (@pagenum-1) AND rownum <= @pagesize * @pagenum ORDER BY rownum; Cette procédure stockée attend les 2 paramètres nécessaires, mais des valeurs par défaut leur sont attribuées (nous verrons plus loin comment les utiliser). Voici un exemple d utilisation de cette procédure stockée, qui demande la 50 ème page de 100 lignes : EXECUTE [tests].[dbo].[paginationadhoc] @pagesize = 100, @pagenum = 50 Remarquez l utilisation directe des paramètres demandés, sans déclaration préalable, en leur affectant leur valeur. Cette syntaxe permet de passer les paramètres dans l ordre qui nous convient, mais les noms des paramètres doivent être exactement ceux qui sont déclarés dans le corps de la procédure stockée. Pour la fonction table incluse, il nous faut supprimer la clause ORDER BY de cette requête, celle-ci étant interdite dans les fonctions tables incluses, qui sont quasiment des vues. Voici le code de création de la fonction : CREATE FUNCTION [dbo].[getpagefromsalesorderdetails] ( @pagenum int, @pagesize int ) RETURNS TABLE AS RETURN ( WITH PageSalesOrder AS (SELECT ROW_NUMBER() OVER (ORDER BY SalesOrderDetailID) AS rownum, FROM AdventureWorks2008.Sales.SalesOrderDetail ) SELECT rownum, FROM PageSalesOrder WHERE rownum > @pagesize * (@pagenum-1) AND rownum <= @pagesize * @pagenum ) Nous devrons mettre ce ORDER BY dans la requête appelante, dont voici un exemple qui demande la 50 ème page de 100 lignes :

SELECT rownum, FROM [tests].[dbo].[getpagefromsalesorderdetails] (50, 100) ORDER BY rownum Comment allons-nous utiliser cette procédure stockée et cette fonction depuis VFP, et depuis un Business Object de StrataFrame? Pour VFP, je vous propose de créer un CursorAdapter pour accéder à ces données. J ai ici simplement repris le code généré par l Explorateur de données. LOCAL oca as CursorAdapter LOCAL oconn as ADODB.Connection LOCAL ors as ADODB.Recordset LOCAL ocmd as ADODB.Command LOCAL oexception AS Exception LOCAL cconnstring * Gestion de la Connexion - Insérez le code de la connexion cconnstring = [Provider=SQLOLEDB.1;Data Source=acervistalevy\sql2008;Initial Catalog=tests;User ID=;Password=;Integrated Security=SSPI] TRY oconn = createobject("adodb.connection") * Vérifez que vous disposez des identifiants de connexion (utilisateur et mot de passe) * s'ils ne sont pas spécifiés dans la chaîne de connexion. * ex. oconn.open(cconnstring, userid, password) oconn.open(cconnstring) ors = CREATEOBJECT("ADODB.Recordset") ors.activeconnection = oconn ocmd=createobject("adodb.command") ocmd.activeconnection=oconn oca=createobject("cursoradapter") oca.datasourcetype = "ADO" oca.datasource = ors oca.mapbinary =.T. oca.mapvarchar =.T. oca.addproperty("pagesize",100) oca.addproperty("pagenum",50) oca.alias = [PageDeSalesOrderDetail] Pour plus de modularité, je crée les 2 propriétés qui stockeront les valeurs des paramètres à passer à la procédure stockée ou à la fonction. Seul le SelectCmd du CursorAdapter va être différent, selon qu on utilise la procédure stockée ou la fonction. Utilisation de la procédure stockée oca.selectcmd="exec [PaginationAdHoc] @pagesize =?oca.pagesize, @pagenum =?oca.pagenum"

Utilisation de la fonction oca.selectcmd=; "SELECT "+; "rownum,"+; ""+; ""+; ""+; ""+; ""+; " "+; "FROM [tests].[dbo].[getpagefromsalesorderdetails] (?oca.pagenum,?oca.pagesize) "+; "ORDER BY rownum" Dans les deux utilisations, remarquez bien le passage de paramètre avec le point d interrogation précédent le nom de la variable :?oca.pagenum et?oca.pagesize. C est la seule façon de se protéger contre une éventuelle injection SQL. Et dans StrataFrame? Il nous faut d abord créer le Business Object, et le mapper sur les champs qui seront générés par la procédure stockée ou par la fonction. La procédure stockée ainsi que la fonction créent un champ qui n existe pas dans la table sous-jacente (le RowNum), et ne ramènent pas tous les champs de cette table. Nous allons donc créer une vue, dont l usage sera limité à l établissement du mappage ; une fois celui-ci terminé, nous pourrons la supprimer. Voici le code de la vue : CREATE VIEW Pagination AS SELECT rownum, FROM dbo.getpagefromsalesorderdetails (DEFAULT, DEFAULT) Pour cette vue, nous utilisons la fonction créée précédemment, en lui passant les paramètres par défaut. Nous pouvons alors effectuer le mappage : la vue que nous venons de créer est visible comme une source de données, de la même façon qu une table.

N oublions pas de forcer la définition de la clé primaire sur le mappage, puisqu il s agit d une vue non indexée : ceci permettra au générateur de mappage d implémenter correctement toutes les propriétés qui seront éventuellement utilisées (dont la propriété PrimaryKeyField). Je reprends ici les champs utilisés comme PK dans la table sousjacente à la vue. (on pourrait supprimer maintenant la vue créée sur le serveur) Notre structure est prête, il nous faut maintenant la remplir avec les données que nous allons chercher sur le serveur. À partir de ce point, je vous propose d utiliser la procédure stockée (on aurait pu tout aussi bien utiliser la fonction table en ligne). Nous avons 2 possibilités : utiliser une méthode FillByStoredProcedure, ou une méthode FillDataTable.

Utilisation de FillByStoredProcedure Public Sub PopulateByStoredProcedure(ByVal PageNum As Integer, ByVal PageSize As Integer) Dim lonum As SqlParameter Dim lopage As SqlParameter lonum = New SqlParameter("@PageNum", Data.SqlDbType.Int) lopage = New SqlParameter("@PageSize", Data.SqlDbType.Int) End Sub lonum.value = PageNum lopage.value = PageSize Me.FillByStoredProcedure("dbo.PaginationAdHoc", lopage, lonum) Utilisation de FillDataTable Public Sub PopulateByFillDataTable(ByVal PageNum As Integer, ByVal PageSize As Integer) Dim losql As New SqlCommand losql.parameters.addwithvalue("@pagenum",pagenum) losql.parameters.addwithvalue("@pagesize",pagesize) losql.commandtext="exec dbo.paginationadhoc @PageSize, @PageNum" Me.FillDataTable(loSQL) End Sub Le code exécuté sur le serveur est le même (vous pouvez le vérifier en lançant une trace au Profiler SQL), le résultat est identique. Chaque fois que l une ou l autre de ces procédure est appelée, le contenu du BO est remplacé par la nouvelle page de données ; comment pourrions-nous mettre en cache cette nouvelle page, pour ne pas avoir à la rappeler depuis le serveur si nous en avons encore besoin? comment pourrions-nous ajouter la nouvelle page aux données déjà récupérées, si nous ne l avons pas déjà fait? Commençons par créer des fonctions Get identiques à nos méthodes Fill déjà créées : les fonctions Get fonctionnent comme des méthodes Fill, mais au lieu de peupler le Business Object, elles renvoient une datatable. Public Function GetPageByStoredProcedure _ (ByVal PageNum As Integer, ByVal PageSize As Integer) _ As DataTable Dim lonum As SqlParameter Dim lopage As SqlParameter lonum = New SqlParameter("@PageNum", Data.SqlDbType.Int) lopage = New SqlParameter("@PageSize", Data.SqlDbType.Int) lonum.value = PageNum lopage.value = PageSize Return Me.GetByStoredProcedure("dbo.PaginationAdHoc", lopage, lonum) End Function Public Function GetPageByGetDataTable _ (ByVal PageNum As Integer, ByVal PageSize As Integer) _ As DataTable Dim losql As New SqlCommand losql.parameters.addwithvalue("@pagenum", PageNum) losql.parameters.addwithvalue("@pagesize", PageSize) losql.commandtext = "EXEC dbo.paginationadhoc @PageSize, @PageNum"

Return Me.GetDataTable(loSQL) End Function Pour la suite de notre exemple, nous utiliserons la méthode PopulateByStoredProcedure, et la fonction GetPageByStoredProcedure. Nous aurons besoin de façon persistante de la taille des pages, du numéro de la page à obtenir ou en cours, de la liste des pages déjà mises en cache ; il nous faudra aussi savoir si nous voulons écraser les données ou les ajouter, et si nous voulons conserver en cache les pages de données. Créons donc dans notre Business Object les propriétés nécessaires. #Region "Propriétés de Pagination" Private PagesPresentes As ArrayList = New ArrayList() Private _PageSize As Integer = 1000 Private _PageNum As Integer = 1 Private _CachePages As Boolean = False Private _AdditiveMode As Boolean = True <Category("Pagination"), _ Description("Taille de la page de données"), _ Browsable(True)> _ Public Property PageSize As Integer Get Return _PageSize End Get Set(ByVal value As Integer) _PageSize = value End Set End Property <Category("Pagination"), _ Description("Numéro de la page"), Browsable(True)> Public Property PageNum As Integer Get Return _PageNum End Get Set(ByVal value As Integer) _PageNum = value End Set End Property <Category("Pagination"), _ Description( _ "En mode Additive, les pages sont ajoutées au BO," _ &" en mode NON additive, chaque page écrase le contenu du BO"), _ Browsable(True)> Public Property AdditiveMode As Boolean Get Return _AdditiveMode End Get Set(ByVal value As Boolean) _AdditiveMode=value End Set End Property <Category("Pagination"), _ Description("Mise en cache local des pages récupérées"), _ browsable(true)> Public Property CachePages As Boolean Get Return _CachePages

End Get Set(ByVal value As Boolean) _CachePages=value End Set End Property #End Region Il ne nous reste plus qu à créer les méthode FillNextPage et FillPreviousPage, en prenant en compte nos options possibles. Voici le code de la méthode FillNextPage : ''' <summary> ''' Remplit le BO avec la page suivante: ''' En mode Additive, si la page n'a pas déjà été chargée, ''' on la charge depuis le serveur et on l'ajoute à celle en cours ''' ''' en mode NON additive, si le CachePages est True et que la page a déja été chargée, ''' on la remonte depuis le cache ''' sinon on la recharge depuis le serveur ''' </summary> ''' <param name="additive"></param> ''' <remarks>le cache est implémenté par des snapshots nommés</remarks> Public Sub FillNextPage(Optional ByVal Additive As Boolean = False) End If Additive ' On charge la page uniquement si elle n'a pas été déjà chargée ' (donc présente dans le tableau des PagesPresentes) if Not Me.PagesPresentes.Contains(Me.PageNum+1) Then Dim lopage As DataTable = Me.GetPageByStoredProcedure(Me.PageNum + 1, Me.PageSize) Me.CopyDataFrom(loPage, BusinessCloneDataType.AppendDataToTableFromCompleteTable) Else If Me.CachePages=True 'si la page est déjà dans le cache, on la recharge depuis le cache 'sinon, on Populate et on la met en cache Dim SnapPageNum As Integer=Me.PageNum+1 Dim SnapPage As String="Snap"+SnapPageNum.ToString If Me.PagesPresentes.Contains(SnapPageNum) Me.RestoreCurrentDataTableSnapshot(SnapPage,true) Else ' si la SnapPage demandé n'existe pas, on doit la charger, et en faire un snapshot Me.PopulateByStoredProcedure(SnapPageNum, Me.PageSize) Me.SaveCurrentDataTableToSnapshot(SnapPage) else Me.PopulateByStoredProcedure(Me.PageNum + 1, Me.PageSize) Me.PageNum += 1 Me.PagesPresentes.Add(Me.PageNum) En mode «Additive», on veut rajouter la page suivante après les données présentes dans la DataTable du BO, uniquement si on ne l a pas déjà fait. On commence donc par vérifier que cette page n est pas présente dans le tableau (ArrayList) «PagesPresentes». Si cette page n y figure pas, on la crée en appelant la fonction GetPageByStoredProcedure, et on utilise simplement la méthode CopyDataFrom présentée par StrataFrame pour l ajouter après les données actuelles.

En mode non Additive, si on ne veut pas mettre les pages en cache (la propriété CachePages est à False), il nous suffit d appeler la méthode PopulateByStoredProcedure ; les données de la page suivante remplacent alors la page en cours. Si on a choisi la mise en cache, on commence par vérifier si cette page n est pas déjà présente (comme cidessus). Si cette page n existe pas, nous la chargeons dans le BO par la méthode PopulateByStoredProcedure, et nous la mettons en cache en utilisant la méthode SaveCurrentDataTableToSnapshot présentée par StrataFrame. Si la page est déjà en cache, il nous suffit de la restaurer par la méthode RestoreCurrentDataTableSnapshot. Ces deux méthode permettent de nommer les snapshot conservés par une clé de type String. La méthode FillPreviousPage est quasiment identique : ''' <summary> ''' Remplit le BO avec la page suivante: ''' En mode Additive, si la page n'a pas déjà été chargée, ''' on la charge depuis le serveur et on l'ajoute à celle en cours ''' par un Merge pour mettre la nouvelle page en tête de l'existant ''' ''' en mode NON additive, si le CachePages est True et que la page a déja été chargée, ''' on la remonte depuis le cache ''' sinon on la recharge depuis le serveur ''' </summary> ''' <param name="additive"></param> ''' <remarks></remarks> ''' Public Sub FillPreviousPage(Optional ByVal Additive As Boolean = False) If Me.PageNum > 1 Then If Additive Then ' On charge la page uniquement si elle n'a pas été déjà chargée ' (donc présente dans le tableau des PagesPresentes) if Not Me.PagesPresentes.Contains(Me.PageNum-1) Then Dim lopage As DataTable = Me.GetPageByStoredProcedure(Me.PageNum - 1, Me.PageSize) lopage.merge(me.currentdatatable) Me.CopyDataFrom(loPage, BusinessCloneDataType.ClearAndFillFromCompleteTable) Else If Me.CachePages=True 'si la page est déjà dans le cache, on la recharge depuis le cache 'sinon, on Populate et on la met en cache Dim SnapPageNum As Integer=Me.PageNum-1 Dim SnapPage As String="Snap"+SnapPageNum.ToString End If Me.PagesPresentes.Contains(SnapPageNum) Me.RestoreCurrentDataTableSnapshot(SnapPage,true) Else ' si la SnapPage demandé n'existe pas, on doit la charger, et en faire un snapshot Me.PopulateByStoredProcedure(SnapPageNum, Me.PageSize) Me.SaveCurrentDataTableToSnapshot(SnapPage) Else Me.PopulateByStoredProcedure(Me.PageNum - 1, Me.PageSize) Me.PageNum -= 1 La seule différence est en mode Additive : il faut mettre la nouvelle page avant les données en cours. On va utiliser la métode native.net Merge pour fusionner la page en cours (la CurrentDataTable) après la nouvelle page, et copier cet ensemble dans la DataTable du BO, avec la méthode StrataFrame CopyDataFrom en lui passant le paramètre ClearAndFillFromCompleteTable.

Pour tester ce Business Object, créez un form dérivé de la classe StandardForm de StrataFrame, jetez-y ce BO, ajoutez un DataGridView natif de.net, un BusinessBindingSource de StrataFrame pour lier ce grid au BO. Définissez la valeur de PageNum à 0. Un double click sur le BO en design du form nous amène sur la méthode ParentFormLoading que nous implémentons simplement comme suit : Private Sub BO_PageSalesOrderDetail1_ParentFormLoading() _ Handles BO_PageSalesOrderDetail1.ParentFormLoading Me.BO_PageSalesOrderDetail1.FillNextPage() End Sub Puis nous ajoutons les boutons cmdpagesuivante et cmdpageprecedente, dont nous implémentons les méthodes click ainsi : Private Sub cmdpagesuivante_click( ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdpagesuivante.click Me.BO_PageSalesOrderDetail1.FillNextPage(Me.BO_PageSalesOrderDetail1.AdditiveMode) End Sub Private Sub cmdpageprecedente_click( ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles cmdpageprecedente.click Me.BO_PageSalesOrderDetail1.FillPreviousPage(Me.BO_PageSalesOrderDetail1.AdditiveMode) End Sub Modifiez les valeurs de la taille de la page, passez en mode Additive, avec et sans le cache