Partager via


Écriture de code d’accès aux données génériques dans ASP.NET 2.0 et ADO.NET 2.0

 

Dr Shahram Khosravi
Solutions d’information

Avril 2005

S’applique à :

   Microsoft ADO.NET 2.0
   Microsoft ASP.NET 2.0
   Microsoft .NET Framework 2.0
   Version bêta de Microsoft Visual Web Developer 2005 Express Edition
   Langage de programmation C#

Résumé : Utiliser une approche pas à pas pour apprendre à utiliser différents outils et techniques ASP.NET 2.0 et ADO.NET 2.0 pour écrire du code d’accès aux données génériques. (18 pages imprimées)

Téléchargez l’exemple de code associé : GenericDataAccessSample.exe.

Introduction

La plupart des applications web contiennent du code d’accès aux données pour accéder au magasin de données sous-jacent pour effectuer des opérations de base telles que Sélectionner, Mettre à jour, supprimeret Insérer. Cet article utilise une approche pas à pas pour montrer comment les développeurs de pages peuvent tirer parti de différentes ASP.NET 2.0 et ADO.NET outils et techniques 2.0 pour écrire du code d’accès aux données génériques qui peuvent être utilisés pour accéder à différents types de magasins de données. L’écriture de code d’accès aux données générique est particulièrement importante dans les applications web pilotées par les données, car les données proviennent de nombreuses sources différentes, notamment Microsoft SQL Server, Oracle, documents XML, fichiers plats et services Web, pour en nommer quelques-uns.

Cet article utilise une application Web simple comme lit de test pour tout le code présenté ici. L’application se compose de deux parties : la première partie permet à l’administrateur système d’envoyer des bulletins d’information à tous les abonnés d’une liste de diffusion. La deuxième partie permet aux utilisateurs de s’abonner ou de se désabonner d’une liste de diffusion. La première partie de l’article commence par implémenter un code d’accès aux données simple (voir la figure 1) pour accéder à Microsoft SQL Server et extraire la liste des abonnés. Le code est modifié et rendu plus générique au cours de l’article.

Figure 1. La méthode GetSubscribers extrait la liste de tous les abonnés.

