Reputation: 775
We are using AutoMapper to map from IDataReader to List of Entities.
One Problem that i noticed while unit testing was the following. If we read an bool value from database (bit), AutoMapper does verry fine. But when we us FluentAssertions for UnitTesting there is a problem with the ShouldAllBeEquivalentTo function. It says True expected but True returned on the bool property of the entity.
So i tried to check the bool properties and noticed that expected == returnd
works (returns true
) but expected.Equals(returned)
does not work (returns false
)?!
I thought ==
and equals
should be almost the same for bool type?
What could cause this strange behaviour?
Here some code:
using (var connection = new SqlConnection("server=someServer;user id=someUser;password=***;database=someDatabase;MultipleActiveResultSets=True"))
using (var command = connection.CreateCommand())
{
connection.Open();
var itemsBefore = new List<Item> { new Item { CheckDispo = true } };
command.CommandText = "SELECT CheckDispo FROM Items WHERE ItemId = 1814";
var itemsAfter = Mapper.DynamicMap<List<Item>>(command.ExecuteReader());
var a = itemsAfter[0].CheckDispo.Equals(true); // false
var b = itemsAfter[0].CheckDispo == true; // true
}
public class Item
{
public bool CheckDispo { get; set; }
}
Upvotes: 0
Views: 1250
Reputation: 775
Sorry for this late edit, but I have found what has gone wrong. We read the property out of our database and our 'boolean' is represented by -1 not 1.
Automapper uses dynamically generated IL code to read from a datareader and because they do this, automapper is able to write -1 to the bool field. This results in a value of 0xff in out RAM.
If we now look at the il code for the equals method then we understand wy .equal
is not ==
.
.method public hidebysig newslot virtual final
instance bool Equals(bool obj) cil managed
{
.custom instance void __DynamicallyInvokableAttribute::.ctor() = ( 01 00 00 00 )
// Code size 6 (0x6)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldind.u1
IL_0002: ldarg.1
IL_0003: ceq
IL_0005: ret
} // end of method Boolean::Equals
This loads an 1 and our value to the stack and compares both. Due to the fact that in our RAM the bool is -1 (0xFF) the comparison will return false.
See also this item on github.com for more information.
Upvotes: 0
Reputation: 2271
Both results should return true. I have tested your same scenario above and it does return true in both cases. Please see below screenshot.
The boolean class overrides the Equals method as follows:
public override bool Equals(object obj)
{
if (!(obj as bool))
{
return false;
}
return this == (bool)obj;
}
It also provides a separate overloaded method which accepts a boolean as a parameter. This just uses the equality operator. I think this was added because it provides better performance over the Equals method because it avoids the typecasting overhead.
public bool Equals(bool obj)
{
return this == obj;
}
In your case since you are passing a boolean it should hit the above overloaded method and as you can see, it is just using the === operator.
Upvotes: 2