Core 3.1 Invalid cast from System.DBNull
KovtunV opened this issue · comments
Hello, in version 0.8.0 on Core 3.1 I have an exception after "Insert".
Invalid cast from 'System.DBNull' to 'System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
It happens if I have a nullable navigation property:
public class Consumer
{
public int Id { get; set; }
public string Name { get; set; }
public Transgas Transgas { get; set; }
public int? TransgasId { get; set; }
}
public class Transgas
{
public int Id { get; set; }
public string Name { get; set; }
}
If I hide property "public Transgas Transgas { get; set; }" all will be okay
Test context:
public class MyDb : DbContext
{
public DbSet<Consumer> Consumers { get; set; }
public DbSet<Transgas> Transgases { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("Host=127.0.0.1;Port=5432;Database=TestBulk;Username=postgres;Password=123;Timeout=120;Command Timeout=300");
}
}
Entry point:
class Program
{
static void Main(string[] args)
{
var data = GetConsumers().ToArray();
var db = new MyDb();
var bulk = new NpgsqlBulkUploader(db);
bulk.Insert(data); // Invalid cast from 'System.DBNul
}
static IEnumerable<Consumer> GetConsumers()
{
yield return new Consumer
{
Name = "My test name"
};
}
}
Hi @KovtunV, thanks for posting this! Should be fixed this in 0.8.1, please check when possible.
Thanks but it still doesn't work. Now I have an exception like:
Invalid cast from 'System.Int32' to 'System.Nullable`1[[System.Int32, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]'.
This is because of the nullable id.
For example:
var typeTo = typeof(int?);
var myId = 1078;
var res = Convert.ChangeType(myId, typeTo); // this exception
Before your fixing, I solved this problem by means of runtime method injection and replaced method ReadValue to
public static object ReadValue(Type expectedType, NpgsqlDataReader reader, string columnName)
{
var value = reader[columnName];
if (value == null)
return value;
var actual = value.GetType();
if (actual == typeof(DBNull))
{
return null;
}
if (expectedType == actual)
return reader[columnName];
else if (actual == typeof(DateTimeOffset) && expectedType == typeof(DateTime))
return ((DateTimeOffset)value).DateTime;
else
{
var nullableSubtype = Nullable.GetUnderlyingType(expectedType);
return Convert.ChangeType(value, nullableSubtype ?? expectedType);
}
}
If this is the true fix, I think you should add it
else
{
var nullableSubtype = Nullable.GetUnderlyingType(expectedType);
return Convert.ChangeType(value, nullableSubtype ?? expectedType);
}
Oh, sorry, I forgot to check this case. Implemented in 0.8.1.1