public IEnumerable GetSubscribers()
{
    SqlConnection con = new SqlConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";

    SqlCommand com = new SqlCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    SqlDataAdapter ad = new SqlDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Étant donné que le code d’accès aux données de la figure 1 contient du code permettant de créer les objets ADO.NET, tels que lesSqlConnection , SqlCommandet Instances de SqlDataAdapter. Le code d’accès aux données ne peut pas être utilisé pour récupérer la liste des abonnés à partir d’autres magasins de données, tels qu’une base de données Oracle. Les développeurs de pages doivent modifier le code d’accès aux données (à l’aide des GetSubscribers méthode) chaque fois qu’ils doivent accéder à un nouveau magasin de données. Découvrez ensuite comment ADO.NET 2.0 utilise le modèle de fournisseur pour aider les développeurs à écrire du code d’accès aux données génériques pour accéder à différents types de magasins de données.

Modèle de fournisseur dans ADO.NET 2.0

Le principal problème avec la méthode GetSubscribers est qu’il contient le code permettant de créer les objets ADO.NET. Selon le modèle de fournisseur, le code d’accès aux données doit déléguer la responsabilité de fournir le code pour la création des objets ADO.NET à une autre classe. Je fais référence à cette classe en tant que « classe de fournisseur de code », car elle fournit le code permettant de créer les objets ADO.NET. La classe de fournisseur de code expose des méthodes telles que CreateConnection, CreateCommandet CreateDataAdapter, où chaque méthode fournit le code pour la création de l’objet ADO.NET correspondant.

Étant donné que la classe de fournisseur de code contient le code réel, la même classe ne peut pas être utilisée pour accéder à différents magasins de données. Par conséquent, le code d’accès aux données (méthode GetSubscribers) doit être modifié et reconfiguré pour déléguer la responsabilité de fournir le code à une nouvelle classe de fournisseur de code chaque fois qu’il est utilisé pour accéder à un nouveau magasin de données. La méthode GetSubscribers est toujours liée au code, même si elle ne contient pas le code.

Le modèle de fournisseur offre une solution à ce problème et se compose des étapes suivantes :

  1. Concevez et implémentez une classe de fournisseur de base abstraite.

  2. Dérivez toutes les classes de fournisseur de code de la classe de fournisseur de base abstraite.

  3. Utilisez le code d’accès aux données (méthode GetSubscribers) pour utiliser la classe de base abstraite au lieu des classes de fournisseur de code individuelles.

    La classe de base abstraite délègue la responsabilité de fournir le code pour la création des objets ADO.NET à la sous-classe appropriée. La classe de base abstraite est nommée DbProviderFactory. Voici quelques-unes des méthodes de cette classe :

    public abstract class DbProviderFactory
    {
            public virtual DbConnection CreateConnection();
            public virtual DbCommand CreateCommand();
            public virtual DbDataAdapter CreateDataAdapter();
    }
    

    Chaque sous-classe fournit le code permettant de créer les objets ADO.NET appropriés pour un magasin de données particulier. Par exemple, la sous-classe SqlClientFactory fournit le code permettant de créer les objets ADO.NET pour accéder à Microsoft SQL Server, comme illustré dans la figure 2.

    Figure 2. La classe SqlClientFactory et certaines de ses méthodes

    public class SqlClientFactory : DbProviderFactory
    {
            public override DbConnection CreateConnection()
            {
                    return new SqlConnection();
            }
    
           public override DbCommand CreateCommand()
           {
                    return new SqlCommand();
           }
    
           public override DbDataAdapter CreateDataAdapter()
           {
                 return new SqlDataAdapter();
           }
    }
    

Le modèle de fournisseur permet au code d’accès aux données de traiter toutes les sous-classes identiques, car elles sont toutes des sous-classes de la même classe de base. En ce qui concerne le code d’accès aux données, toutes les sous-classes sont de type DbProviderFactory. Le code d’accès aux données n’a aucun moyen de connaître le type spécifique de la sous-classe utilisée. Cela introduit un nouveau problème. Si le code d’accès aux données (méthode GetSubscribers) ne connaît pas le type de sous-classe, comment peut-il instancier une instance de la sous-classe ?

La solution de modèle de fournisseur à ce problème se compose des trois parties suivantes :

  1. Une chaîne unique est utilisée pour identifier chaque sous-classe. ADO.NET 2.0 utilise l’espace de noms de la sous-classe comme ID de chaîne unique. Par exemple, l’ID de chaîne unique System.Data.SqlClient et System.Data.OracleClient identifier sqlClientFactory et sous-classes OracleClientFactory, respectivement.
  2. Un fichier texte (normalement un fichier XML) est utilisé pour stocker des informations sur toutes les sous-classes. ADO.NET 2.0 utilise les fichiers machine.config et web.config pour stocker les informations requises. Les informations relatives à une sous-classe contiennent, entre autres, l’ID de chaîne unique et le nom du type de la sous-classe. Par exemple, les informations relatives à la sous-classe SqlClientFactory incluent l’ID de chaîne unique System.Data.SqlClient et le nom du type de la sous-classe, c’est-à-dire System.Data.SqlClient.SqlClientFactory.
  3. Une méthode statique est conçue et implémentée. La méthode peut faire partie de la classe de base abstraite ou d’une classe distincte. ADO.NET 2.0 utilise une classe distincte nommée DbProviderFactories qui expose la méthode statique GetFactory. La méthode accepte l’ID de chaîne unique de la sous-classe souhaitée comme seul argument et recherche dans le fichier machine.config une sous-classe avec l’ID de chaîne unique donné. La méthode extrait le nom du type de la sous-classe souhaitée et utilise la réflexion pour créer dynamiquement une instance de la sous-classe.

Le code d’accès aux données (méthode GetSubscribers) appelle la méthode statique GetFactory et transmet l’ID de chaîne unique approprié pour accéder à l’instance de la sous-classe correspondante. Une fois que la méthode GetSubscribers accède à l’instance, elle appelle les méthodes de création appropriées, telles que CreateConnection(), CreateCommand(), etc., pour instancier les objets ADO.NET appropriés, comme illustré dans la figure 3.

Figure 3. Version de la méthode GetSubscribers qui utilise le nouveau modèle de fournisseur de ADO.NET

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = @"Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf";
    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Le code d’accès aux données (méthode GetSubscribers) délègue la responsabilité de fournir le code pour créer les objets ADO.NET à l’instance de classe du fournisseur de code que la méthode GetFactory instancie et retourne. Par conséquent, le même code d’accès aux données peut être utilisé pour accéder à différents magasins de données, tels que Microsoft SQL Server et Oracle.

Le code permettant de créer les objets ADO.NET appropriés est spécifique au magasin de données. Le modèle de fournisseur dans ADO.NET 2.0 supprime ces parties spécifiques au magasin de données du code d’accès aux données (méthode GetSubscribers) pour la rendre plus générique. Toutefois, le modèle de fournisseur ne supprime pas toutes les parties spécifiques au magasin de données. Une inspection plus étroite de la méthode GetSubscribers révèle les parties suivantes spécifiques au magasin de données :

  1. Chaîne de connexion
  2. ID de chaîne unique qui identifie la classe de fournisseur de code sous-jacente
  3. Texte de la commande
  4. Type de commande

Sauf si quelque chose est fait sur les parties ci-dessus, le code d’accès aux données est toujours lié à un type particulier de magasin de données. Le modèle de fournisseur dans ADO.NET 2.0 n’aide pas à résoudre ce problème. Toutefois, ADO.NET 2.0 nous fournit d’autres outils et techniques pour supprimer les deux premières parties spécifiques au magasin de données, telles que la chaîne de connexion et l’ID de chaîne unique de la méthode GetSubscribers.

Chaînes de connexion

Les chaînes de connexion constituent certaines des ressources les plus précieuses dans une application web. Ils sont si importants que le .NET Framework 2.0 les traite comme des « citoyens de première classe ». Le fichier web.config prend désormais en charge une nouvelle section nommée <connectionStrings> qui contient toutes les chaînes de connexion utilisées dans une application. Par conséquent, nous allons déplacer la chaîne de connexion de la méthode GetSubscribers vers cette section :

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
          <add 
            name="MySqlConnectionString" 
            connectionString="Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
      providerName="System.Data.SqlClient"/>
    </connectionStrings>
</configuration>

L'<ajouter> sous-élément de l’élément <connectionStrings> expose les trois attributs importants suivants :

  • Nom: nom convivial de la chaîne de connexion
  • connectionString—Chaîne de connexion réelle
  • providerName:ID de chaîne unique ou invariant de la classe de fournisseur de code

NET Framework 2.0 fournit le code d’accès aux données (méthode GetSubscribers) avec les outils appropriés pour extraire génériquement la valeur de la chaîne de connexion à partir du fichier web.config, comme décrit dans les sections suivantes. L’espace de noms System.Configuration dans .NET Framework 2.0 inclut une nouvelle classe nommée Configuration. Cette classe représente l’intégralité du contenu d’un fichier web.config ou machine.config. Le code d’accès aux données ne peut pas utiliser le nouvel opérateur pour créer directement une instance de cette classe.

La classe elle-même expose une méthode statique nommée GetWebConfiguration qui prend le chemin du fichier web.config et retourne une instance de la classe Configuration qui représente l’intégralité du contenu du fichier web.config :

Configuration configuration = Configuration.GetWebConfiguration("~/");

Une classe qui hérite de la classe ConfigurationSection représente chaque section du fichier web.config. Le nom de la classe se compose du nom de la section et de la section du mot clé. Par exemple, la classe ConnectionStringsSection représente le contenu de la section <connectionStrings> du fichier web.config. Le code d’accès aux données (méthode GetSubscribers) ne peut pas utiliser le nouvel opérateur pour créer directement une instance de la classe ConnectionStringsSection. La classe Configuration expose une propriété de collection nommée Sections qui contient tous les objets qui représentent différentes sections du fichier web.config :

ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];

