Option SqlMapper.Settings.ApplyNullValues is not respected
Nobody has claimed this yet.
- Dominant language
- C#
- Stars
- 18.4k
- Forks
- 3.7k
- Avg merge
- 5h 8m
- Merged PRs (30d)
- 1
Description
In the following case, option SqlMapper.Settings.ApplyNullValues is not respected, null-values are not assigned:
I have a DTO (PersonDto) with some properties (Id, GivenName, MiddleName, Surname, Picture). The Id is of data type Int32, the other properties are Settable<T>:
public class PersonDto {
public int Id { get; set; }
public Settable<string> GivenName { get; set; }
public Settable<string?> MiddleName { get; set; }
public Settable<string> Surname { get; set; }
public Settable<byte[]?> Picture { get; set; }
}
Settable<T> is a structure (value type) that allows to distinguish whether a value was assigned null or was not initialized. Imagine now a data access method that returns a list of PersonDto and that has a Boolean parameter IncludePicture that is false by default and that adjusts the SQL query accordingly to avoid loading expensive property Picture when it is not needed. When the consumer of this method now accidentally accesses uninitialized property Picture, an InvalidOperationException is thrown. Unfortunately that is now also the case for the MiddleName that should have been initialized with null and wasn't. Here the test method:
[TestMethod]
public void Null_Value_Is_Not_Applied_Although_ApplyNullValues_Flag_Is_Set() {
LocalDB localDB = LocalDBManager.CreateLocalDB("SQL-Scripts"); //Creates a new database on LocalDB-SQLServer
IDbConnection dbConnection = localDB.DatabaseConnection;
dbConnection.EnsureSettableConvertersLoaded(); //Sets ApplyNullValues and registers the converters to Settable<T>
Assert.IsTrue(SqlMapper.Settings.ApplyNullValues);
PersonDto actual = dbConnection.QuerySingle<PersonDto>("SELECT Id, GivenName, MiddleName, Surname FROM Person WHERE Id = 3;");
Assert.IsTrue(SqlMapper.Settings.ApplyNullValues);
Assert.AreEqual("Kerry", actual.GivenName);
Assert.AreEqual("Norton", actual.Surname);
Assert.IsNull(actual.MiddleName.Value); // <--- Fails (null was not assigned!)
Assert.ThrowsException<InvalidOperationException>(() => actual.Picture.Value); //Works as expected, throws an exception because the value was not initialized.
}
And here the prove that the bug is not in Settable<T>:
[TestMethod]
public void Settable_Works_As_Expected() {
PersonDto actual = new PersonDto();
actual.GivenName = "Kerry";
actual.MiddleName = null;
actual.Surname = "Norton";
Assert.AreEqual("Kerry", actual.GivenName);
Assert.AreEqual("Norton", actual.Surname);
Assert.IsNull(actual.MiddleName.Value); //Works, null was assigned.
Assert.ThrowsException<InvalidOperationException>(() => actual.Picture.Value); //Works as expected, throws an exception because the value was not initialized.
}
Here the complete test project (Visual Studio 2022 solution) including database initialization on test execution (using LocalDB):
BugReport.zip
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by running the supplied Visual Studio test project from BugReport.zip and the test method Null_Value_Is_Not_Applied_Although_ApplyNullValues_Flag_Is_Set against LocalDB. Trace the QuerySingle mapping and the Settable converters after EnsureSettableConvertersLoaded(). Done means MiddleName.Value is null while the unselected Picture property remains uninitialized and still throws.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp, sql
- Domain
- backend, databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100