Yeah, yeah, yeah, I know I shouldn’t create aggregate roots. However, sometimes I just want to hack something together and I don’t particularly feel like wasting my time with stored procedures. However, if you’ve got an old list and a new list and you need to sync up the changes to the database, the code to do this isn’t exactly obvious. What’s worse, some of the error messages you get when you get it wrong are positively unhelpful. My personal favourite is “Non-static method requires a target” (what on earth is NHibernate doing that generates that error?) but “a different object with the same identifier value was already associated with the session” when you called a method called session.Merge comes a close second.
Anyway, here’s the code. Wrap it in a transaction if you care at all about performance.
void Synchronise<TValue>(IEnumerable<TValue> current, IEnumerable<TValue> desired) { foreach (var value in current.Except(desired)) { session.Delete(value); } foreach (var value in desired.Except(current)) { session.Save(value); } foreach (var value in desired.Intersect(current)) { session.Merge(value); } }
Note that I’m assuming that TValue implements Equals and GetHashCode correctly, which you should be doing anyway if you’re using NHibernate.