Étant donné que sections est une collection d’objets ConfigurationSection, le code d’accès aux données doit taper la conversion de l’instance retournée. Une fois que la méthode GetSubscribers accède à l’objet ConnectionStringsSection et l’utilise pour accéder à la valeur de chaîne de connexion :

string connectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;

La figure 4 montre la nouvelle version de la méthode GetSubscribers qui contient le code requis pour extraire la chaîne de connexion de manière générique.

Figure 4. Version de la méthode GetSubscribers qui extrait la chaîne de connexion du fichier web.config

public IEnumerable GetSubscribers()
{
    DbProviderFactory provider = DbProviderFactories.GetFactory("System.Data.SqlClient");
    DbConnection con = provider.CreateConnection();

    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];
    con.ConnectionString = section.ConnectionStrings["MySqlConnectionString"].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

La méthode GetSubscribers inclut désormais la chaîne MySqlConnectionString. Nous devons donc toujours modifier la méthode GetSubscribers pour ajouter la prise en charge d’un autre magasin de données tel que la base de données Oracle. Il semblerait que nous revenions à la place 1. Pas vraiment. Nous avons obtenu quelques avantages importants en déplaçant la chaîne de connexion du code d’accès aux données vers le fichier web.config :

  • La chaîne de connexion est maintenant la valeur de l’attribut connectionString de l'<ajouter> sous-élément de l’élément connectionStrings du fichier web.config, qui est un document XML. La grande chose à propos d’un document XML est que nous pouvons chiffrer un seul élément dans le document. Nous n’avons pas besoin de chiffrer l’intégralité du document si nous n’avons besoin de protéger qu’une petite partie de celui-ci. .NET Framework 2.0 est fourni avec un outil qui nous permet de chiffrer la section <connectionStrings> pour protéger notre ressource la plus importante, les chaînes de connexion. Imaginez combien de dommages qu’un pirate peut faire à notre base de données précieuse s’il obtient sa main sur nos chaînes de connexion. N’oubliez pas que les chaînes de connexion sont toutes nécessaires pour accéder à notre base de données.

  • Il peut sembler que tout ce que nous avons fait est de remplacer la chaîne suivante

    "Data Source=.\SQLExpress;Integrated Security=True;AttachDBFilename=D:\Application\Data\Database.mdf"
    

    avec la nouvelle chaîne, MySqlConnectionString. Cependant, il y a une grande différence. La première chaîne contient les informations spécifiques à la base de données SQL Server qui ne s’appliquent pas à une autre base de données telle qu’Oracle, mais cette dernière chaîne n’est qu’un nom convivial.

