using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Developpez.Dotnet.Properties; using Developpez.Dotnet.Reflection; namespace Developpez.Dotnet.Data { /// /// Fournit des méthodes d'extension pour faciliter l'utilisation des classes ADO.NET /// public static class DataExtensions { #region Méthodes d'extension pour DbProviderFactory /// /// Crée une connexion à partir d'une DbProviderFactory et d'une chaine de connexion /// /// la DbProviderFactory du provider ADO.NET à utiliser /// la chaine de connexion /// Une DbConnection avec la chaine de connexion spécifiée public static DbConnection CreateConnection(this DbProviderFactory factory, string connectionString) { var connection = factory.CreateConnection(); if (connection != null) connection.ConnectionString = connectionString; return connection; } #endregion #region Méthodes d'extension pour IDbConnection/DbConnection /// /// Crée une commande à partir d'une connexion et d'une requête /// /// La connexion /// Le texte de la requête /// Une commande pour la connexion et la requête spécifiées public static IDbCommand CreateCommand(this IDbConnection connection, string commandText) { IDbCommand command = connection.CreateCommand(); command.CommandText = commandText; return command; } /// /// Crée une commande à partir d'une connexion et d'une requête /// /// La connexion /// Le texte de la requête /// Une commande pour la connexion et la requête spécifiées public static DbCommand CreateCommand(this DbConnection connection, string commandText) { DbCommand command = connection.CreateCommand(); command.CommandText = commandText; return command; } /// /// Exécute la requête spécifiée et renvoie les résultats sous forme d'une séquence d'enregistrements. /// /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// Les résultats de la requête sous forme d'une série d'enregistrements public static IEnumerable Query(this IDbConnection connection, string commandText) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); return connection.QueryImpl(commandText, null); } /// /// Exécute la requête spécifiée avec les paramètres spécifiés, et renvoie les résultats sous forme d'une séquence d'enregistrements. /// /// Type de l'objet contenant les paramètres /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// Objet dont les propriétés correspondent aux noms des paramètres dans la requête SQL /// Les résultats de la requête sous forme d'une série d'enregistrements public static IEnumerable Query(this IDbConnection connection, string commandText, TParams parameters) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); Action setParameters = null; // ReSharper disable CompareNonConstrainedGenericWithNull if (parameters != null) setParameters = command => command.SetParameters(parameters); // ReSharper restore CompareNonConstrainedGenericWithNull return connection.QueryImpl(commandText, setParameters); } private static IEnumerable QueryImpl(this IDbConnection connection, string commandText, Action setParameters) { using (var command = connection.CreateCommand(commandText)) { if (setParameters != null) setParameters(command); using (var reader = command.ExecuteReader()) { // On ne peut pas renvoyer directement reader.AsEnumerable(), // sinon le reader et la commande seront disposés trop tôt. // Il faut donc utiliser un itérateur. while (reader.Read()) { yield return reader; } } } } /// /// Exécute la requête spécifiée avec les paramètres spécifiés, et renvoie la première colonne de la première ligne du résultat. /// /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// La première colonne de la première ligne du résultat, ou null si la requête n'a renvoyé aucun résultat public static object QueryScalar(this IDbConnection connection, string commandText) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); using (var command = connection.CreateCommand(commandText)) { return command.ExecuteScalar(); } } /// /// Exécute la requête spécifiée avec les paramètres spécifiés, et renvoie la première colonne de la première ligne du résultat. /// /// Type de l'objet contenant les paramètres /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// Objet dont les propriétés correspondent aux noms des paramètres dans la requête SQL /// La première colonne de la première ligne du résultat, ou null si la requête n'a renvoyé aucun résultat public static object QueryScalar(this IDbConnection connection, string commandText, TParams parameters) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); using (var command = connection.CreateCommand(commandText)) { // ReSharper disable CompareNonConstrainedGenericWithNull if (parameters != null) command.SetParameters(parameters); // ReSharper restore CompareNonConstrainedGenericWithNull return command.ExecuteScalar(); } } /// /// Exécute la commande spécifiée et renvoie le nombre de lignes affectées /// /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// Le nombre de lignes affectées par la commande public static int Execute(this IDbConnection connection, string commandText) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); using (var command = connection.CreateCommand(commandText)) { return command.ExecuteNonQuery(); } } /// /// Exécute la commande spécifiée avec les paramètres spécifiés, et renvoie le nombre de lignes affectées /// /// Type de l'objet contenant les paramètres /// Connection sur laquelle exécuter la commande /// Texte de la requête SQL /// Objet dont les propriétés correspondent aux noms des paramètres dans la requête SQL /// Le nombre de lignes affectées par la commande public static int Execute(this IDbConnection connection, string commandText, TParams parameters) { connection.CheckArgumentNull("connection"); commandText.CheckArgumentNull("commandText"); using (var command = connection.CreateCommand(commandText)) { // ReSharper disable CompareNonConstrainedGenericWithNull if (parameters != null) command.SetParameters(parameters); // ReSharper restore CompareNonConstrainedGenericWithNull return command.ExecuteNonQuery(); } } #endregion #region Méthodes d'extension pour IDbCommand/DbCommand /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// Le paramètre créé public static IDbDataParameter AddParameter(this IDbCommand command, string name, DbType dbType) { return command.AddParameter(name, dbType, 0, ParameterDirection.Input); } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La taille du paramètre /// Le paramètre créé public static IDbDataParameter AddParameter(this IDbCommand command, string name, DbType dbType, int size) { return command.AddParameter(name, dbType, size, ParameterDirection.Input); } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La direction du paramètre /// Le paramètre créé public static IDbDataParameter AddParameter(this IDbCommand command, string name, DbType dbType, ParameterDirection direction) { IDbDataParameter parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.DbType = dbType; parameter.Direction = direction; command.Parameters.Add(parameter); return parameter; } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La taille du paramètre /// La direction du paramètre /// Le paramètre créé public static IDbDataParameter AddParameter(this IDbCommand command, string name, DbType dbType, int size, ParameterDirection direction) { IDbDataParameter parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.DbType = dbType; parameter.Direction = direction; parameter.Size = size; command.Parameters.Add(parameter); return parameter; } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// Le paramètre créé public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType) { return command.AddParameter(name, dbType, 0, ParameterDirection.Input); } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La taille du paramètre /// Le paramètre créé public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size) { return command.AddParameter(name, dbType, size, ParameterDirection.Input); } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La direction du paramètre /// Le paramètre créé public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, ParameterDirection direction) { DbParameter parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.DbType = dbType; parameter.Direction = direction; command.Parameters.Add(parameter); return parameter; } /// /// Ajoute à la commande un paramètre avec les options spécifiées /// /// La commande à laquelle ajouter un paramètre /// Le nom du paramètre /// Le type du paramètre /// La taille du paramètre /// La direction du paramètre /// Le paramètre créé public static DbParameter AddParameter(this DbCommand command, string name, DbType dbType, int size, ParameterDirection direction) { DbParameter parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.DbType = dbType; parameter.Direction = direction; parameter.Size = size; command.Parameters.Add(parameter); return parameter; } /// /// Exécute la commande et renvoie les résultats sous forme d'une séquence d'éléments /// résultant de la projection spécifiée. /// /// Le type des éléments à renvoyer /// La commande à exécuter /// La projection à appliquer à chaque ligne renvoyée par la requête /// Une séquence d'éléments résultant de la projection public static IEnumerable Select(this IDbCommand command, Func selector) { command.CheckArgumentNull("command"); selector.CheckArgumentNull("selector"); return command.SelectImpl(selector); } private static IEnumerable SelectImpl(this IDbCommand command, Func selector) { using (var reader = command.ExecuteReader()) { while (reader.Read()) { yield return selector(reader); } } } /// /// Définit les valeurs des paramètres d'un commande, en créant les paramètres si besoin, d'après les propriétés de l'objet passé en paramètre. /// /// Type de l'objet contenant les paramètres /// Commande dont les valeurs des paramètres sont définies /// Objet dont les propriétés correspondent aux noms des paramètres dans la requête SQL /// La commande avec les valeurs de paramètres spécifiées public static IDbCommand SetParameters(this IDbCommand command, TParams parameters) { ParamsReflectionCache.CreateOrUpdateParameters(command, parameters); return command; } /// /// Définit les valeurs des paramètres d'un commande, en créant les paramètres si besoin, d'après les propriétés de l'objet passé en paramètre. /// /// Type de l'objet contenant les paramètres /// Commande dont les valeurs des paramètres sont définies /// Objet dont les propriétés correspondent aux noms des paramètres dans la requête SQL /// La commande avec les valeurs de paramètres spécifiées public static DbCommand SetParameters(this DbCommand command, TParams parameters) { ParamsReflectionCache.CreateOrUpdateParameters(command, parameters); return command; } #endregion #region Méthodes d'extension pour IDataRecord /// /// Récupère la valeur de le colonne spécifiée en tant que Boolean /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static bool GetBoolean(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetBoolean(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Boolean, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static bool GetBooleanOrDefault(this IDataRecord record, string name, bool defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetBoolean(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Byte /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static byte GetByte(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetByte(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Byte, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static byte GetByteOrDefault(this IDataRecord record, string name, byte defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetByte(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Char /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static char GetChar(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetChar(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Char, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static char GetCharOrDefault(this IDataRecord record, string name, char defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetChar(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que DateTime /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static DateTime GetDateTime(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetDateTime(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que DateTime, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static DateTime GetDateTimeOrDefault(this IDataRecord record, string name, DateTime defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetDateTime(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Decimal /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static decimal GetDecimal(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetDecimal(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Decimal, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static decimal GetDecimalOrDefault(this IDataRecord record, string name, decimal defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetDecimal(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Double /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static double GetDouble(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetDouble(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Double, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static double GetDoubleOrDefault(this IDataRecord record, string name, double defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetDouble(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Float /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static float GetFloat(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetFloat(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Float, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static float GetFloatOrDefault(this IDataRecord record, string name, float defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetFloat(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Guid /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static Guid GetGuid(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetGuid(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Guid, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static Guid GetGuidOrDefault(this IDataRecord record, string name, Guid defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetGuid(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int16 /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static short GetInt16(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetInt16(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int16, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static short GetInt16OrDefault(this IDataRecord record, string name, short defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetInt16(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int32 /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static int GetInt32(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetInt32(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int32, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static int GetInt32OrDefault(this IDataRecord record, string name, int defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetInt32(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int64 /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static long GetInt64(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetInt64(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que Int64, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static long GetInt64OrDefault(this IDataRecord record, string name, long defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetInt64(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que String /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static string GetString(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetString(i); } /// /// Récupère la valeur de le colonne spécifiée en tant que String, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static string GetStringOrDefault(this IDataRecord record, string name, string defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetString(i); } /// /// Récupère la valeur de le colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne public static object GetValue(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetValue(i); } /// /// Récupère la valeur de le colonne spécifiée, ou la valeur par défaut spécifiée si la colonne est DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur par défault à renvoyer si la colonne vaut DBNull /// La valeur de la colonne, ou la valeur par défaut spécifiée si la colonne vaut DBNull public static object GetValueOrDefault(this IDataRecord record, string name, object defaultValue) { int i = record.GetOrdinal(name); return record.IsDBNull(i) ? defaultValue : record.GetValue(i); } /// /// Lit un flux binaire de la colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à lire /// La position à laquelle commence la lecture dans la colonne /// Le buffer de destination /// La position à laquelle commence l'écriture dans le buffer /// Le nombre maximum d'octets à lire /// Le nombre d'octets lus public static long GetBytes(this IDataRecord record, string name, long dataOffset, byte[] buffer, int bufferOffset, int length) { int i = record.GetOrdinal(name); return record.GetBytes(i, dataOffset, buffer, bufferOffset, length); } /// /// Lit un flux de caractères de la colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à lire /// La position à laquelle commence la lecture dans la colonne /// Le buffer de destination /// La position à laquelle commence l'écriture dans le buffer /// Le nombre maximum de caractères à lire /// Le nombre de caractères lus public static long GetChars(this IDataRecord record, string name, long dataOffset, char[] buffer, int bufferOffset, int length) { int i = record.GetOrdinal(name); return record.GetChars(i, dataOffset, buffer, bufferOffset, length); } /// /// Renvoie un IDataReader pour la colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// Un IDataReader public static IDataReader GetData(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetData(i); } /// /// Indique si la colonne spécifiée a la valeur DBNull /// /// Le IDataRecord qui contient les données /// Le nom de la colonne à lire /// True si la colonne vaut DBNull, false sinon public static bool IsDBNull(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.IsDBNull(i); } /// /// Renvoie le nom du type de données de la colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne dont on veut obtenir le type de données /// Le nom du type de données de la colonne public static string GetDataTypeName(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetDataTypeName(i); } /// /// Renvoie le type .NET de la colonne spécifiée /// /// Le IDataRecord qui contient les données /// Le nom de la colonne dont on veut obtenir le type .NET /// Le nom type .NET de la colonne public static Type GetFieldType(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetFieldType(i); } /// /// Renvoie la valeur de la colonne spécifiée en tant que valeur du type spécifié /// /// Le type de retour souhaité /// Le IDataRecord qui contient les données /// L'index de la colonne à récupérer /// true pour tenter de convertir la valeur si elle n'est pas de type T ; false pour effectuer un cast direct /// La valeur de la colonne /// La conversion spécifiée n'est pas valide public static T Field(this IDataRecord record, int ordinal, bool tryConvert) { if (tryConvert) return ConvertT.Convert(record[ordinal]); return UnboxT.Unbox(record[ordinal]); } /// /// Renvoie la valeur de la colonne spécifiée en tant que valeur du type spécifié /// /// Le type de retour souhaité /// Le IDataRecord qui contient les données /// L'index de la colonne à récupérer /// La valeur de la colonne /// La conversion spécifiée n'est pas valide public static T Field(this IDataRecord record, int ordinal) { return record.Field(ordinal, false); } /// /// Renvoie la valeur de la colonne spécifiée en tant que valeur du type spécifié /// /// Le type de retour souhaité /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// true pour tenter de convertir la valeur si elle n'est pas de type T ; false pour effectuer un cast direct /// La valeur de la colonne /// La conversion spécifiée n'est pas valide public static T Field(this IDataRecord record, string name, bool tryConvert) { int i = record.GetOrdinal(name); return record.Field(i, tryConvert); } /// /// Renvoie la valeur de la colonne spécifiée en tant que valeur du type spécifié /// /// Le type de retour souhaité /// Le IDataRecord qui contient les données /// Le nom de la colonne à récupérer /// La valeur de la colonne /// La conversion spécifiée n'est pas valide public static T Field(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.Field(i, false); } #region Classes utilitaires UnboxT et ConvertT pour convertir les valeurs de champ // Tiré de la classe System.Data.DataRowExtensions de .NET 3.5 private static class UnboxT { internal static readonly Converter Unbox; static UnboxT() { Unbox = Create(typeof(T)); } private static Converter Create(Type type) { if (!type.IsValueType) { return ReferenceField; } if ((type.IsGenericType && !type.IsGenericTypeDefinition) && (typeof(Nullable<>) == type.GetGenericTypeDefinition())) { return (Converter)Delegate.CreateDelegate( typeof(Converter), typeof(UnboxT) .GetMethod( "NullableField", BindingFlags.NonPublic | BindingFlags.Static) .MakeGenericMethod(type.GetGenericArguments())); } return ValueField; } private static TElem? NullableField(object value) where TElem : struct { if (DBNull.Value == value) { return null; } return (TElem)value; } private static T ReferenceField(object value) { if (DBNull.Value != value) { return (T)value; } return default(T); } private static T ValueField(object value) { if (DBNull.Value == value) { throw new InvalidCastException(string.Format(ExceptionMessages.InvalidCastFromTo, typeof(DBNull), typeof(T))); } return (T)value; } } private static class ConvertT { internal static readonly Converter Convert; static ConvertT() { Convert = Create(typeof(T)); } private static Converter Create(Type type) { if (!type.IsValueType) { return ReferenceField; } if (type.IsGenericType && !type.IsGenericTypeDefinition && typeof(Nullable<>) == type.GetGenericTypeDefinition()) { return (Converter)Delegate.CreateDelegate(typeof(Converter), typeof(ConvertT).GetMethod("NullableField", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(type.GetGenericArguments())); } return ValueField; } private static TElem? NullableField(object value) where TElem : struct { if (null == value || DBNull.Value == value) { return null; } object converted = System.Convert.ChangeType(value, typeof(TElem)); if (converted == null) return null; return (TElem)converted; } private static T ReferenceField(object value) { if (null != value && DBNull.Value != value) { return (T)System.Convert.ChangeType(value, typeof(T)); } return default(T); } private static T ValueField(object value) { if (DBNull.Value == value) { throw new InvalidCastException(string.Format(ExceptionMessages.InvalidCastFromTo, typeof(DBNull), typeof(T))); } if (null == value) { throw new InvalidCastException(ExceptionMessages.InvalidCastFromNullToValueType); } return (T)System.Convert.ChangeType(value, typeof(T)); } } #endregion /// /// Renvoie un flux permettant d'accéder aux données d'un champ binaire (BLOB) du IDataRecord /// /// IDataRecord à partir duquel lire les données /// Index du champ qui contient les données /// Un flux permettant de lire les données du champ spécifié public static Stream GetStream(this IDataRecord record, int ordinal) { return new DbBinaryFieldStream(record, ordinal); } /// /// Renvoie un flux en lecture seule permettant d'accéder aux données d'un champ binaire (BLOB) du IDataRecord /// /// IDataRecord à partir duquel lire les données /// Nom du champ qui contient les données /// Un flux permettant de lire les données du champ spécifié public static Stream GetStream(this IDataRecord record, string name) { int i = record.GetOrdinal(name); return record.GetStream(i); } #region Classe DbBinaryFieldStream pour lire un champ binaire (BLOB) d'un IDataRecord /// /// Permet d'accéder à un champ binaire d'un IDataRecord sous forme de flux /// private class DbBinaryFieldStream : Stream { private readonly IDataRecord _record; private readonly int _fieldIndex; private long _position; private long _length = -1; /// /// Initialise une nouvelle instance de DbBinaryFieldStream /// /// IDataRecord à partir duquel lire les données /// Index du champ dans lequel se trouvent les données public DbBinaryFieldStream(IDataRecord record, int fieldIndex) { _record = record; _fieldIndex = fieldIndex; } /// /// Indique si le flux prend en charge la lecture. /// Renvoie toujours true pour DbBinaryFieldStream /// public override bool CanRead { get { return true; } } /// /// Indique si le flux prend en charge la recherche. /// Renvoie toujours true pour DbBinaryFieldStream /// public override bool CanSeek { get { return true; } } /// /// Indique si le flux prend en charge l'écriture. /// Renvoie toujours false pour DbBinaryFieldStream /// public override bool CanWrite { get { return false; } } /// /// Non supporté. /// public override void Flush() { throw new NotSupportedException(); } /// /// Renvoie la longueur du flux. /// public override long Length { get { if (_length < 0) { _length = _record.GetBytes(_fieldIndex, 0, null, 0, 0); } return _length; } } /// /// Obtient ou définit la position courante dans le flux. /// public override long Position { get { return _position; } set { _position = value; } } /// /// Lit une séquence d'octets à partir du flux et avance la position dans le flux du nombre d'octets lus /// /// Buffer dans lequel copier les données /// Position dans le buffer à partir de laquelle copier les données /// Nombre maximum d'octets à lire /// Le nombre d'octets lus public override int Read(byte[] buffer, int offset, int count) { long nRead = _record.GetBytes(_fieldIndex, _position, buffer, offset, count); _position += nRead; return (int)nRead; } /// /// Définit la position courante dans le flux /// /// Position désirée par rapport au paramètre origin /// Valeur indiquant le point de référence utilisé pour obtenir la nouvelle position /// public override long Seek(long offset, SeekOrigin origin) { long newPosition = _position; switch (origin) { case SeekOrigin.Begin: newPosition = offset; break; case SeekOrigin.Current: newPosition = _position + offset; break; case SeekOrigin.End: newPosition = this.Length - offset; break; default: break; } if (newPosition < 0) throw new ArgumentOutOfRangeException("offset"); _position = newPosition; return _position; } /// /// Non supporté. /// /// Nouvelle longueur du flux public override void SetLength(long value) { throw new NotSupportedException(); } /// /// Non supporté /// /// Buffer à partir duquel copier les données /// Position dans le buffer à partir de laquelle copier les données /// Nombre maximum d'octets à copier public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } #endregion #endregion #region Méthodes d'extension pour IDataReader /// /// Renvoie un IDataReader comme une séquence de IDataRecord /// /// Le IDataReader à énumérer /// Une séquence de IDataRecord /// ATTENTION : les données de chaque ligne doivent être récupérées avant de passer à la suivante, /// sinon les données renvoyées ne seront pas celles attendues. Cela implique qu'il ne faut pas utiliser /// ToList() ou ToArray() (par exemple) sur la séquence de IDataRecord obtenue. public static IEnumerable AsEnumerable(this IDataReader reader) { reader.CheckArgumentNull("reader"); return reader.AsEnumerableImpl(); } private static IEnumerable AsEnumerableImpl(this IDataReader reader) { while (reader.Read()) { yield return reader; } } #endregion #region Méthodes d'extension pour IDbDataAdapter /// /// Ajoute un handler d'évènement RowUpdating à un DbDataAdapter /// /// L'adapteur pour lequel ajouter un handler d'évènement /// Le handler d'évènement à ajouter public static void AddRowUpdatingHandler(this IDbDataAdapter adapter, EventHandler handler) { adapter.CheckArgumentNull("adapter"); handler.CheckArgumentNull("handler"); AddEventHandler(adapter, "RowUpdating", handler); } /// /// Ajoute un handler d'évènement RowUpdated à un DbDataAdapter /// /// L'adapteur pour lequel ajouter un handler d'évènement /// Le handler d'évènement à ajouter public static void AddRowUpdatedHandler(this IDbDataAdapter adapter, EventHandler handler) { adapter.CheckArgumentNull("adapter"); handler.CheckArgumentNull("handler"); AddEventHandler(adapter, "RowUpdated", handler); } /// /// Enlève un handler d'évènement RowUpdating d'un DbDataAdapter /// /// L'adapteur pour lequel enlever un handler d'évènement /// Le handler d'évènement à enlever public static void RemoveRowUpdatingHandler(this IDbDataAdapter adapter, EventHandler handler) { adapter.CheckArgumentNull("adapter"); handler.CheckArgumentNull("handler"); RemoveEventHandler(adapter, "RowUpdating", handler); } /// /// Enlève un handler d'évènement RowUpdated d'un DbDataAdapter /// /// L'adapteur pour lequel enlever un handler d'évènement /// Le handler d'évènement à enlever public static void RemoveRowUpdatedHandler(this IDbDataAdapter adapter, EventHandler handler) { adapter.CheckArgumentNull("adapter"); handler.CheckArgumentNull("handler"); RemoveEventHandler(adapter, "RowUpdated", handler); } private static void AddEventHandler(object target, string eventName, Delegate handler) { Type targetType = target.GetType(); EventInfo evt = targetType.GetEvent(eventName); if (evt == null) { throw new InvalidOperationException(string.Format(ExceptionMessages.TypeDoesntImplementEvent, targetType, eventName)); } evt.AddEventHandler(target, ChangeType(evt.EventHandlerType, handler)); } private static void RemoveEventHandler(object target, string eventName, Delegate handler) { Type targetType = target.GetType(); EventInfo evt = targetType.GetEvent(eventName); if (evt == null) { throw new InvalidOperationException(string.Format(ExceptionMessages.TypeDoesntImplementEvent, targetType, eventName)); } evt.RemoveEventHandler(target, ChangeType(evt.EventHandlerType, handler)); } private static Delegate ChangeType(Type targetType, Delegate d) { return Delegate.CreateDelegate(targetType, d.Target, d.Method); } #endregion #region Cache de réflexion pour les paramètres private static class ParamsReflectionCache { private class ParameterDescriptor { private readonly string _name; private readonly DbType _dbType; private readonly Func _getter; public ParameterDescriptor(string name, DbType dbType, Func getter) { _name = name; _dbType = dbType; _getter = getter; } public DbType DbType { get { return _dbType; } } public string Name { get { return _name; } } public object GetValue(TParams parameters) { return _getter(parameters); } } private static readonly Dictionary _descriptors = InitDescriptors(); private static Dictionary InitDescriptors() { var descriptors = new Dictionary(); foreach (var prop in typeof(TParams).GetProperties()) { if (!prop.CanRead) continue; var descriptor = new ParameterDescriptor(prop.Name, GetDbType(prop.PropertyType), MakeGetter(prop)); descriptors.Add(prop.Name, descriptor); } return descriptors; } private static DbType GetDbType(Type type) { if (type.IsValueType && type.IsNullable()) type = type.GetGenericArguments()[0]; if (type == typeof(bool)) return DbType.Boolean; if (type == typeof(Byte)) return DbType.Byte; if (type == typeof(SByte)) return DbType.SByte; if (type == typeof(Int16)) return DbType.Int16; if (type == typeof(UInt16)) return DbType.UInt16; if (type == typeof(Int32)) return DbType.Int32; if (type == typeof(Int64)) return DbType.Int64; if (type == typeof(UInt64)) return DbType.UInt64; if (type == typeof(Decimal)) return DbType.Decimal; if (type == typeof(Double)) return DbType.Double; if (type == typeof(Single)) return DbType.Single; if (type == typeof(DateTime)) return DbType.DateTime; if (type == typeof(DateTimeOffset)) return DbType.DateTimeOffset; if (type == typeof(TimeSpan)) return DbType.Time; if (type == typeof(Guid)) return DbType.Guid; if (type == typeof(string)) return DbType.String; if (type == typeof(byte[])) return DbType.Binary; return DbType.Object; } private static Func MakeGetter(PropertyInfo prop) { var prm = Expression.Parameter(typeof(TParams), "p"); Expression body = Expression.Property(prm, prop); if (prop.PropertyType.IsValueType) body = Expression.Convert(body, typeof(object)); var lambda = Expression.Lambda>(body, prm); var getter = lambda.Compile(); return getter; } public static void CreateOrUpdateParameters(IDbCommand command, TParams parameters) { var joined = from d in _descriptors.Values join p in command.Parameters.Cast() on d.Name equals p.ParameterName into tmp from p in tmp.DefaultIfEmpty() select new { Descriptor = d, Parameter = p }; foreach (var x in joined) { var p = x.Parameter ?? command.CreateParameter(); p.ParameterName = x.Descriptor.Name; p.DbType = x.Descriptor.DbType; p.Value = x.Descriptor.GetValue(parameters); if (x.Parameter == null) { Debug.Print("Added parameter {0}", p.ParameterName); command.Parameters.Add(p); } else { Debug.Print("Updated parameter {0}", p.ParameterName); } } } } #endregion } }