c# ado.net system.data

c# - Tipo de sistema.NET a SqlDbType



ado.net system.data (2)

Estaba buscando una conversión inteligente entre .Net System.Type y SqlDbType. Lo que encontré fue la siguiente idea:

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; }

El código anterior no es realmente agradable y es un olor a código, por lo que escribí la siguiente, ingenua, no inteligente, pero útil función, basada en 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)]; }

¿Alguien tiene idea de cómo obtener el mismo resultado de una manera más limpia, mejor y agradable?


Editar: estaba pensando y esto funciona para los tipos System.Data.SqlTypes. Lo dejaré aquí solo en caso de que ayude a alguien en el futuro.

Yo hago algo como esto:

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(); }

Como cada SqlDbType tiene una propiedad .Value que es el tipo de CLR subyacente real, yo uso el reflejo para obtenerlo. Es una lástima que SqlDbType no tenga alguna interfaz que pueda contener esta propiedad .Value y la reflexión no sería necesaria.
No es perfecto, pero no es necesario crear, mantener o completar manualmente un diccionario. Puede buscar un tipo en un diccionario existente y, si no existe, utilice el método superior para agregar el mapa automáticamente. Casi autogenerado.
También se ocupa de los tipos nuevos que SQL Server pueda recibir en el futuro.


Su enfoque es un buen comienzo, pero poblar ese diccionario solo debe hacerse una vez , como dice Ian en un comentario.

Aquí hay un GIST que se basa en la misma idea, aunque no convierte entre los mismos tipos de tipos: https://gist.github.com/abrahamjp/858392

Advertencia

Tengo un ejemplo de trabajo a continuación, pero debe tener en cuenta que este enfoque tiene algunos problemas. Por ejemplo:

  • Para una string , ¿cómo elegir la correcta entre Char , NChar , VarChar , NVarChar , Text o NText (o incluso Xml , tal vez) ?
  • Y para blobs como byte[] , ¿deberías usar Binary , VarBinary o Image ?
  • Para decimal , float y double , ¿debería SmallMoney Decimal , Float , Money , SmallMoney o Real ?
  • Para un DateTime , ¿necesita DateTime2 , DateTimeOffset , DateTime o SmallDateTime ?
  • ¿Estás usando tipos Nullable , como int? ? Es muy probable que estos den el mismo tipo de SqlDbType como el tipo subyacente.

Además, el solo hecho de proporcionar un Type le dice nada sobre otras restricciones, como el tamaño del campo y la precisión. Tomar la decisión correcta también se trata de cómo se usan los datos en su aplicación y cómo se almacenan en la base de datos.

Lo mejor que puedes hacer es dejar que un ORM haga esto por ti.

Código

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)); } }

Y así es como lo usarías:

var sqlDbType = SqlHelper.GetDbType<string>(); // or: var sqlDbType = SqlHelper.GetDbType(typeof(DateTime?)); // or: var sqlDbType = SqlHelper.GetDbType(property.PropertyType);