Je recherchais une conversion intelligente entre .Net System.Type et SqlDbType. Ce que j'ai trouvé était l'idée suivante:
private static SqlDbType TypeToSqlDbType(Type t)
{
String name = t.Name;
SqlDbType val = SqlDbType.VarChar; // default value
try
{
if (name.Contains("16") || name.Contains("32") || name.Contains("64"))
{
name = name.Substring(0, name.Length - 2);
}
val = (SqlDbType)Enum.Parse(typeof(SqlDbType), name, true);
}
catch (Exception)
{
// add error handling to suit your taste
}
return val;
}
Le code ci-dessus n’est pas vraiment agréable et c’est une odeur de code. C’est pourquoi j’ai écrit la fonction suivante, naïve, pas intelligente, mais utile, basée sur https://msdn.Microsoft.com/en-us/library/ cc716729 (v = vs.110) .aspx :
public static SqlDbType ConvertiTipo(Type giveType)
{
var typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
typeMap[typeof(char[])] = SqlDbType.NVarChar;
typeMap[typeof(int)] = SqlDbType.Int;
typeMap[typeof(Int32)] = SqlDbType.Int;
typeMap[typeof(Int16)] = SqlDbType.SmallInt;
typeMap[typeof(Int64)] = SqlDbType.BigInt;
typeMap[typeof(Byte[])] = SqlDbType.VarBinary;
typeMap[typeof(Boolean)] = SqlDbType.Bit;
typeMap[typeof(DateTime)] = SqlDbType.DateTime2;
typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
typeMap[typeof(Decimal)] = SqlDbType.Decimal;
typeMap[typeof(Double)] = SqlDbType.Float;
typeMap[typeof(Decimal)] = SqlDbType.Money;
typeMap[typeof(Byte)] = SqlDbType.TinyInt;
typeMap[typeof(TimeSpan)] = SqlDbType.Time;
return typeMap[(giveType)];
}
Quelqu'un a-t-il une idée de la façon d'obtenir le même résultat de manière plus propre, plus efficace et plus agréable?
Votre approche est un bon début, mais il ne faut renseigner ce dictionnaire que une fois, comme le dit Ian dans un commentaire.
Il y a un Gist ici basé sur la même idée, bien qu'il ne convertisse pas entre les mêmes types de types: https://Gist.github.com/abrahamjp/858392
Caveat
J'ai un exemple de travail ci-dessous, mais vous devez savoir que cette approche pose quelques problèmes. Par exemple:
string
, comment choisir le bon entre Char
, NChar
, VarChar
, NVarChar
, Text
ou NText
(ou même Xml
, peut-être)}?byte[]
, devriez-vous utiliser Binary
, VarBinary
ou Image
? decimal
, float
et double
, devriez-vous choisir Decimal
, Float
, Money
, SmallMoney
ou Real
?DateTime
, avez-vous besoin de DateTime2
, DateTimeOffset
, DateTime
ou SmallDateTime
?Nullable
, comme int?
? Ceux-ci devraient probablement donner la même SqlDbType
que le type sous-jacent.De plus, le simple fait de fournir une Type
ne vous dit rien des autres contraintes, telles que la taille et la précision du champ. Prendre la bonne décision concerne également la manière dont les données sont utilisées dans votre application et leur stockage dans la base de données.
La meilleure chose à faire est vraiment de laisser un ORM le faire pour vous.
Code
public static class SqlHelper
{
private static Dictionary<Type, SqlDbType> typeMap;
// Create and populate the dictionary in the static constructor
static SqlHelper()
{
typeMap = new Dictionary<Type, SqlDbType>();
typeMap[typeof(string)] = SqlDbType.NVarChar;
typeMap[typeof(char[])] = SqlDbType.NVarChar;
typeMap[typeof(byte)] = SqlDbType.TinyInt;
typeMap[typeof(short)] = SqlDbType.SmallInt;
typeMap[typeof(int)] = SqlDbType.Int;
typeMap[typeof(long)] = SqlDbType.BigInt;
typeMap[typeof(byte[])] = SqlDbType.Image;
typeMap[typeof(bool)] = SqlDbType.Bit;
typeMap[typeof(DateTime)] = SqlDbType.DateTime2;
typeMap[typeof(DateTimeOffset)] = SqlDbType.DateTimeOffset;
typeMap[typeof(decimal)] = SqlDbType.Money;
typeMap[typeof(float)] = SqlDbType.Real;
typeMap[typeof(double)] = SqlDbType.Float;
typeMap[typeof(TimeSpan)] = SqlDbType.Time;
/* ... and so on ... */
}
// Non-generic argument-based method
public static SqlDbType GetDbType(Type giveType)
{
// Allow nullable types to be handled
giveType = Nullable.GetUnderlyingType(giveType) ?? giveType;
if (typeMap.ContainsKey(giveType))
{
return typeMap[giveType];
}
throw new ArgumentException($"{giveType.FullName} is not a supported .NET class");
}
// Generic version
public static SqlDbType GetDbType<T>()
{
return GetDbType(typeof(T));
}
}
Et voici comment vous l'utiliseriez:
var sqlDbType = SqlHelper.GetDbType<string>();
// or:
var sqlDbType = SqlHelper.GetDbType(typeof(DateTime?));
// or:
var sqlDbType = SqlHelper.GetDbType(property.PropertyType);
Il semble que ce type de table de recherche soit déjà disponible, mais pas dans System.Data
(ou .Object
ou .Type
), mais plutôt dans System.Web.
Projet -> Ajouter une référence -> System.Web -> OK
Alors https://msdn.Microsoft.com/en-us/library/system.data.sqldbtype(v=vs.110).aspx dit aussi
Lors de la définition des paramètres de commande, les types SqlDbType et DbType sont liés . Par conséquent, la définition de DbType modifie le SqlDbType en un SqlDbType.
Donc, cela devrait théoriquement fonctionner;)
using Microsoft.SqlServer.Server; // SqlDataRecord and SqlMetaData
using System;
using System.Collections; // IEnumerator and IEnumerable
using System.Collections.Generic; // general IEnumerable and IEnumerator
using System.Data; // DataTable and SqlDataType
using System.Data.SqlClient; // SqlConnection, SqlCommand, and SqlParameter
using System.Web.UI.WebControls; // for Parameters.Convert... functions
private static SqlDbType TypeToSqlDbType(Type t) {
DbType dbtc = Parameters.ConvertTypeCodeToDbType(t.GetTypeCodeImpl());
SqlParameter sp = new SqlParameter();
// DbParameter dp = new DbParameter();
// dp.DbType = dbtc;
sp.DbType = dbtc;
return sp.SqlDbType;
}
Edit: Je pensais à et cela fonctionne pour les types System.Data.SqlTypes. Je vais le laisser ici juste au cas où cela pourrait aider quelqu'un à l'avenir.
Je fais quelque chose comme ça:
object objDbValue = DbReader.GetValue(columnIndex);
Type sqlType = DbReader.GetFieldType(columnIndex);
Type clrType = null;
if (sqlType.Name.StartsWith("Sql"))
{
var objClrValue = objDbValue.GetType()
.GetProperty("Value")
.GetValue(objDbValue, null);
clrType = objClrValue.GetType();
}
Parce que chaque SqlDbType a une propriété .Value qui est le type CLR sous-jacent réel, j’utilise la réflexion pour l’obtenir. Il est dommage que SqlDbType n'ait pas d'interface qui contiendrait cette propriété .Value et une réflexion ne serait pas nécessaire.
Ce n'est pas parfait, mais vous n'avez pas besoin de créer, de maintenir ou de renseigner manuellement un dictionnaire. Vous pouvez simplement rechercher un type dans un dict existant, et s'il n'existe pas, utilisez la méthode supérieure pour ajouter le mappage automatiquement .
.__ prend également en charge tous les nouveaux types que SQL Server pourrait recevoir à l’avenir.