2012-06-14 27 views
5

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 NULLint, 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 intnullable<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>.

+1

Ha visto Entity Framework http: //msdn.microsoft.com/en-us/library/aa697427 (v = vs.80) .aspx – Bas

+0

@Bas: Sí, y es demasiado lento. –

+0

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

Respuesta

3

Estás boxeo, y el resultado de una operación de boxeo para un tipo de valor que admite valores de nulo es nunca un valor en caja de ese tipo. Es nulo o un tipo de valor que no admite nulos. Vea MSDN para más información.

+1

Genial - Ahora sé el problema, pero no la solución ... –

+0

@Quandary: para ser sincero, no está muy claro qué intentas hacer. ¿El problema es "establecer un campo anulable por reflexión"? Valdría la pena aislar esto de los aspectos de la base de datos, etc. –

+0

El problema real es que System.Convert.ChangeType no funciona para tipos anulables. Pero acabo de ver que el campo de origen para mi int era de hecho varchar ... Por eso era necesario convertir. Ahora con la opción de anulación, todo funciona bien, simplemente no puedo llamar a la conversión si fi.FieldType puede contener nulos. –

Cuestiones relacionadas