Toutefois, le nom convivial peut toujours entraîner des problèmes, car il fait référence à une chaîne de connexion spécifique dans la section <connectionStrings> du fichier web.config. Dans notre exemple, il fait référence à la chaîne de connexion utilisée pour accéder à Microsoft SQL Server. Cela signifie que la méthode GetSubscribers (le code d’accès aux données) doit être modifiée pour utiliser un nom convivial différent pour accéder à un autre magasin de données tel qu’Oracle.

Pour éviter de modifier le code d’accès aux données, nous pouvons déplacer le nom convivial du code d’accès aux données vers la section <appSettings> du fichier web.config et extraire dynamiquement le code d’accès aux données dans l’exécution comme suit :

string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];

Nous allons également déplacer le nom du fournisseur vers la section <appSettings> :

string providerName = ConfigurationSettings.AppSettings["ProviderName"];

Les développeurs de pages modifient simplement la valeur attribut du <ajouter> sous-élément de l’élément appSettings <> au même code d’accès aux données pour accéder à un autre magasin de données sans apporter de modifications dans le code d’accès aux données lui-même.

La figure 5 présente la version du code d’accès aux données (méthode GetSubscribers) qui contient les modifications récentes.

Figure 5. Version de la méthode GetSubscribers pour extraire le nom du fournisseur et le nom convivial de la chaîne de connexion à partir du fichier web.config

public IEnumerable GetSubscribers()
{
    string connectionStringName = ConfigurationSettings.AppSettings["ConnectionStringName"];
    string providerName = ConfigurationSettings.AppSettings["ProviderName"];
    Configuration configuration = Configuration.GetWebConfiguration("~/");
    ConnectionStringsSection section = (ConnectionStringsSection)configuration.Sections["connectionStrings"];

    

    DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
    DbConnection con = provider.CreateConnection();
    con.ConnectionString = section.ConnectionStrings[connectionStringName].ConnectionString;

    DbCommand com = provider.CreateCommand();
    com.Connection = con;
    com.CommandText = "Select * From Subscribers";
    com.CommandType = CommandType.Text;

    DataSet ds = new DataSet();
    DbDataAdapter ad = provider.CreateDataAdapter();
    ad.SelectCommand = com;

    con.Open();
    ad.Fill(ds);
    con.Close();

    return ds.Tables[0].DefaultView;
}

Contrôles de source de données

La dernière version de la méthode GetSubscribers comme illustré dans la figure 5 n’est toujours pas générique en raison des problèmes suivants :

  • La méthode contient un magasin de données , des informations spécifiques, c’est-à-dire le texte de la commande et le type de commande. Par conséquent, les développeurs de pages doivent toujours modifier la méthode avant de pouvoir l’utiliser pour accéder à une autre base de données.
  • La méthode retourne une instance de la classe DataView à ses appelants afin que la méthode alimente ses données tabulaires des appelants. Nous pouvons considérer cela comme un contrat entre la méthode GetSubscribers et ses appelants. Les appelants s’attendent à ce que la méthode GetSubscribers respecte le contrat dans toutes les circonstances, même si le magasin de données sous-jacent lui-même n’est pas tabulaire. Étant donné que la méthode GetSubscribers utilise ADO.NET objets pour accéder au magasin de données sous-jacent, il ne peut pas accéder à un magasin de données hiérarchique, tel qu’un fichier XML, où les instances des classes dans System.Xml et ses sous-espaces de noms doivent être utilisées au lieu de ADO.NET objets.

Le principal problème lié au code d’accès aux données utilisé dans la méthode GetSubscribers est qu’il contient directement le code réel qui extrait les données du magasin de données sous-jacent. Il s’agit exactement du type de problème que le modèle de fournisseur .NET Framework 2.0 est spécifiquement conçu pour résoudre. Selon le modèle de fournisseur, la méthode GetSubscribers doit déléguer la responsabilité de fournir le code pour accéder au magasin de données à une autre classe. Elle est appelée classe de fournisseur de code. Le type de classe de fournisseur de code dépend du type du magasin de données auquel il accède. Ces classes de fournisseur de code sont collectivement appelées contrôles de source de données. ASP.NET 2.0 est fourni avec plusieurs types de contrôles de source de données différents, notamment SqlDataSource, AccessDataSource, ObjectDataSource, XmlDataSourceet Contrôles SiteMapDataSource.

Le contrôle SqlDataSource est spécifiquement conçu pour mettre à jour, supprimer, insérer et extraire des données à partir de magasins de données relationnelles tels que Microsoft SQL Server et Oracle. Le contrôle AccessDataSource est une sous-classe du contrôle SqlDataSource qui sait comment utiliser les bases de données Microsoft Access. En revanche, le contrôle ObjectDataSource utilise des objets métier en mémoire comme magasin de données. Le contrôle XmlDataSource est spécifiquement conçu pour extraire des données à partir de documents XML. Toutefois, le contrôle XmlDataSource ne fournit pas d’accès en écriture au document XML sous-jacent.

