Pregunta:Cómo convertir [TYPE] a nulable <[TYPE]> en C#?
Escribí un método para recuperar un resultado SQL como una lista de una clase en lugar de una tabla de datos. El problema es que tengo un campo int en la base de datos, que es anulable.
Si lo golpeo una fila con un NULL
int
, DataReader
vuelve DbNull.Value
en lugar de null. Entonces System.Convert.ChangeType(objVal, fi.FieldType)
arroja una excepción, porque no puede convertir DbNull
en int
.
Hasta ahora tan malo. pensé que había resuelto el problema, cuando acabo comparado objVal
-DbNull.Value
y si es verdad, hice esto en su lugar: System.Convert.ChangeType(null, fi.FieldType)
por desgracia, me he dado cuenta, el tipo entero resultante es 0 en lugar de NULL.
Así que sólo intentado cambiar el tipo int en mi clase a Nullable<int>
, pero ahora tengo el problema de que cuando un valor no es DbNull.Value
, ChangeType
se produce una excepción, ya que no se puede convertir a int
nullable<int>
...
Así que ahora trato de detectar el tipo de objeto devuelto por datareader
y convertirlo a un valor que admite valores.
tTypeForNullable
se muestra correctamente como Nullable<int>
. Pero cuando miro el tipo de resultado, obtengo: int
.
¿Por qué es eso? Y más importante: ¿cómo puedo hacer eso correctamente?
Tenga en cuenta que, dado que el tipo es un objeto, no puedo usar un método genérico para crear Nullable<int>
.
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
Aquí está la rutina completa para referencia:
public virtual System.Collections.Generic.IList<T> GetList<T>(System.Data.IDbCommand cmd)
{
System.Collections.Generic.List<T> lsReturnValue = new System.Collections.Generic.List<T>();
T tThisValue = default(T);
Type t = typeof(T);
lock (cmd)
{
using (System.Data.IDataReader idr = ExecuteReader(cmd))
{
lock (idr)
{
while (idr.Read())
{
//idr.GetOrdinal("")
tThisValue = Activator.CreateInstance<T>();
// Console.WriteLine(idr.FieldCount);
for (int i = 0; i < idr.FieldCount; ++i)
{
string strName = idr.GetName(i);
object objVal = idr.GetValue(i);
System.Reflection.FieldInfo fi = t.GetField(strName);
//Type tttttt = fi.FieldType;
if (fi != null)
{
//fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
if (objVal == System.DBNull.Value)
{
objVal = null;
fi.SetValue(tThisValue, null);
}
else
{
//System.ComponentModel.TypeConverter conv = System.ComponentModel.TypeDescriptor.GetConverter(fi.FieldType);
bool bisnull = IsNullable(objVal);
bool bisnullt = IsNullable(fi.FieldType);
if (bisnullt)
{
Type tTypeForNullable = typeof(Nullable<>).MakeGenericType(objVal.GetType());
//object result = Activator.CreateInstance(tTypeForNullable, new object[] { objVal });
//object result = Activator.CreateInstance(typeof(Nullable<int>), new object[] { objVal });
object result = Activator.CreateInstance(tTypeForNullable, objVal);
Type tres = result.GetType();
fi.SetValue(tThisValue, System.Convert.ChangeType(result, fi.FieldType));
}
fi.SetValue(tThisValue, System.Convert.ChangeType(objVal, fi.FieldType));
}
}
else
{
System.Reflection.PropertyInfo pi = t.GetProperty(strName);
if (pi != null)
{
//pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
if (objVal == System.DBNull.Value)
{
objVal = null;
pi.SetValue(tThisValue, null, null);
}
else
{
pi.SetValue(tThisValue, System.Convert.ChangeType(objVal, pi.PropertyType), null);
}
}
// Else silently ignore value
} // End else of if (fi != null)
//Console.WriteLine(strName);
} // Next i
lsReturnValue.Add(tThisValue);
} // Whend
idr.Close();
} // End Lock idr
} // End Using idr
} // End lock cmd
return lsReturnValue;
} // End Function GetList
con esto:
public System.Data.IDataReader ExecuteReader(System.Data.IDbCommand cmd)
{
System.Data.IDataReader idr = null;
lock(cmd)
{
System.Data.IDbConnection idbc = GetConnection();
cmd.Connection = idbc;
if (cmd.Connection.State != System.Data.ConnectionState.Open)
cmd.Connection.Open();
idr = cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
} // End Lock cmd
return idr;
} // End Function ExecuteReader
Tenga en cuenta que debido a que el tipo es un objeto, no puedo usar un método genérico para crear Nullable<int>
.
Ha visto Entity Framework http: //msdn.microsoft.com/en-us/library/aa697427 (v = vs.80) .aspx – Bas
@Bas: Sí, y es demasiado lento. –
pero mucho más rápido que la reflexión, y si usa AsNoTracking será casi tan rápido como un lector de datos – Bas