Questions.doc
Transcription
Questions.doc
Spécialiste Visual Basic Formation et développement [email protected] Réponses à vos questions Plusieurs des questions que je reçois par courrier électronique de mes étudiants après la formation peuvent en intéresser d’autres. J’ai donc amassé une petite collection de ces questions et de mes réponses, en espérant qu’elles pourront vous être utiles. Veuillez excuser les fautes d’orthographes et la syntaxe, je vous présente les questions et les réponses telles quelles, n’ayant pas le temps de faire les corrections. De nouvelles questions s’ajoutent de temps à autre. Vous pouvez consulter la copie la plus récente en tout temps en format pdf, sur notre site Web, à http://www3.sympatico.ca/jbfi/questions.pdf ADO.NET Passer des guillemets et apostrophes dans un objet Command ....................................................5 Récupérer la valeur par défaut d’un champ dans la base de données .........................................23 Classes Références circulaires ...................................................................................................................24 Collections Utiliser une clé plutôt que l’indice ..................................................................................................14 ComboBox DoubleClick ne fonctionne pas ........................................................................................................4 Emplir un ComboBox avec un DataReader ..................................................................................21 Contrôles Référencer un contrôle d’un formulaire à l’autre ..............................................................................6 DataReader Emplir un ComboBox avec un DataReader ..................................................................................21 Événements DoubleClick – ComboBox ................................................................................................................4 1 Formulaires Formulaires MDI .............................................................................................................................19 Référencer un contrôle d’un formulaire à l’autre ..............................................................................6 MDAC Problèmes avec les différentes versions et mises à jour ..............................................................13 SQL Server et MDAC ....................................................................................................................12 MDI Formulaires MDI .............................................................................................................................19 SQL Server Comment se préparer à passer à SQL Server à partir d’Access ....................................................3 Installation minimum sur le client ...................................................................................................12 SQL Server et MDAC ....................................................................................................................12 VB.NET vs VB6 Conversion de VB6 à VB.NET........................................................................................................16 Les applications VB.NET demandent plus de mémoire ................................................................16 VB vs C# VB ou C#? Lequel est le meilleur? ..................................................................................................7 2 Quelles sont, en gros, les choses importantes à considérer si on monte une application avec une BD Access 2000 et qu'ensuite on veuille éventuellement la mettre sur SQL Serveur ? (d'ici un ou 2 ans... ) Passer les tables de Access à SQL Server ne cause généralement pas de problèmes. Ce sont les requêtes et le code qui ne passent pas bien. Pour les requêtes, il y a une couple de choses à éviter : - Référencer des contrôles dans les formulaires. C'est généralement fait dans les critères, pour filter le résultat sur une sélection ou une valeur saisie par l'utilisateur dans un formulaire. SQL Server ne voyant pas les formulaires, il ne peut les référencer. Il faut faire des requêtes dont les critères sont des paramètres, et passer ces paramètres dans du code. - Dans les requêtes avec paramètres, il est important de ne pas se contenter de spécifier les paramètres à passer en les mettant simplement entre crochets [paramètre], ce qui est suffisant en Access. Il faut en plus spécifier les paramètres dans les propriétés de la requête. - Éviter de référencer des procédures, particulièrement des procédures qu'on a écrites soi-même. SQL Server est capable de reconnaître certaines procédures qui sont standard en SQL, mais pas celles qui sont spécifiques à Access (par exemple le DLookup) ou qu'on a écrites nous-mêmes. Si le stagiaire ne connaît pas assez bien SQL Server pour savoir quelles sont les procédures reconnues par Transact-SQL, il est mieux de ne pas du tout utiliser des procédures dans les champs calculés ou dans les critères. Pour le code : - Si on ajoute du code dans les formulaire et les rapports, ou si on crée des modules, il faut utiliser la librairie ADO au lieu de DAO. Le code ADO fonctionnera à peu près tel quel sur SQL Server, mais le code DAO est spécifique à Access. Tous les trucs énumérés sont des chose qu'on fait couramment en Access, mais qui ne passent pas dans SQL Server. Ce sont celles qui m'ont personnellement causé des problèmes. Il y a peut-être aussi de mauvaises techniques (Access étant conçu au départ pour monsieur-tout-lemonde, il est assez permissif) à éviter, mais comme toutes les expériences de conversion que j'ai eues étaient sur des bases de données que j'avais moi-même conçues, je ne pourrais les identifier (naturellement, je n'utilise jamais de mauvaises techniques :-)). Ne pas oublier qu'Access 2000 vient avec un truc qui s'appelle le MSDE qui est une version réduite de SQL Server, mais qui est limité, dans la pratique, à 5 utilisateurs simultanés (en théorie, c'est une vingtaine, mais en pratique, ça commence à ralentir considérablement après 5). Si ça vous convient, ça fait des bases de données qui vont passer directement à SQL Server quand vous serez prêt. Ça implique cependant que le stagiaire est déjà à l'aise avec la conception des tables, schémas, vues et procédures de SQL Server. 3 L’événement DoubleClick ne semble pas fonctionner dans les ComboBox. Effectivement, ça a été confirmé par Microsoft : It is because the ComboBox control doesn't support DoubleClick event. The ComboBox control has a textbox inside it, which "eat" the DoubleClick event to perform the default predefined behavior - selecting word. Though the event can be seen and handled in the IDE, it will not fire at runtime. La solution est de simuler le DoubleClick en utilisant un Timer : Timer1.Interval = SystemInformation.DoubleClickTime 'Dans l’événement Load 'du formulaire Private Sub Timer1 (ByVal sender As Object, ByVal e As System.EventArgs) Handles Timer1.Tick tmrTimer.Enabled = False 'Mettre ici le code que vous auriez mis sur le Click, au besoin 'Ça va être exécuté avec un petit délai, mais ce n’est pas grave ' dans la plupart des situations End Sub 'Modification de l’événement Click, qui agit à la place du DoubleClick Private Sub Combo1_Click (ByVal sender As Object, ByVal e As System.EventArgs) Handles Combo1.Click If Timer1.Enabled Then 'Mettre ici le code que vous auriez mis dans le DoubleClick Else Timer1.Start() End If End Sub 4 On fait des "SqlCommand" dans une application et on a eu des erreurs à cause que les gens entraient des caractères spéciaux dans une "String" qu'on passait en paramètre (exemple: ' ou " ). Est-ce qu'il y a un moyen facile de valider une "String" pour capter les caractères spéciaux et les encoder/décoder au besoin ou bien, je dois me construire un objet qui ferait ce travail ? Le problème des apostrophes et guillemets est assez classique. En VB, un double guillemet ("") à l'intérieur d'une String est vu comme un simple guillemet : "WHERE Province = ""QC""" sera vu comme WHERE Province="QC" dans la base de données. La plupart des bases de données (SQL Server et Access et quelques autres) acceptent aussi bien un guillemet qu'un apostrophe comme délimiteur de String dans une commande SQL, de sorte que si on envoie ""Prud'homme"" qui donnera "Prud'homme" au lieu de 'Prud'homme' Ça règle le problème. La méthode Replace des Strings peut être utilisée pour faire les changements appropriés avant d'appeler la commande. Microsoft a un article sur ça, spécifique à ADO.NET à http://support.microsoft.com/default.aspx?scid=kb;en-us;311023. 5 En VB 6.0, j'ai un formulaire frmMain qui ouvre un formulaire frmChild. Dans frmChild, je suis capable d'aller lire la valeur d'une propriété d'un contrôle de frmMain en faisant par exemple xyz = frmMain.txtID.text En VB.NET, ça ne fonctionne plus. Comment faire ? Est-ce que je suis obligé d'initialiser une variable global pour échanger des données entre deux formulaires ou y a t'il une syntaxe particulière pour faire la même chose qu'avant ? VB.NET est plus pointu. Il ne crée par automatiquement une propriété permettant d'accéder à chacun des contrôles , parce que ça ouvre la porte à un certain nombre de bogues, particulièrement du fait que c'est en lecture-écriture. Un formulaire peut donc en faire "casser" un autre en VB6. C'est donc à toi de créer dans frmMain une propriété pour chacune des valeurs que tu veux manipuler. Ça permet entre autres de déterminer le niveau d'accès pour chacune de ces valeurs. Dans le cas que tu me soumets, ça ressemblerait à ceci : Public ReadOnly Property Toto() As String Get Return txtID.Text End Get End Property Tu pourrais alors faire ceci pour récupérer la valeur : xyz = frmMain.Toto 6 J’hésite entre VB et C#. Lequel des deux langages est le meilleur. Il semble avoir sur le marché présentement une demande pour le C#. Si je pouvais seulement avoir 10 cents à chaque fois qu’on m’a posé cette question, je pourrais prendre ma retraite toute de suite. Il n’y a pas d’avantage à utiliser un plutôt que l’autre. Le fait que les classes internes de .NET aient été en majorité écrites en C# laisse à penser que le C# est meilleur. C’est faux. Microsoft à privilégié C# pour deux raisons. La première est que le type de programmeur qui travaille sur des compilateurs ou sur des environnements aussi complexe que Visual Studio, incluant un système de 6500 classes où tout le monde hérite de tout le monde, a un background C++et va donc travailler plus facilement avec un syntaxe s’apparentant au C. La deuxième, c’est que C# étant nouveau, ils voulaient le raffiner et le déboguer. Comme il n’était pas prêt, ils ne pouvaient pas le tester sur de nouvelles applications. En développant le C# en parallèle avec l’outil dans lequel il est utilisé, ils pouvaient en même temps tester les concepts et s’assurer que le langage serait adéquat au moment de sa sortie. La syntaxe de VB étant déjà relativement développée, il y avait donc moins à faire. Ça a déjà été dit chez Microsoft : ça aurait aussi bien pu avoir été fait en VB. C’est penser en « mode Windows » et ne pas connaître ou comprendre .NET que de croire que parce que les dll sont en C#, C# en fera un meilleur usage. C’est uniquement une question de style et de goût. Mais pour répondre aux différentes interrogations, voici une petite discussion sur 3 aspects du sujet : une comparaison des deux langages, la perception que peut donner une lecture rapide du marché, et le futur des deux langages. Différences entre les langages Il n'y a fondamentalement pas de différence entre travailler en C# ou en Visual Basic dans .NET. Que vous travailliez dans un langage ou l'autre (ou même en COBOL, Fortran et Pascal, qui sont disponibles de compagnies indépendantes de Microsoft), le résultat d'une compilation .NET devrait en théorie donner le même résultat. Vous utilisez le même outils pour créer les formulaires, vous connecter à une base de données ou générer une application d'installation, peut importe le langage. Une variable Integer VB ou une variable int C#, ça finit compilé en Int32 .NET. C'est une classe qui fait partie de l'environnement .NET, et bien que le terme pour l'utiliser en VB soit différent de celui de C#, le résultat est exactement le même. C'est vrai pour les String, les dates, ou n'importe laquelle des 6500 (et plus dans la version 2003) classes fournies avec .NET. Vous pouvez généralement interchanger le code entre les langages avec presque pas de modifications. Voici un exemple, dans les deux langages, d'une routine qui exécute une procédure stockée dans une base de données. C’est le code en rouge qui fait le travail, le reste, c’est uniquement des déclarations de variables et une trappe d’erreur : 7 Visual Basic : Dim con As New OleDbConnection _ ("file name=..\..\Frizotte.udl") Dim com As OleDbCommand Dim dta As New OleDbDataAdapter() Dim dts As New DataSet() Dim par As New OleDbParameter() par.ParameterName = "@Pays" par.DbType = DbType.String par.Value = cboPays.Text com = New OleDbCommand() com.Connection = con com.CommandText = "spClientsPays" com.CommandType = CommandType.StoredProcedure com.Parameters.Add(par) dta.SelectCommand = com Try dta.Fill(dts) Catch erreur As OleDbException Debug.WriteLine(erreur) End Try dtgClients.DataSource = dts.Tables(0) C#: OleDbConnection con=new OleDbConnection ("file name=..\\..\\Frizotte.udl"); OleDbCommand com; OleDbDataAdapter dta=new OleDbDataAdapter(); DataSet dts=new DataSet(); OleDbParameter par=new OleDbParameter(); par.ParameterName = "@Pays"; par.DbType = DbType.String; par.Value = cboPays.Text; com=new OleDbCommand(); com.Connection = con; com.CommandText = "spClientsPays"; com.CommandType = CommandType.StoredProcedure; com.Parameters.Add(par); dta.SelectCommand = com; try { dta.Fill(dts); } catch (OleDbException erreur) { Debug.WriteLine(erreur); } dtgClients.DataSource = dts.Tables[0]; } 8 Si vous comparez les deux, vous constaterez qu'à part les déclarations de variables, l'utilisation des ; et des accolades {}, et des indices d'arrays définis par () en VB et [] en C#, le code est exactement le même. Il devrait aussi donner le même résultat à la compilation. Il n'y a aucun avantage particulier à utiliser un langage plutôt que l'autre. Les compilateurs sont différents, de sorte qu'il est possible que certaines routines soient plus efficaces en VB et d'autres plus efficaces en C#, mais en théorie, ils devraient générer le même code. Certaines options de l'environnement de développement ne fonctionnent que pour C#. Ainsi, en VB, vous devez recompiler complètement l'application dès que vous faites un petit changement. C# offre une option permettant de ne recompiler que les lignes modifiées depuis la dernière compilation. Ça accélère le développement de grosses applications en C#. Par contre, les événements sont beaucoup plus faciles à créer dans VB, qui génère automatiquement les procédures événementielles, alors qu'en C#, il faut les écrire à la main. Ça accélère le développement en VB. Vous utilisez donc les mêmes outils et les même concepts pour développer en VB et C#. Le code compilé devrait être à peu près identique, donc pas de différence de performance. Vous pourriez même avoir une application « bilingue », ayant un .EXE VB et un .DLL C# ou l'inverse. Le choix du langage n'est pas important. Ce qui fait la différence, c'est uniquement une question de goût personnel, habituellement basé sur l'expérience du programmeur. Les vieux programmeurs VB préfèrent Visual Basic .NET (ils sont habitués aux Dim et peuvent toujours utiliser leurs vieilles fonctions VB); les programmeurs qui ont une expérience plus forte en C ou en Java vont habituellement préférer le C#, qui est un mélange des deux syntaxes. Analyse du marché Si l’on se fie aux annonces de journaux, il semble y avoir une demande plus grande pour le C# que pour le Visual Basic. Il est difficile de savoir si c’est vraiment le cas, mais il nous semble que non. On parle plus du C#, mais il faut comprendre pourquoi. Dans un premier temps, le C# attire surtout les entreprises qui programment déjà en C ou en Java et qui veulent passer à .NET. Ces entreprises ont déjà compris l’intérêt de travailler dans un environnement objet, et voient l’intérêt de .NET. Dans les boîtes où l’on faisait du VB6, on ne sait pas vraiment ce qu’est un objet (les petites classes de VB6 n’étaient pas de vrais objets), et l’on ne voit pas vraiment pourquoi on investirait dans quelque chose de nouveau alors que ce qu’on a fonctionne. « Pourquoi se compliquer la vie avec le polymorphisme et l’héritage, on n’en a jamais senti le besoin »… donc on n’en saisi pas l’utilité. On ne comprend pas l’intérêt de passer à .NET. Je sens très bien ce phénomène sur le terrain : il y a actuellement (septembre 2003), deux à trois fois plus de demande pour des cours de VB6 que pour des cours de VB.NET. Les entreprises C et Java font le saut, mais pas les entreprises VB. Comme le C# attire d’avantage les programmeurs C++ et Java, la demande aurait à priori tendance à être un peu plus forte pour le C# à ce stade-ci, sans compter que certains programmeurs VB décident, tant qu’à apprendre quelque chose de nouveau, de passer à C#, alors que l’inverse n’existe à-peuprès pas. Pourtant, si l’on se fie uniquement aux inscriptions à nos cours, on constate à-peu-près 2 inscriptions en VB.NET pour 1 en C#. 9 Les journaux donnent l’impression du contraire? C# est nouveau. Les programmeurs C# sont donc plus rares que les programmeurs VB, et donc, plus en demande dans les petites annonces. Il est facile pour une entreprise de trouver à l’interne quelqu’un qui connaît VB et qui n’a qu’à se mettre à jour pour passer à VB.NET. Ce n’est pas le cas pour le C#. Il y a donc plus d’offres d’emplois en C#, parce qu’on doit sortir à l’extérieur. Si l’on se promène depuis quelques années dans l’univers .NET, autant chez Microsoft que dans les sites Web et les revues spécialisées, on constate qu’au lancement de Visual Studio .NET au début de 2002, il y avait plus d’articles C# que d’articles VB, dans une proportion d’environ 2 pour 1. Cette tendance semble s’inverser. Dans le numéro de juin du Visual Studio Magazine, il y a 1 seul article C#, 3 articles VB et 1 article mixte. Il y a exactement les mêmes proportions en août. En Juillet, c’était égal à 2 / 2 / 2. Ma perception est peut-être biaisée parce que j’ai plus tendance à rencontrer des programmeurs VB, mais il me semble qu’à l’exception des offres d’emplois, il y se fait plus de VB que de C#. Le futur des deux langages Lequel est le plus intéressant à long terme? Lequel va être privilégié par Microsoft? Microsoft a investi autant dans l’un que dans l’autre, et promet autant de travail dans l’un que dans l’autre. De leur part, on dit donc que les deux choix sont aussi valables. Par contre, les observateurs du milieu ont tendance à croire que les langages vont éventuellement se distancier. Il y a eu plusieurs articles et discussions à ce sujet ces derniers mois. À quoi sert un langage de programmation sans système d’opération pour faire fonctionner l’application? Microsoft a dans un premier temps concentré ses énergies à concevoir un environnement solide (le framework), pas à raffiner des langages. Il y a cependant beaucoup de « grogne » du côté de Visual Basic, et un grand nombre de programmeurs VB6 ne font pas le saut parce que : « ce n’est plus aussi simple qu’avant »1. Les programmeurs C trouvent qu’ils perdent du contrôle en C#, et en même temps que le C en « managed code » est inutile. Il n’y a pas de langage RAD (Rapid Application Development) dans le monde .NET. Ça fait penser à Windows à ses débuts. Il a fallu attendre la venue de VB pour pouvoir penser faire des applications en quelques jours au lieu de quelques mois. Bien des gens pensent que ça devrait encore être le rôle de VB, et croient que, bien que ce ne soit pas officiel, ce soit aussi l’idée de Microsoft. On le sent même déjà un peu, par exemple, pour la déclaration des procédures événementielles déjà mentionnée en comparant les deux langages. VB fait plus de choses automatiquement. On a donc tendance à croire qu’avec le temps, Microsoft sera porté à ajouter à C# des fonctionnalités le rapprochant du C parce que les programmeurs C# demanderont des fonctionnalités auxquelles ils étaient habitués en C, et porté à donner aux programmeurs VB le 1 Il n’y a pas à se le cacher, c’est plus complexe de programmer en VB.NET qu’en VB6. Un grand nombre d’individus se sont improvisés programmeur « professionnel » avec juste un peu d’expérience dans les versions antérieures de VB. Ce ne sera pas le cas avec VB.NET, ne seraitce que parce qu’ils n’auront pas la patience d’apprendre à s’en servir correctement. X=Y passe toujours la compilation en VB6, pas toujours en VB.NET. 10 choix d’utiliser plus d’outils permettant de simplifier et d’accélérer leur travail, comme ils étaient habitués à le faire « dans le temps ». En conclusion La transition de VB6 à VB.NET est plus aisée que de VB6 à C#, alors d’après moi, si VB6 faisait l’affaire pour vous, VB.NET la fera, seulement mieux et avec un outil plus solide, qui vous oblige à vous discipliner et à concevoir des applications plus faciles à maintenir, quoique un peu plus complexes à développer. Si VB n’était pas suffisant, vous faisiez probablement du C et aurez probablement encore à en faire. C# est donc alors plus adéquat. Même chose si vous êtes plus à l’aise avec la syntaxe de C ou Java, parce que la transition sera plus facile. 11 Minimalement qu'est-ce qu'il me faut installer sur un poste de travail pour utiliser une application ADO.NET qui accède à SQL Server? Ton application et le framework, naturellement, mais aussi : - Les dll d'accès aux bases de données, un truc que Microsoft appelle MDAC (Microsoft Data ACcess) et que tes utilisateurs ont peut-être déjà, même s'ils n'utilisaient que des bases de données Access dans le passé, mais qui vaudrait peut-être la peine d'être mis à jour. Tu peux trouver la version la plus récente à http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28001860. - Le client SQL. Ça peut s'installes CD de SQL Server, mais il y a plusieurs approche, incluant le MSDE dont on parle dans le manuel de cours. Je te suggère de consulter la documentation SQL Server sur l'installation du client. Un ancien étudiant m’a affirmé que l’installation de MDAC était suffisante, mais ce serait à vérifier. En ce qui me concerne, j’ai toujours installé le client SQL. Lorsqu'on installe un client SQL Server, automatiquement MDAC s'installe, dois-je supposer que c'est la bonne version de MDAC? J'imagine que oui. Tu n'as pas nécessairement la bonne version du MDAC, ça dépend de ta version de SQL Server. Ils ne sont pas faciles à suivre. D'après les spécifications, ça prend minimum MDAC 2.6 pour ADO.NET et SQL Server 2000, mais le 2.7 avec le Service Pack 1 est suggéré pour profiter de toutes les possibilités. Si je ne me trompe pas, l’installation du client SQL Server va te donner le MDAC 2.6, il y aurait donc avantage à installer le MDAC 2.7 séparément Si tu installes le client SQL Server 7, je crois que c’est le MDAC 2.5 qui s’installe, et ce n’est pas suffisant pour ADO.NET. On se demandait aussi si ADO.NET passe réellement par MDAC lorsqu'on utilise des SqlConnection pour SQL Server? En d'autres termes, MDAC semble être plus au niveau de OLEDB et ODBC. Tu as raison, ça joue surtout au niveau d'OLEDB et ODBC. ADO.NET a besoin de certaines composantes de MDAC. On ne passe pas par ODBC et OLEDB, mais il utilise certains éléments de MDAC. Je pense par exemple à la validation et à la création des connexions. Les ConnectionStrings ont exactement les mêmes syntaxes en ADO.NET qu'en ADO. Je ne serais donc pas surpris que ce soit une des composantes du MDAC qui établisse la connexion. 12 Est-ce que les différentes mise à jour de MDAC ont un impact sur ADO.NET lorsque utilisé avec SQL Server? Par exemple, est-ce qu'on pourrait avoir un type d'erreur chez un client qui exécute notre application avec une version de MDAC différente de celle qu'on s'attend? Oui, il est possible que tu aies des erreurs si ton client n'a pas la même version du MDAC.Normalement, la version du MDAC nécessaire fait partie des spécifications de l'application, et l’on essaie de motiver nos clients à faire des mises à jour régulières. À ce qu’on m’a dit, le MDAC se met à jour automatiquement au travers de Windows Update à partir de Windows XP. On lit des article sur MDAC qui nous font un peu peur en terme de déploiement: Terminal Server, SQL Server 7.0 en cluster non supporté par MDAC, les version MDAC venant avec le OS sur XP et pas avec les autres os... Beaucoup de problème en vue... MDAC, ce n'est pas une application en soi, c'est une série de dll, une centralisation de tous les dll d'accès aux données. Plutôt que de créer des problèmes, il corrige un problème qui était courant à l'époque. Dans le temps, il fallait mettre à jour ODBC, puis OLEDB, puis Access, puis SQL, etc. Ce sont tous des outils qui travaillent ensemble, mais qu'on mettait à jour séparément. On se retrouvait donc souvent avec des incompatibilités entre les versions. Microsoft a décidé de simplifier notre tâche en mettant tout dans MDAC. Ainsi, si une nouvelle version de SQL Server sort, il y a habituellement une nouvelle version du MDAC qui s'occupe à ce que tout ce beau monde puisse travailler avec la nouvelle version. MDAC venant avec le OS sur XP, c'est la nouvelle tendance. C'est comme le framework qui vient avec le OS sur Windows Server 2003. Ça aussi, ça correspond à un besoin qui s'est présenté dans le passé. Par exemple, on devait toujours installer le runtime de VB avec nos applications. C'était un peu absurde, parce que presque tous les ordinateurs se retrouvaient éventuellement avec ce runtime, mais avec des versions différentes, dépendant des applications qui s'y étaient installer. En distribuant le runtime avec le système d'opération, ça nous évite d'avoir à l'installer, et ça devient le rôle de Windows Update de s'assurer que la dernière mise à jour est toujours installée. Ça simplifie la vie de l'usager, et celle des concepteurs d'applications. Si tu lis des articles sur n'importe quoi, en informatique, tu vas avoir peur. Les systèmes sont rendus très complexes, y'en a pas deux qui a la même configuration d'appareils et de logiciels. C'est certain qu'il va y avoir des problèmes de temps en temps. J'ai déjà vu une liste de bogues VB6 qui faisait pas loin de 30 pages à raison d'une vingtaine de bogues par page. Ça fait peur. Pourtant, des millions de programmeurs travaillaient allégrement et avec un certain "plaisir" en VB6. Si on fouillait, on se rendait compte que la plupart de ces bogues étaient reliés à des configurations très particulières (lire rares), et qu'il y avait presque toujours une façon de les contourner. 13 En VB6, nous pouvions associer une clé (Key) à la function Add d'une collection, ce qui, je ne croit pas, être le cas aux 6 types de Collections en .NET. Est-ce qu'il y a une façon de faire en .Net pour simuler cette clé. Je sais qu'on peut utiliser l'objet Collection dans .Net mais je crois qu'il n'est pas natif, est-ce exacte? L'utilisation d'une clé facilite le travail mais diminue la performance. Chaque fois que tu références un objet par sa clé, VB doit consulter une table virtuelle pour savoir quel indice (les collections fonctionnent avant tout par indice, pas par clé) contient l'objet référencé par la clé. On conseillait donc autant que possible de ne pas utiliser les clés, sauf dans les cas où la performance n'était pas essentielle ou quand il y avait beaucoup de création/destruction d'objets et que le travail nécessaire à "suivre" un objet par son indice devenait trop difficile. En VB6, à moins de travailler à partir de rien pour créer nos propres classes collections, il n'y avait qu'un seul mécanisme pour créer des collections, et Microsoft a donc décidé d'y ajouter le truc de la clé qui est très commode dans certaines circonstances, comme tu le sais probablement déjà. En VB.NET, comme on a plusieurs classes collections, ils y sont allés avant tout pour la performance. L'utilisation des clés n'est essentielle que dans certaines situations, alors, comme tu l'as constaté, la plupart des collections n'offrent pas cette technique. Avant d'utiliser une clé, pose-toi la question suivante : est-ce que je fais ça uniquement par "paresse", parce que c'est plus facile, ou parce qu'il est très difficile de faire autrement? Si tu as des grosses collections, la performance en prend un coup quand tu recherches un élément par sa clé. Si tu ne peux pas t'en passer (y'a des cas où essayer de suivre les objets pas leur indice est plus pénalisant que d'utiliser la clé), tu as deux choix : --------------------1. La bonne vieille collection de VB6, qui est toujours disponible, mais qui fait partie du namespace Microsoft.VisualBasic. Ce n'est donc pas une collection de .NET. Je n'ai pas vraiment fouillé, mais j'ai l'impression que le compilateur VB génère quelque chose qui utilise les collections .NET en ajoutant du code pour gérer les clés. Dim z As New Collection ou, pour être plus précis Dim z As New Microsoft.VisualBasic.Collection t'offre les mêmes mécanismes qu'à l'époque, incluant une clé optionnelle. Le code écrit avec ce type de collection n'est cependant pas portable dans les autres langages, ce qui peut être un handicap dans certaines entreprises. --------------------2. Si tu veux être .NET pur, avec du code portable, les classes SortedList et HashTable sont des collections qui utilisent une clé. Je n'ai travaillé avec aucunes d'entre elles (mes collections sont presque toutes créées en héritant de CollectionBase, comme on apprend à le faire dans le cours objet, et dans ce cas, c'est toi qui crée la méthode Add, tu la fais comme tu veux), alors ce que je te dis n'est pas par expérience, mais seulement une analyse personnelle de la documentation. Donc, à prendre avec un grain de sel. En théorie, elle sont plus performantes que celle de VB parce qu'elle sont optimisée pour travailler avec une clé. En pratique, faudrait faire des tests. En théorie, la HashTable est plus performante, mais la SortedList, comme son nom l'indique, est triée sur la clé. Encore là, faudrait faire des tests, et ça peut dépendre du type d'utilisation qu'on en fait. 14 Leur handicap, c'est quelles fonctionnent uniquement avec une clé, il n'y a pas d'indice : Dim z As New Collections.SortedList ou Dim z As New Collections.Hashtable Pour un programmeur VB6, ça ne saute pas aux yeux. Les paramètres à ajouter au Add sont key As Object, value As Object. La clé peut être n'importe quel objet, incluant une String, qui est un objet comme tout le monde le sait. Son utilisation est donc semblable à celle de VB, sauf que les paramètres ne sont pas dans le même ordre, la clé passant en premier : z.Add ("Toto",<Objet à ajouter dans la collection>) ' Toto est la clé x = z.Item("Toto") Il n'y a, à ma connaissance, pas de collection .NET qui offre à la fois un indice et une clé. Seule la Microsoft.VisualBasic.Collection VB permet les deux. 15 J'ai commencé à jouer avec vb.net et la première chose que j'ai fait c'est de traduire une app vb6 à vb.net (je sais que ce n'est pas très conseillé) mais je fut très surpris de voir "engraisser" mon application de tel manière. Mon application ne contient qu'un Timer, 2 Labels, ainsi qu'un contrôle text et un textarea, ce qu'elle fait et bien tu choisis une application dans le textarea (path, hardcoder) et puis tu entres dans le contrôle text l'heure à laquelle tu veux que la dites application (ou process) se termine et vb la ferme et se ferme aussi. La conversion s'est faite presque sans anicroche sauf quelques petits détails (entre autre delegate pour des api de windows, qui m'ont un peu fait suer). Mais la surprise fut surtout au niveau de l'utilisation mémoire des 2 applications. VB6 = 2 Mo +- , vb.net = 12 Mo +-, pire encore lorsque je change mon textarea pour un FSO afin de rendre l'application plus complète alors c'est presque 17 Mo que l'applciation utilise. Est-ce normal ou c'est le fait de la convertir qui fait ça, sinon les app Vb.net sont très gourmandes en mémoire non? Ton rapport de 6 à 1 m'a a première vue surpris, mais si on y pense bien, c'est probablement normal. Je suis allé vérifier, et c'est à peu près ce que j'ai aussi pour la seule application que j'ai personnellement converti. Je suis cependant surpris par le 5 Mo supplémentaire pour le FSO. J'ai fait un test pour voir, et chez moi, utiliser le FSO avec un petit fichier d'une seule ligne n'ajoute qu'un seul Mo. La différence vient peut-être du fait que tu as un gros fichier chargé dans l'espace mémoire de l'application. Mais sans considérer l'Interop FSO, qui n'est supposé être qu'une mesure temporaire, pourquoi une si grande différence? Si tu jettes un coup d'oeil aux modules qui roulent en même temps qu'une application .NET (Debug -> Windows -> Modules à partir d'une application en mode Break), tu constateras que le run-time de .NET (la première entrée : mscorlib) fait environ de 10 Mo, contre 1.3 Mo pour le runtime de VB6. .NET utilise par défaut des classes qui assurent la sécurité et qui valident continuellement les opérations. Les variables de type primitif demandent beaucoup plus de mémoire, venant avec une série de propriété et méthodes (les méthodes sont cependant partagées entre toutes les variables de même type). Le rapport de grosseur entre les deux runtime est donc normal. Pour une application Windows il faut rajouter environ 2 Mo pour System.Windows.Forms.dll, sans compter System.Drawing.dll qui est requis par Forms. En VB6, tout ça était dans le run-time de VB. Si tu ajoutes par-dessus tout ça le fait que tu as une application convertie qui utilise mal les ressources de .NET et qui a besoin de dll supplémentaires pour assurer la compatibilité et fonctionner, ça commence à "faire du monde à la messe". Il y a donc nécessairement une augmentation de l'utilisation des ressources. Par contre, faut voir si les chiffres que tu as sont bien représentatifs de la réalité. Je suppose que tu t'es fié au Gestionnaire de tâches (Task Manager) de Windows. C'est un outil qui a toujours été assez approximatif, et c'est particulièrement le cas pour une application .NET. Il faut donc interpréter l'information qu'il nous donne. Dans un premier temps, il faut se souvenir qu'une application .NET roule dans l'environnement mémoire de .NET, pas celui de Windows. Il y a donc une communication entre .NET et le Task Manager pour simuler le fait qu'une application roule dans Windows, et ce n'est pas toujours très précis. Ta question m'a amené à faire quelques petits tests qui m'ont prouvé qu'on ne pouvait pas tellement se fier au Task Manager pour une application .NET. Tu devrais pouvoir reproduire ça très facilement chez toi. Note que je suis sur Windows XP Pro, il est possible que le comportement soit différent dans d'autres systèmes d'opération. J'assume que tu es à l'aise avec le code à écrire. Si ce n'est pas le cas, fais-le moi savoir et je t'enverrai un exemple tout fait. 16 1. Crée un projet ne contenant qu'un formulaire vide et deux boutons. Compile et lance-le à partir de bin. Note la mémoire utilisée. 2. Déclare une variable FSO au niveau de la classe qui définit ton formulaire, sans l'instancier (pas de New). Si tu lances l'application à nouveau, tu constateras, sans surprise, que ça occupe approximativement la même quantité de RAM. Un objet non instancié est simplement un pointeur. 3. Sous le premier des deux boutons, crée une instance du FSO avec un New. Sous le deuxième bouton, détruit l'instance en la mettant à Nothing. 4. Lance l'application avec le Task Manager en parallèle. Même utilisation de mémoire qu'auparavant. En cliquant sur le premier bouton, ça monte d'environ 1 Mo, le FSO vient de partir (interop + scrrun.dll + probablement une ou quelques autres librairies nécessaires pour les interop, parce que interop + scrrun.dll ne font pas 1 Mo). 5. Clique sur le deuxième bouton. Il détruit l'objet. On devrait récupérer la mémoire, mais ça ne se reflète pas dans le Task Manager. L'objet n'est plus présent dans l'application, il est rendu dans le Garbage Collector. C'est vrai que l'espace mémoire n'a pas été vraiment récupéré, mais on peut en réalité considérer que cette mémoire est disponible. On peut donc affirmer que l'affichage du Task Manager n'est pas représentatif de la vraie utilisation de la mémoire par une application. Quand tu dis que ton application convertie occupe 12 Mo, il y a une partie de ça qui n'est plus vraiment là. En VB6, comme les objets ne passent pas par le Garbage Collector, on ne sent pas ça. Ensuite, le Task Manager ne tient pas compte du fait que plusieurs applications peuvent utiliser simultanément le même dll. Part 2 copies de la même application, et tu constateras que le Task Manager indique qu'elles utilisent à peu près la même quantité de mémoire, ce qui n'est pas tout à fait vrai. Supposons que l'application demande elle-même 2 Mo de mémoire et utilise 10 Mo de dll. Si tu en lances 2 copies, Task Manager indiquera 12 Mo pour chacune, laissant croire à un total de 24 Mo. En réalité, le dll est partagé par les deux applications, de sorte que la mémoire réellement utilisée est de 2 + 2 + 10 = 14 Mo. Voyons ça sous un autre aspect. Imagine que tu lances 3 applications Windows conventionnelles dans une session Windows qui vient juste de démarrer : une en VB, une en C++ et une autre en Delphi. Chacune des applications part un run-time différent. La somme de l'espace mémoire affiché dans le Task Manager devrait ressembler à peu près à la réalité. Maintenant, imagine que tu fais la même chose avec des versions .NET des 3 applications. La somme de l'espace mémoire affiché dans le Task Manager sera beaucoup plus grosse, mais ne sera pas significative. Les applications partagent le même run-time, mais Task Manager n'en tient pas compte. Ça donne l'impression que .NET demande beaucoup plus de mémoire, alors qu'en réalité, Task Manager a compté 3 fois un run-time qui n'est chargé qu'une seule fois en mémoire. En résumé : oui, ça demande plus de ressources (surtout tant qu'il y aura un "mix" d'application .NET et Windows), mais pas autant qu'il n'y paraît. Mon expérience personnelle est que sur des ordinateurs où la mémoire n'était pas un problème avant, les applications .NET roulent sans problème. Mon vieux Compaq (Microprocesseur AMD Athlon 600 MHz et 196 Mo de mémoire vive) était un dinosaure quand j'essayais d'y faire du développement .NET, mais j'y utilise régulièrement une version avancée de PhotoCat (l'application d'exemple durant le cours) roulant avec une base de données SQL Server locale (MSDE), en même temps que Word, Outlook et Internet Explorer sont ouverts. C'est sûr que ça roule mieux sur un Pentium 4 à 2 GHz et avec 512 Mo de RAM, mais la performance sur le vieil ordinateur ne me fatigue pas du tout (et je suis très facile à fatiguer sur cet aspect). 17 Par contre, dans un environnement où la mémoire était déjà limitée (j'utilise encore pour des tests un vieux Pentium I 300 Mhz et 96 Mo de mémoire), ça n'est pas acceptable, quoique ça fonctionne. 18 Je viens de commencer mon premier projet en vb.net. Nous avons besoin de réaliser un projet qui possède plusieurs fenêtres ouvertes en même temps car plusieurs type d’information doivent être afficher. J’ai tout de suite penser à une bonne vieille application MDI. Par contre, nous voulons utiliser le « docking » pour attacher certaine fenêtres enfants aux bords de la MDI (un peu comme visual studio le fait). Le hic s’est que dès qu’une fenêtre enfant sort de la fenêtre parent, un scroll bar est automatiquement affiché. Je pensais faire du code pour faire un resize de ma fenêtre enfant lorsque le pointeur de la souris arrive au bord de la MDI, mais comme une portion de la fenêtre enfant sort de la MDI avant le pointeur de souris, le scroll bar apparaît. La question est la suivante : connaissez-vous un bon livre ou un site sur le design d’application MDI ou je pourrais trouver des informations pertinentes? Non, je ne connais aucun livre qui traite spécifiquement des applications MDI, et une recherche rapide chez Camelot et Amazon ne sort rien. L'approche MDI est courante dans la "vraie vie", mais la plupart des manuels n'y consacrent que quelques pages, et plusieurs n'en parlent même pas. Tout ce que je peux t'offrir, ce sont quelques réflexions de mon cru, en espérant que ça pourra te servir. Dans un premier temps, les formulaires ont une propriété AutoScroll qui détermine si des scrollbars s'affichent quand un contrôle sort de la surface du formulaire. À bien des égards, un enfant est vu comme un contrôle dans le parent. Peut-être que modifier cette propriété dans le parent te permettrait d'empêcher l'affichage des barres de défilement avant que n'aies eu le temps de redimensionner ton enfant. Comme je l'ai peut-être mentionné dans le cours, la plupart de mes applications .NET sont en MDI, et comme toi, j'ai eu quelques problèmes au début, même si ce ne sont pas les mêmes. Mes applications n'ont généralement pas à afficher plusieurs fenêtre simultanées. J'utilise le MDI pour plus facilement contrôler le départ et l'arrêt de l'application, ainsi que pour faciliter la gestion des menus. L'idée est de donner aux utilisateurs l'idée qu'ils sont dans une fenêtre unique dont l'affichage change selon l'opération à effectuer, sans qu'ils aient l'impression de changer de fenêtre pour chaque formulaire. Si tu connais Microsoft Money, il utilise le même mécanisme : une trentaine d'écrans différents, mais qui s'ajustent toujours à la grosseur du formulaire MDI. Pour arriver à ça, il y a deux solutions. La première, si elle est possible, est la plus facile au point de vue apparence : concevoir les formulaires pour qu'ils aient tous la même dimension, soit l'espace disponible à l'intérieur du MDI. Dans un tel cas, le MDI est normalement figé et ne peut être ni agrandi ni rapetissé (FormBorderStyle = FixedDialog) La deuxième est de concevoir tous les formulaires pour qu'ils s'ajustent automatiquement quand ils sont redimensionnés (propriété Anchor de chacun des contrôles), et d'ouvrir chacun des enfants en mode plein écran. De cette façon, peu importe la grosseur du parent, l'enfant s'affiche correctement à l'intérieur du parent (on règle le MinimumSize du parent pour s'ajuster au plus petit des formulaires enfant). Le problème de cette approche est qu'il est rare d'avoir une application où tous les formulaires peuvent être agrandis et rapetissés à volonté. En travaillant bien, on peut cependant y arriver. C'est le cas dans Microsoft Money, où peu importe la grosseur de la fenêtre principale, tous les formulaires enfants, qui sont pourtant bien différents les uns des autres, s'affichent toujours correctement (ou presque). Il y a aussi l'approche hybride, qui consiste à ouvrir la plupart des formulaires comme des enfants, mais certains d'entre eux comme des formulaires séparés, généralement avec un ShowDialog (formulaire modal). Les fenêtres À propos de (About) et Options (configuration) des mes applications entrent généralement dans ce cadre. 19 Si ça ne se prête pas à ton application, une autre solution, toujours si c'est possible, serait de faire l'inverse de ce que tu tentes de faire. Plutôt que de redimensionner l'enfant, pourquoi ne pas essayer de redimensionner le parent? Ça ne peut naturellement pas fonctionner si le parent est "accoté" sur le bord de l'écran ou plein écran, mais dans un tel cas, il devrait y avoir moyen d'empêcher l'enfant de sortir du parent. Tu pourrais réagir à l'événement Move de l'enfant pour faire les ajustements nécessaires. Un petit truc qui pourrait être utile ici, un formulaire possède une propriété ClientSize que bien des programmeurs ne connaissent pas parce qu'il n'est disponible qu'en "run-time". Quand on "joue" à l'intérieur d'un MDI, cette propriété est plus intéressante que Size, parce qu'elle donne la surface d'affichage du formulaire, excluant la barre titre, la bordure et les menus. C'est en fait la surface disponible pour les enfants. Je n'ai pas testé, mais je ne serais pas surpris que les fenêtres "dockées" soient considérées comme faisant partie du parent, lors du calcul de la surface restante. 20 J’essaie remplir un ComboBox manuellement à partir de DataReader, je parviens à mettre des valeurs dedans, mais je veux aussi y mettre mes codes sans qu’ils soient visibles !! Exemple : 110 Montréal 120 Québec 130 Ste-Julie Mais je parviens juste à mettre des valeurs dans la liste, sans mettre aucun code. Voilà ce que je fais : Dim conn As New SqlClient.SqlConnection("…connection string") Dim commande As New SqlClient.SqlCommand("SELECT …", conn) Dim dtReader As SqlClient.SqlDataReader conn.Open() dtReader = commande.ExecuteReader While dtReader.Read 'Il doit me manquer quelque chose ici ComboBox1.Items.Add(dtReader(1)) End While La solution la plus simple est de passer par un DataSet (ds) et d'assigner la table et les colonnes à utiliser au ComboBox : Combo1.DataSource = ds.Tables(0) Combo1.DisplayMember = "nom du champ à afficher" Combo1.ValueMember = "nom du champ caché" Pour récupérer les valeurs sélectionnées par l'utilisateur, Combo1.SelectedValuedonne la valeur cachée correspondant à la sélection de l'utilisateur. C'est la solution la plus simple, mais ce n'est pas la plus efficace, parce qu'un DataSet demande beaucoup de ressources qui ne sont généralement pas utilisées dans un ComboBox. C'est pourquoi j'ai dit dans le cours qu'on utilise généralement un DataReader. La technique est plus efficace, mais demande cependant plus de code. En .NET, tout passe par des classes, et c'est le cas du ComboBox. Pour pouvoir y mettre plus d'une "colonne", il faut que ce que les Items que tu y ajoutes soient un objet possédant plusieurs propriétés. Il faut donc généralement créer une classe simple possédant une propriété pour chaque colonne. Tu crées ensuite une collection qui accumule des objets provenant de cette classe, puis tu associes la collection au ComboBox. Dans l'application d'exemple utilisée durant le cours (PhotoCatBD), il y a dans le fichier clsSujet.vb une classe Sujet qui sert à initialiser la liste de Sujets (cboSujet) en affichant le champ Sujet et en cachant le NoSujet, exactement ce que tu tentes de faire. Dans le formulaire principal, frmPhotoCat.InitSujets démontre la technique de création d'une collection (objSujets au pluriel) en utilisant un DataReader et des objets de cette classe (objSujet au singulier), puis comment l'assigner au ComboBox. En voici une copie abrégée au principal. Tu peux consulter l'exemple sur le CD de cours pour une copie complète avec des commentaires supplémentaires et une trappe d'erreur. 21 'Initialise la liste de sujets Dim comSujets As OleDbCommand Dim conPhotoCat As New OleDbConnection() Dim dtrSujets As OleDbDataReader Dim objSujet As Jbfi.Sujet Dim objSujets As Collections.ArrayList 'Connexion à la base de données conPhotoCat.ConnectionString = pcConnectionString conPhotoCat.Open() comSujets = New OleDbCommand("SELECT * FROM tbSujets", conPhotoCat) dtrSujets = comSujets.ExecuteReader(CommandBehavior.CloseConnection) 'Création de la collection objSujets = New Collections.ArrayList While dtrSujets.Read objSujet = New Jbfi.Sujet() objSujet.NoSujet = dtrSujets.GetInt32(TbSujets.NoSujet) objSujet.Sujet = dtrSujets.Item(chSujet).ToString objSujets.Add(objSujet) End While 'Initialisation du ComboBox cboSujet.DataSource = objSujets cboSujet.DisplayMember = "Sujet" cboSujet.ValueMember = "NoSujet" 22 J'essaye d'obtenir les infos de la structure de ma table à partir de Vb.Net...Incapable... Je remplis un dataset à partir d'une requête Sql. Par la suite, j'essaie de voir la propriété DefaultValue d'une colonne. La value est toujours System.DBNull. Pour tant dans Sql Server 2000 ma valeur par défaut est égale à 1. Avez-vous une idée comment résoudre ce problème ? Normalement, pour récupérer les détails de la structure d'une table, il suffit d'appeler la commande FillSchema du DataAdapter avant de faire le Fill, comme ceci (dadPhotos est le DataAdapter, m_dtsPhotos est le DataSet et tbPhotos est le nom que je donne à la table) : dadPhotos.FillSchema(m_dtsPhotos, SchemaType.Source, "tbPhotos") dadPhotos.Fill(m_dtsPhotos, "tbPhotos") Malheureusement, FillSchema récupère bien des choses (PrimaryKey, AllowDBNull, AutoIncrement, ReadOnly, etc.), mais pas la valeur par défaut. C'est explicite si on regarde dans l'aide en ligne. En faisant une petite recherche, j'ai trouvé l'article suivant qui tente d'expliquer pourquoi et qui discute d'une technique permettant de contourner le problème. C'est à http://dotnet247.com/247reference/msgs/18/91618.aspx. 23 J’ai une classe qui en référence une deuxième, et la deuxième référence aussi la première. Dis en d’autres mots, les classes se référencent mutuellement 2. Lorsque je compile, j’obtiens une erreur parce que j’ai une référence circulaire. Comment puis-je régler le problème? VB empêche les références circulaires entre 2 projets, mais les permet entre 2 dll. Deux dll peuvent donc se référencer mutuellement, mais pas deux projets. Les deux projets doivent être dans des solutions différentes, avec des références au dll, pas au code source. Ça veut dire qu'en théorie, on ne peut déboguer les deux en même temps. Quand on en débogue un, l'autre est déjà compilé. Quand on débogue le deuxième, il faut que le premier soit compilé. Pas commode pour le débogage, à première vue, mais on peut utiliser une petite particularité de Visual Studio dont je ne parle pas dans le cours (je peux pas tout dire, j'ai pas assez de temps). Quand tu es en session de débogage, si tu appelles des fonctions qui sont dans un dll compilé, mais que le code source des disponible sur la station de développement, le code source du dll que tu appelles est automatiquement chargé dans l'environnement de Visual Studio quand il y a une erreur ou que tu es en pas à pas. Tu peux donc lancer une solution dans laquelle il n'y a que le projet Classe, puis quand il va appeler Data, les fichiers source vont automatiquement s'ouvrir dans Visual Studio au besoin. Pour faciliter le travail, j'avais l'habitude de lancer en même temps 2 copies de Visual Studio, une avec la solution contenant Data et l'autre avec la solution pour Classe. Quand tu fais ça, le débogueur passe automatiquement d'une copie à l'autre de Visual Studio, comme si les deux projets étaient dans la même copie. La seule différence est qu'il faut s'assurer de compiler individuellement chaque copie chaque fois que tu fais un changement. Si tu fais un changement dans Data, Classe ne le voit pas tant que tu n'as pas recompilé Data, ce qui fait que c'est un peu plus fastidieux à utiliser que quand on a les deux projets dans la même session de VS. De temps en temps, il perd la synchronisation entre les projets, probablement à cause des changements dans les numéros de version. Dans ce temps, supposons par exemple que Data ne voit plus les changements de Classe, tu vas dans Data, tu enlèves la référence à Classe, tu détruit le dll Classe qu'il a copié dans le dossier bin de Data, et tu recrées la référence. Il resynchronise les numéros de version, et tu peux continuer. 2 Ça arrive fréquemment dans une classe d’accès aux données. Par exemple, dans une application du type de celle utilisée dans le cours, une classe Photo peut référencer une classe PhotoBD qui sert d’intermédiaire entre Photo et la base de données. La classe PhotoBD a bien des chances de référencer aussi la classe Photo si c’est elle qui a la tâche de créer des objets Photo à partir des données de la BD. 24