Chaque contrôle de source de données expose une ou plusieurs vues de son magasin de données sous-jacent. Chaque vue est une instance d’une classe appropriée. Par exemple, les contrôles SqlDataSource, AccessDataSource et ObjectDataSource exposent des vues qui sont des instances de SqlDataSourceView, AccessDataSourceViewet classes ObjectDataSourceView, respectivement. Les vues masquent le type réel du magasin de données sous-jacent et le font agir comme le type attendu par le code d’accès aux données. Par exemple, la méthode GetSubscribers attend un type tabulaire de magasin de données, car elle alimente ses données tabulaires clientes. Les vues tabulaires permettent à la méthode GetSubscribers d’extraire des données tabulaires du magasin de données sous-jacent, même lorsque le magasin de données lui-même est une source de données hiérarchique, telle qu’un document XML. Cela permet à la méthode GetSubscribers de traiter les magasins de données tabulaires et hiérarchiques comme des magasins de données tabulaires.

Les contrôles de source de données peuvent fournir à leurs clients deux types de vues : tabulaire et hiérarchique. ASP.NET 2.0 est fourni avec deux contrôles de source de données qui fournissent les deux types de vues, XmlDataSource et SiteMapDataSource. Le reste des contrôles de source de données ( SqlDataSource, AccessDataSource et ObjectDataSource) ne présentent que des vues tabulaires. Toutefois, elles peuvent être étendues pour fournir des vues tabulaires et hiérarchiques.

Contrôles de source de données tabulaires

Un contrôle de source de données tabulaire fait agir son magasin de données sous-jacent comme un magasin de données tabulaire si le magasin de données est tabulaire ou non. Un magasin de données tabulaire se compose de tables de lignes et de colonnes où chaque ligne représente un élément de données. Le nom d’une table identifie et localise la table entre les autres tables du magasin de données tabulaire. Une vue tabulaire agit comme une table, ce qui signifie que les vues sont nommées.

Comme indiqué précédemment, chaque classe de contrôle de source de données et sa classe d’affichage associée (par exemple, la classe SqlDataSource et sa classe SqlDataSourceView associée) fournissent le code réel pour la mise à jour, la suppression, l’insertion et l’extraction de données à partir du magasin de données sous-jacent. Évidemment, le code de chaque contrôle de source de données et sa classe d’affichage associée est spécifiquement conçu pour fonctionner avec un type spécifique de magasin de données. Par conséquent, chaque classe de contrôle de source de données et sa classe de vue associée sont spécifiques au magasin de données. Cela pose un problème sérieux à la méthode GetSubscribers qui utilise des contrôles de source de données et leurs vues tabulaires pour accéder à sa banque de données sous-jacente, car elle lie la méthode à un type spécifique de magasin de données, ce qui signifie que la même méthode ne peut pas être utilisée pour accéder à différents types de magasins de données.

ASP.NET 2.0 offre une solution qui utilise le modèle de fournisseur pour :

  1. Présenter l’interface IDataSource et classe abstraite DataSourceView
  2. Dériver tous les contrôles de source de données tabulaires de l’interface IDataSource
  3. Dériver toutes les vues tabulaires de la classe abstraite DataSourceView

L’interface IDataSource et la classe abstraite DataSourceView délèguent la responsabilité de fournir le code réel pour la mise à jour, la suppression, l’insertion et l’extraction des données du magasin de données dans leurs sous-classes appropriées. Le code d’accès aux données, tel que la méthode GetSubscribers, doit utiliser les méthodes et les propriétés de l’interface IDataSource et de la classe abstraite DataSourceView. Ils ne doivent pas utiliser de méthode ou de propriété spécifique à une classe de contrôle de source de données particulière telle que SqlDataSource ou une classe de vue de source de données particulière telle que SqlDataSourceView. Le modèle de fournisseur permet au code d’accès aux données de traiter tous les contrôles de source de données et leurs vues de source de données respectives de manière générique. En ce qui concerne le code d’accès aux données, tous les contrôles de source de données sont de type IDataSource et toutes les vues de source de données sont de type DataSourceView. Le code d’accès aux données n’a aucun moyen de connaître le type réel du contrôle de source de données et de l’objet de vue de source de données utilisé. Cela provoque un nouveau problème. Si le code d’accès aux données (méthode GetSubscribers) ne connaît pas le type du contrôle de source de données, comment puis-il instancier une instance de celui-ci ?

Comme indiqué précédemment, le modèle de fournisseur fournit une solution qui se compose des étapes suivantes :

  1. Un ID de chaîne unique est utilisé pour identifier chaque classe de contrôle de source de données.
  2. Un fichier texte (normalement un fichier XML) est utilisé pour stocker des informations sur toutes les classes de contrôle de code source de données.
  3. Un mécanisme est conçu et implémenté qui recherche le fichier XML d’une sous-classe avec un ID de chaîne donné.

