Differ is a fast generic object compare tool. It's based on SqlDiffer from Dapper found here.
I needed a solution that could do change tracking on "stateless" (different instances) of the same type T when persisting (i.e. original vs. modified/current). During an update, the original value of T is pulled from the database and compared with the current object posted to the server. From here we can make business decisions based on what properties have changed and produce a human-readable audit log.
Snappshotter works really well for objects are not serialized over the wire. When dealing with a REST facade and a statless web connection, during a POST or PUT back do the server, I needed a way to yank out the original object and compare it to the modified one- not simply for SQL generation but also for auditing purposes.
The real magic is in IL emitting (Specifically Emit and CreateDelegate methods). @SamSaffron, the original creator of Dapper (and Snapshotter), explains at a high level how this paid dividends for Stack Overflow here.
TLDR: You don't pay a reflection penalty for reading object properties after the first comparison of any given T; the "Differ" method is cached as a Func.
With a little help from our friend AutoFixture we can fill our SimpleType "object under test" with mock changes:
//in the real world, we'd be comparing the updated value with what's currently in the repository
var @out = fixture.CreateAnonymous<SimpleType>();
var outChanged = fixture.CreateAnonymous<SimpleType>();
var changes = Differ<SimpleType>.Diff(@out, outChanged).ToList();That's it.
There's also a small helper method for creating more human-friendly messages useful for logging to an audit log (given changes from above):
var auditText = diffs.ToFriendlyDescription();which produces something simiar to the following:
SimpleType had the following properties changed:
SomeProp: 0 -> 1
Unfortunately, this code will not cure cancer or stupidity. Though pull requests are welcome.
It also won't compare "sub types"- that is, given:
public class SimpleType
{
public int SomeProp { get; set; }
}
public class ComplexType
{
public SimpleType SimpleTypeProp { get; set; }
}... it won't compare SimpleTypeProp. This would require some recursive diffing and I haven't encounted a need for it yet (of course contributions are welcome 😃).
- Dapper (specifically, Snapshotter mentioned earlier).
- Bookkeeper by @iristyle
There may be others... if you know of one, create an issue and I'll list it here.