diff --git a/Dapper.Contrib/SqlMapperExtensions.Async.cs b/Dapper.Contrib/SqlMapperExtensions.Async.cs index 8d283f07a..320bb42eb 100644 --- a/Dapper.Contrib/SqlMapperExtensions.Async.cs +++ b/Dapper.Contrib/SqlMapperExtensions.Async.cs @@ -50,7 +50,16 @@ public static async Task GetAsync(this IDbConnection connection, dynamic i foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + if (val == null) continue; + if (property.PropertyType.IsGenericType() && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var genericType = Nullable.GetUnderlyingType(property.PropertyType); + if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null); + } + else + { + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + } } ((IProxy)obj).IsDirty = false; //reset change tracking and return @@ -100,7 +109,16 @@ private static async Task> GetAllAsyncImpl(IDbConnection conne foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + if (val == null) continue; + if (property.PropertyType.IsGenericType() && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var genericType = Nullable.GetUnderlyingType(property.PropertyType); + if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null); + } + else + { + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + } } ((IProxy)obj).IsDirty = false; //reset change tracking and return list.Add(obj); diff --git a/Dapper.Contrib/SqlMapperExtensions.cs b/Dapper.Contrib/SqlMapperExtensions.cs index 696dd6e9d..8d281701c 100644 --- a/Dapper.Contrib/SqlMapperExtensions.cs +++ b/Dapper.Contrib/SqlMapperExtensions.cs @@ -202,7 +202,16 @@ public static T Get(this IDbConnection connection, dynamic id, IDbTransaction foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + if (val == null) continue; + if (property.PropertyType.IsGenericType() && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var genericType = Nullable.GetUnderlyingType(property.PropertyType); + if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null); + } + else + { + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + } } ((IProxy)obj).IsDirty = false; //reset change tracking and return @@ -249,7 +258,16 @@ public static IEnumerable GetAll(this IDbConnection connection, IDbTransac foreach (var property in TypePropertiesCache(type)) { var val = res[property.Name]; - property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + if (val == null) continue; + if (property.PropertyType.IsGenericType() && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) + { + var genericType = Nullable.GetUnderlyingType(property.PropertyType); + if (genericType != null) property.SetValue(obj, Convert.ChangeType(val, genericType), null); + } + else + { + property.SetValue(obj, Convert.ChangeType(val, property.PropertyType), null); + } } ((IProxy)obj).IsDirty = false; //reset change tracking and return list.Add(obj); diff --git a/Dapper.Tests.Contrib/TestSuite.Async.cs b/Dapper.Tests.Contrib/TestSuite.Async.cs index 7c57a3634..413ed5fff 100644 --- a/Dapper.Tests.Contrib/TestSuite.Async.cs +++ b/Dapper.Tests.Contrib/TestSuite.Async.cs @@ -355,6 +355,30 @@ public async Task GetAllAsync() } } + /// + /// Test for issue #933 + /// + [Fact] + public async void GetAsyncAndGetAllAsyncWithNullableValues() + { + using (var connection = GetOpenConnection()) + { + var id1 = connection.Insert(new NullableDate { DateValue = new DateTime(2011, 07, 14) }); + var id2 = connection.Insert(new NullableDate { DateValue = null }); + + var value1 = await connection.GetAsync(id1).ConfigureAwait(false); + Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value); + + var value2 = await connection.GetAsync(id2).ConfigureAwait(false); + Assert.True(value2.DateValue == null); + + var value3 = await connection.GetAllAsync().ConfigureAwait(false); + var valuesList = value3.ToList(); + Assert.Equal(new DateTime(2011, 07, 14), valuesList[0].DateValue.Value); + Assert.True(valuesList[1].DateValue == null); + } + } + [Fact] public async Task InsertFieldWithReservedNameAsync() { diff --git a/Dapper.Tests.Contrib/TestSuite.cs b/Dapper.Tests.Contrib/TestSuite.cs index 44c2bc280..d5d373df0 100644 --- a/Dapper.Tests.Contrib/TestSuite.cs +++ b/Dapper.Tests.Contrib/TestSuite.cs @@ -53,6 +53,19 @@ public class User : IUser public int Age { get; set; } } + public interface INullableDate + { + [Key] + int Id { get; set; } + DateTime? DateValue { get; set; } + } + + public class NullableDate : INullableDate + { + public int Id { get; set; } + public DateTime? DateValue { get; set; } + } + public class Person { public int Id { get; set; } @@ -543,6 +556,29 @@ public void GetAll() } } + /// + /// Test for issue #933 + /// + [Fact] + public void GetAndGetAllWithNullableValues() + { + using (var connection = GetOpenConnection()) + { + var id1 = connection.Insert(new NullableDate { DateValue = new DateTime(2011, 07, 14) }); + var id2 = connection.Insert(new NullableDate { DateValue = null }); + + var value1 = connection.Get(id1); + Assert.Equal(new DateTime(2011, 07, 14), value1.DateValue.Value); + + var value2 = connection.Get(id2); + Assert.True(value2.DateValue == null); + + var value3 = connection.GetAll().ToList(); + Assert.Equal(new DateTime(2011, 07, 14), value3[0].DateValue.Value); + Assert.True(value3[1].DateValue == null); + } + } + [Fact] public void Transactions() { diff --git a/Dapper.Tests.Contrib/TestSuites.cs b/Dapper.Tests.Contrib/TestSuites.cs index 3949138b7..88cafecef 100644 --- a/Dapper.Tests.Contrib/TestSuites.cs +++ b/Dapper.Tests.Contrib/TestSuites.cs @@ -57,6 +57,8 @@ static SqlServerTestSuite() connection.Execute("CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null);"); dropTable("GenericType"); connection.Execute("CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null);"); + dropTable("NullableDates"); + connection.Execute("CREATE TABLE NullableDates (Id int IDENTITY(1,1) not null, DateValue DateTime null);"); } } } @@ -106,6 +108,8 @@ static MySqlServerTestSuite() connection.Execute("CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null);"); dropTable("GenericType"); connection.Execute("CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null);"); + dropTable("NullableDates"); + connection.Execute("CREATE TABLE NullableDates (Id int not null AUTO_INCREMENT PRIMARY KEY, DateValue DateTime);"); } } catch (MySqlException e) @@ -142,6 +146,7 @@ static SQLiteTestSuite() connection.Execute("CREATE TABLE ObjectY (ObjectYId integer not null, Name nvarchar(100) not null) "); connection.Execute("CREATE TABLE ObjectZ (Id integer not null, Name nvarchar(100) not null) "); connection.Execute("CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute("CREATE TABLE NullableDates (Id integer primary key autoincrement not null, DateValue DateTime) "); } } } @@ -173,6 +178,7 @@ static SqlCETestSuite() connection.Execute(@"CREATE TABLE ObjectY (ObjectYId int not null, Name nvarchar(100) not null) "); connection.Execute(@"CREATE TABLE ObjectZ (Id int not null, Name nvarchar(100) not null) "); connection.Execute(@"CREATE TABLE GenericType (Id nvarchar(100) not null, Name nvarchar(100) not null) "); + connection.Execute(@"CREATE TABLE NullableDates (Id int IDENTITY(1,1) not null, DateValue DateTime null) "); } Console.WriteLine("Created database"); }