Как преобразовать [TYPE] в nullable ‹[TYPE]› в C #?

Вопрос:

Я написал метод для получения результата SQL в виде списка классов, а не данных. Проблема в том, что у меня есть поле int в базе данных, которое допускает значение NULL.

Если я попал в строку с NULL int, DataReader вернет DbNull.Value вместо null. Итак, System.Convert.ChangeType(objVal, fi.FieldType) выдает исключение, потому что он не может преобразовать DbNull в int.

Пока все плохо. Я думал, что решил проблему, когда просто сравнил objVal с DbNull.Value и, если это правда, сделал следующее: System.Convert.ChangeType(null, fi.FieldType)

К сожалению, я только что понял, что в результате получается целочисленный тип 0 вместо NULL.

Поэтому я просто попытался изменить тип int в моем классе на Nullable<int>, но теперь у меня проблема в том, что, когда значение не равно DbNull.Value, ChangeType выдает исключение, потому что не может преобразовать int в _15 _...

Итак, теперь я пытаюсь определить тип объекта, возвращаемого datareader, и преобразовать его в значение, допускающее значение NULL.

tTypeForNullable правильно отображается как Nullable<int>. Но когда я смотрю на тип результата, я получаю: int.

Это почему ? И что еще важнее: как я могу это сделать правильно?

Обратите внимание: поскольку тип - это объект, я не могу использовать общий метод для создания 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));
}

Вот полная процедура для справки:

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

с этим:

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

Обратите внимание: поскольку тип - это объект, я не могу использовать общий метод для создания Nullable<int>.


person Stefan Steiger    schedule 14.06.2012    source источник
comment
Вы видели Entity Framework? msdn.microsoft.com /en-us/library/aa697427(v=vs.80).aspx   -  person Bas    schedule 14.06.2012
comment
@Bas: Да, и это слишком медленно.   -  person Stefan Steiger    schedule 24.09.2014
comment
но намного быстрее, чем отражение, и если вы используете AsNoTracking, он будет почти так же быстро, как устройство чтения данных   -  person Bas    schedule 26.09.2014
comment
Я только что столкнулся с той же проблемой и пришел к выводу, что в C # просто невозможно установить значение типа nullable ‹› с использованием отражения.   -  person fusi    schedule 12.10.2015


Ответы (1)


Вы упаковываете - и результатом операции упаковки для типа значения, допускающего значение NULL, является никогда упакованное значение этого типа. Это либо значение NULL, либо тип значения, не допускающий значения NULL. Дополнительную информацию см. В MSDN.

person Jon Skeet    schedule 14.06.2012
comment
@Quandary: Честно говоря, не совсем понятно, что вы пытаетесь сделать. Проблема с настройкой поля, допускающего значение NULL, путем отражения? Стоит изолировать это от аспектов базы данных и т. Д. - person Jon Skeet; 14.06.2012
comment
Настоящая проблема в том, что System.Convert.ChangeType не работает для типов, допускающих значение NULL. Но я только что увидел, что поле происхождения для моего int на самом деле было varchar ... Вот почему преобразование было необходимо. Теперь с nullable все работает нормально, я просто не могу вызвать convert, если fi.FieldType имеет значение null. - person Stefan Steiger; 14.06.2012
comment
@Quandary: Я не думаю, что это действительно ваша основная проблема - вам не нужно решать это, вам нужно решить настройку своего поля с помощью отражения, верно? Если ChangeType не помогает вам, когда он равен нулю, не используйте его. - person Jon Skeet; 14.06.2012
comment
@John Skeet: Без ChangeType вы не можете напрямую прочитать varchar в поле int, что довольно жалко. С ChangeType он преобразует NULL в 0. Мысль заключалась в том, что с Nullable ‹int›, Convert установит NULL в NULL. Теперь я понимаю, почему это невозможно сделать: Convert не может вернуть тип, допускающий значение NULL, как Object, потому что это приведет к удалению значения Nullable из возвращаемого типа ... Остается только один вопрос: какой смысл имеет это поведение упаковки объектов ... Это делает невозможным преобразование для типов, допускающих значение NULL. Возможно, вам стоит обновить свой ответ на: это невозможно сделать - person Stefan Steiger; 14.06.2012
comment
@Quandary - просто сохраните целочисленное значение в целочисленной переменной, которая допускает значение NULL. - person Security Hound; 14.06.2012
comment
@Ramhound: Да Ramhound, если бы были только целочисленные переменные, это было бы возможно. Но это должно работать для всех типов переменных (string, boolean, datetime, float, decimal, double, int, int64, char - во время выполнения, а не во время компиляции), что требует объекта и запрещает дженерики, и в этом суть . - person Stefan Steiger; 14.06.2012