Voyons maintenant comment ASP.NET 2.0 implémente les trois tâches ci-dessus du modèle fournisseur. ASP.NET 2.0 dérive tous les contrôles de source de données de la classe Control . Pourquoi les contrôles de source de données dérivent-ils de la classe Control s’ils n’affichent pas de texte de balisage HTML ? Ils dérivent de la classe Control afin qu’ils puissent hériter des trois fonctionnalités importantes suivantes :

  1. Ils peuvent être instanciés de manière déclarative.
  2. Ils enregistrent et restaurent leurs valeurs de propriété dans les post-backs.
  3. Ils sont ajoutés à l’arborescence de contrôle de la page contenante.

La première fonctionnalité permet aux développeurs de pages d’instancier de manière déclarative les contrôles de source de données dans leurs fichiers .aspx respectifs. Par conséquent, le fichier .aspx agit comme le fichier texte ou XML requis par la deuxième étape du modèle de fournisseur. L’architecture de contrôle ASP.NET 2.0 crée dynamiquement une instance du contrôle de source de données déclaré et affecte l’instance à une variable dont le nom est la valeur de la propriété ID du contrôle de source de données déclaré. Cela prend en charge les premières et troisième étapes ci-dessus requises par le modèle fournisseur.

La figure 6 montre la version de la méthode GetSubscribers qui utilise les méthodes et les propriétés de l’interface IDataSource et de la classe abstraite DataSourceView pour accéder au magasin de données sous-jacent :

Figure 6. Version de la méthode GetSubscribers qui utilise les méthodes et les propriétés de l’interface IDataSource et de la classe abstraite DataSourceView

void GetSubscribers()
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    DataSourceSelectArguments args = new DataSourceSelectArguments();
    if (dv.CanSort)
        args.SortExpression = "Email";
    DataSourceViewSelectCallback callback = new DataSourceViewSelectCallback(SendMail);
    dv.Select(args, callback);
}

La première ligne de la méthode GetSubscribers montre clairement que la méthode traite le contrôle de source de données comme un objet de type IDataSource. La méthode ne doit pas se soucier du type réel du contrôle de source de données, par exemple s’il s’agit d’un contrôle SqlDataSource, AccessDataSource, ObjectDataSource ou XmlDataSource. Cela permet aux développeurs de pages de passer d’un contrôle de source de données à un autre sans avoir à modifier le code d’accès aux données (méthode GetSubscribers). La section suivante traite de ce problème important plus en détail.

La méthode GetSubscribers appelle la méthode GetView de l’objet IDataSource pour accéder à son objet d’affichage tabulaire par défaut. Notez que la méthode GetView retourne un objet de type DataSourceView. La méthode GetSubscribers ne doit pas se soucier du type réel de l’objet d’affichage, par exemple s’il s’agit d’un objet SqlDataSourceView, AccessDataSourceView, ObjectDataSourceView ou XmlDataSourceView.

Ensuite, la méthode GetSubscribers crée une instance des DataSourceSelectArguments classe pour demander des opérations supplémentaires telles que l’insertion, la pagination ou la récupération du nombre total de lignes sur les données retournées par l’opération Select . La méthode doit d’abord vérifier la valeur du CanInsert, CanPageou CanRetrieveTotalRowCount propriété de la classe DataSourceView pour vous assurer que l’objet d’affichage prend en charge l’opération respective avant d’effectuer la requête.

Étant donné que l’opération Select est asynchrone, la méthode GetSubscribers inscrit la méthode SendMail comme rappel. La méthode Select appelle automatiquement la méthode SendMail une fois qu’elle interroge les données et transmet les données en tant qu’argument, comme illustré dans la figure 7.

Figure 7. La méthode SendMail énumère les données et extrait les informations nécessaires.

void SendMail(IEnumerable data)
{
    string firstName = String.Empty;
    string lastName = String.Empty;
   
    IEnumerator iter = data.GetEnumerator();
    while (iter.MoveNext())
    {
        MailMessage message = new MailMessage();
        message.From = "admin@greatnews.com";
        message.To = DataBinder.Eval(iter.Current, "Email").ToString();
        message.Subject = "NewsLetter";
        firstName = DataBinder.Eval(iter.Current, "FirstName").ToString();
        lastName = DataBinder.Eval(iter.Current, "LastName").ToString();
        string mes = "Dear " + firstName + " " + lastName + ",<br/>";
        mes += MessageBody.Text;
        message.Body = mes;
        message.BodyFormat = MailFormat.Html;
      SmtpMail.SmtpServer = "<myserver>";
      SmtpMail.Send(message);
    }
}

La méthode SendMail appelle la méthode GetEnumerator de l’objet passé en tant que premier argument pour accéder à son objet d’énumérateur et utilise l’énumérateur pour énumérer les données. La méthode SendMail utilise la méthode Eval de la classe DataBinder pour extraire l’adresse de messagerie, le prénom et le nom de chaque abonné, puis envoie la lettre d’actualité à chacun d’eux.

Passage d’un contrôle de source de données à un autre

