6

I've a method that generates list of class type from data of data reader.

if (datareader != null && datareader .HasRows)
{
  Dictionary<string, PropertyInfo> pDict= GetPropertyDictionary<T>();
  var fields = GetFieldNames(datareader );
  while (datareader .Read())
  {
    T myobj= new T();
    for (int index = 0; index < fields.Count; index++)
    {                        
      if (pDict.TryGetValue(fields[index], out PropertyInfo info))
      {
        var val1 = datareader .GetValue(index);                                
        info.SetValue(myobj, (val1 == DBNull.Value) ? null : val1, null);
      }
    }
  }
}

I have class properties, some of them are nullable.

public string StudentName{ get; set; }
public decimal? percentage{ get; set; }
public int? StudentNumber{ get; set; }

Code works properly for all properties except StudentNumber which is int.

In above code following line throws exeption Object of type 'System.Int16' cannot be converted to type 'System.Nullable`1[System.Int32] :

info.SetValue(myobj, (val1 == DBNull.Value) ? null : val1, null);

What can be done to solve this issue?

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Priya
  • 1,375
  • 8
  • 21
  • 45
  • Does `(val1 == DBNull.Value) ? null : (int?)val1` work as your ternary? val1's type is Int16 and not nullable, so it doesn't match the type expectation set by returning null in the first expression. – Jonathon Chase Jun 13 '18 at 16:49
  • Yes its ternary. I am facing this issue only when type is int?, otherwise its working with decimal?, datetime?. – Priya Jun 13 '18 at 16:51
  • Possible duplicate of [Conditional operator assignment with Nullable types?](https://stackoverflow.com/questions/75746/conditional-operator-assignment-with-nullablevalue-types) – Dour High Arch Jun 13 '18 at 17:00
  • @Priya: How did you solve this issue? All types use the same method in my case as well – user2782405 Jul 19 '19 at 22:26
  • Hello @user2782405. It was issue with datatype. Since I am using reflection here, I need to specify the same datatype that is reader is returning from database. In database it was smallint and I was trying to convert it in int. so there it was failing. – Priya Sep 26 '19 at 09:30

4 Answers4

4

I don't agree with this code for many reasons but to solve your current problem and answer your question it's because you can't explicitly convert Int16 to Int32 nor to Nullable<Int32> or int?.

To make this happen you need to first convert the value to Int32 then to Nullable<Int3>.

There are better ways but to make it clear what's going on and to fix this one error here you go...

info.SetValue(myobj, val1 == DBNull.Value ? null : (int?)Convert.ToInt32(val1), null);
Michael Puckett II
  • 6,586
  • 5
  • 26
  • 46
  • but I can not directly use (int?) to convert it into Integer. This method is being commonly used for all types like string, int, decimal, date, double etc. I am facing issue only with int. – Priya Jun 13 '18 at 17:22
  • 2
    This is exactly why I don't agree with this code. It's not usable in a variety of ways but the answer was just to clarify your problem and fix that error. I believe what you need, IMO, is to back up and rethink the process a little and I mean this friendly I promise. I don't doubt your skills so don't take it as a blow. I just feel that what you want to do and what you're doing isn't inline with each other. This code assumes too much and doesn't consider the generic behavior you've intended. You're doing good though, please don't read this in disgust, I just answered the specific question – Michael Puckett II Jun 13 '18 at 17:36
  • 2
    @Michale, no no I will not take your suggestion in negative way. I will surely rethink over the taken approach to make it more efficient. :) – Priya Jun 13 '18 at 17:43
  • Thanks man. I certainly mean the best and I'm glad you're not upset. – Michael Puckett II Jun 13 '18 at 17:44
4

The problem that you're having here is that, while you can cast a short to an int, you can't cast a boxed short to an int directly. ie,

object box = (short)5;
var x = (int?)box; // Invalid cast.
var y = (int?)(short?)box; // fine, we cast to short and then to int.

You could change the property type of your class to short?, or you could check if your property's type is Nullable<int32> and use Convert.ToInt32 on the value under that situation.

Jonathon Chase
  • 9,396
  • 21
  • 39
  • Can you suggest any generic solution? Because this method is called and executed for each and every column in datareader – Priya Jun 13 '18 at 17:24
  • @Priya I can't think of a good generic approach to this. You're going to have to write some type-specifc code to handle type-specific conversion. – Jonathon Chase Jun 13 '18 at 17:31
  • One strange thing I tried and it worked. Instead of simple int?, if I use Int16?, i.e. public Int16? StudentNumber { get; set; } , it works without exception. Why so! – Priya Jun 13 '18 at 17:46
  • @Priya For the reasons I outlined above. You _can_ cast from a boxed `Int16` to `Int16?`. You _cannot_ cast from a boxed `Int16` to `Int32` or `Int32?`, and will have to use a `Convert.ToInt32` call. – Jonathon Chase Jun 13 '18 at 17:49
  • Okay, okay! Thanks for explaining this! :) I will have to make some changes in my code then. – Priya Jun 13 '18 at 17:53
1

Try to change type:

var val1 = datareader.GetValue(index);    
var convertedValue = (val1 == DBNull.Value) ? null : Convert.ChangeType(val1, info.PropertyType);                            
info.SetValue(myobj, convertedValue, null);
Backs
  • 24,430
  • 5
  • 58
  • 85
  • It gives another error :System.InvalidCastException: 'Invalid cast from 'System.Int16' to 'System.Nullable`1[[System.Int32,...]] – Priya Jun 13 '18 at 16:46
  • System.InvalidCastException: 'Invalid cast from 'System.Int16' to 'System.Nullable`1[[System.Int32, ]] – Priya Jun 13 '18 at 16:49
0

This error can occur when different types are given, check the field types of your database and your model file for that table.

salvo720
  • 166
  • 1
  • 4