Comme indiqué précédemment, l’architecture de contrôle ASP.NET crée dynamiquement une instance du contrôle de source de données déclarée dans la page .aspx respective et l’affecte à la variable dont le nom est la valeur de l’ID de propriété du contrôle de source de données déclaré. Une instanciation dynamique du contrôle de source de données déclaré isole la méthode GetSubscribers du type réel du contrôle de source de données et permet à la méthode de traiter tous les contrôles de source de données en tant qu’objets de type IDataSource. Cela permet aux développeurs de pages de passer d’un type de contrôle de source de données à un autre sans modifier le code d’accès aux données (méthode GetSubscribers). Cette section présente un exemple pour ce cas.

Supposons que notre application web utilise la méthode GetSubscribers conjointement avec le contrôle ObjectDataSource pour récupérer la liste des abonnés à partir d’un magasin de données relationnelles tel que Microsoft SQL Server :

<asp:SqlDataSource ID="MySource" Runat="Server"
ConnectionString="<%$ ConnectionStrings:MySqlConnectionString %>"
SelectCommand="Select * From Subscribers" />

Supposons que notre application web fonctionne dans un environnement où la liste des abonnés peut également provenir d’un document XML. Le document XML peut être un fichier XML local ou une ressource distante accessible via l’URL. Par conséquent, notre application doit être en mesure de récupérer la liste des abonnés à partir de documents XML. Évidemment, le contrôle ObjectDataSource n’est pas conçu pour récupérer des données tabulaires à partir de documents XML, ce qui signifie que nous devons utiliser un contrôle de source de données qui peut récupérer des données tabulaires à partir de documents XML, comme le contrôle XmlDataSource.

La méthode GetSubscribers n’utilise aucune propriété ou méthode spécifique aux classes ObjectDataSource et ObjectDataSourceView, et utilise uniquement les méthodes et les propriétés de l’interface IDataSource et de la classe abstraite DataSourceView pour traiter les contrôles de source de données. Nous pouvons facilement passer de ObjectDataSource au contrôle XmlDataSource et utiliser le même code d’accès aux données que la méthode GetSubscribers pour récupérer la liste des abonnés :

<asp:XmlDataSource ID="MySource" Runat="Server"
      DataFile="data.xml" XPath="/Subscribers/Subscriber" />

La valeur de l’attribut XPath du contrôle XmlDataSource est définie sur /Subscribers/Subscribers pour sélectionner tous les abonnés.

Opération d’insertion et de suppression

Rappelez-vous que notre application web se compose de deux parties. La deuxième partie de l’application permet aux utilisateurs de s’abonner/se désabonner d’une liste de diffusion. La méthode s’abonner est appelée lorsque le bouton s’abonner est cliqué, comme illustré dans la figure 8.

Figure 8. La méthode Subscribe est appelée lorsque le bouton S’abonner est cliqué.

void Subscribe(Object sender, EventArgs e)
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    KeyedList values = new KeyedList();
    values.Add("Email", Email.Text);
    values.Add("FirstName", FirstName.Text);
    values.Add("LastName", LastName.Text);
    DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
    if (dv.CanInsert)
        dv.Insert(values, callback);
}

La première ligne de la méthode Subscribe montre que la méthode ne s’intéresse pas au type réel du contrôle de source de données. Par conséquent, nous pouvons basculer vers un autre contrôle de source de données pour prendre en charge un nouveau magasin de données sans avoir à modifier le code dans la méthode Subscribe.

La méthode utilise une instance de la classe KeyedList pour collecter le courrier électronique, le prénom et le nom de l’abonné. Nous n’avons pas besoin d’utiliser la classe KeyedList. Nous pouvons utiliser n’importe quelle classe qui implémente l’interface IDictionary, notamment ArrayList, KeyedList, etc.

La méthode Subscribe vérifie la valeur de la propriété CanInsert de l’objet de vue de source de données pour vous assurer que l’objet d’affichage prend en charge l’opération insert avant d’appeler la méthode Insert . La méthode Subscribe transmet l’instance KeyedList comme premier argument à la méthode Insert.

La méthode Se désabonner fonctionne comme la méthode Subscribe. La principale différence est que la méthode Unsubscribe appelle la méthode Delete de l’objet d’affichage respectif pour supprimer l’abonnement du magasin de données sous-jacent, comme illustré dans la figure 9.

Figure 9. La méthode De désabonnement est appelée lorsque le bouton Se désabonner est cliqué.

void Unsubscribe(Object sender, EventArgs e)
{
    IDataSource ds = (IDataSource)MySource;
    DataSourceView dv = ds.GetView(String.Empty);
    KeyedList keys = new KeyedList();
    keys.Add("Email", Email.Text);
    KeyedList oldValues = new KeyedList();
    oldValues.Add("Email", Email.Text);
    DataSourceViewOperationCallback callback = new DataSourceViewOperationCallback(OperationCallback);
    if (dv.CanDelete)
        dv.Delete(keys, oldValues, callback);
}

Contrôles de source de données hiérarchiques

La méthode GetSubscribers (le code d’accès aux données) alimente les données tabulaires à ses appelants. Toutefois, il arrive que le code d’accès aux données retourne des données hiérarchiques à ses appelants. Cette section présente l’implémentation de la version de la méthode GetSubscribers qui retourne des données hiérarchiques. Nous pouvons considérer cela comme un contrat entre la méthode et ses appelants. Les appelants s’attendent à ce que la méthode retourne des données hiérarchiques à partir de magasins de données hiérarchiques et tabulaires.

ASP.NET 2.0 utilise le modèle de fournisseur pour isoler la méthode GetSubscribers du type réel du magasin de données sous-jacent et présente la méthode avec les vues hiérarchiques du magasin de données. Cela permet à la méthode de traiter les magasins de données hiérarchiques et tabulaires comme des magasins de données hiérarchiques.

Chaque contrôle de source de données hiérarchique est spécifiquement conçu pour fonctionner avec un magasin de données spécifique. Toutefois, étant donné que tous les contrôles de source de données hiérarchique implémentent l’interface IHierarchicalDataSource et toutes les vues de source de données hiérarchiques dérivent de la classe HierarchyDataSourceView, la méthode GetSubscribers n’a pas à traiter les spécificités de chaque contrôle de source de données et peut les traiter de manière générique.

IHierarchicalEnumerable GetSubscribers()
{
    IHierarchicalDataSource ds = (IHierarchicalDataSource)MySource;
    HierarchicalDataSourceView dv = ds.GetHierarchicalView("/Subscribers");
    return dv.Select();
}

La première ligne de la méthode GetSubscribers montre que la méthode traite le contrôle de source de données comme un objet de type IHierarchicalDataSource et ne s’intéresse pas au type réel du contrôle de source de données. Cela permet aux développeurs de pages de basculer vers un nouveau contrôle de source de données hiérarchique afin d’ajouter la prise en charge d’un nouveau magasin de données sans avoir à modifier le code dans la méthode GetSubscribers.

La méthode GetSubscribers appelle ensuite la méthode GetHierarchicalView de la classe HierarchyDataSourceView pour accéder à la vue hiérarchique avec le chemin donné, par exemple « /Subscribers ». Notez que la méthode Select n’est pas asynchrone. L’application transmet les données retournées de la méthode GetSubscribers à la méthode SendMail (voir la figure 15). Notez que les données sont de type IHierarchicalEnumerable.

IHierarchicalEnumerable implémente IEnumerable, ce qui signifie qu’il expose la méthode GetEnumerator. La méthode SendMail appelle la méthode GetEnumerator pour accéder à l’objet IEnumerator respectif, qui est ensuite utilisé pour énumérer les données. IHierarchicalEnumerable expose également une méthode nommée GetHierarchyData qui accepte l’objet énuméré et retourne l’objet IHierarchyData associé.

L’interface IHierarchyData expose une propriété importante nommée Item, qui n’est rien d’autre que l’élément de données. La méthode SendMail utilise la méthode Eval de la classe XPathBinder pour évaluer les expressions XPath par rapport à l’objet Item.

Figure 10. La méthode SendMail énumère les données, extrait les informations nécessaires et envoie le bulletin à chaque abonné.

void SendMail(IHierarchicalEnumerable data)
{
    string firstName = String.Empty;
    string lastName = String.Empty;

    IEnumerator iter = data.GetEnumerator();
    while (iter.MoveNext())
    {
        IHierarchyData ihdata = data.GetHierarchyData(iter.Current);
        MailMessage message = new MailMessage();
        message.From = "admin@subscribers.com";
        message.To = XPathBinder.Eval(ihdata.Item, "@Email").ToString();
        message.Subject = "NewsLetter";
        firstName = XPathBinder.Eval(ihdata.Item, "@FirstName").ToString();
        lastName = XPathBinder.Eval(ihdata.Item, "@LastName").ToString();
        string mes = "Hi " + firstName + " " + lastName + ",<br/>";
        mes += MessageBody.Text;
        message.Body = mes;
        message.BodyFormat = MailFormat.Html;
        SmtpMail.SmtpServer = "MyServer";
        SmtpMail.Send(message);
    }
}

Conclusion

À l’aide d’une approche pas à pas montrant différents outils et techniques ASP.NET 2.0 et ADO.NET 2.0, cet article montre comment les développeurs de pages peuvent écrire du code d’accès aux données générique qui peut être utilisé pour accéder à différents types de magasins de données.

Dr Shahram Khosraviest ingénieur logiciel senior avec Schlumberger Information Solutions (SIS). Shahram se spécialise dans les ASP.NET, les services Web XML, les technologies .NET, les technologies XML, les graphiques d’ordinateur 3D, HI/Usability, les modèles de conception et le développement de contrôles et de composants de serveur ASP.NET. Il a plus de 10 ans d’expérience dans la programmation orientée objet. Il utilise divers outils et technologies Microsoft tels que SQL Server et ADO.NET. Shahram a écrit des articles sur .NET et ASP.NET technologies pour le magazine asp.netPRO.