Advertisement
Guest User

Untitled

a guest
Feb 21st, 2019
58
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.42 KB | None | 0 0
  1. /// <summary>
  2. /// Gets the changes [Deleted, changed, inserted] comparing this collection to another.
  3. /// </summary>
  4. /// <param name="local">The source collection.</param>
  5. /// <param name="remote">The remote collection to comare agains.</param>
  6. /// <param name="keySelector">The primary key selector function</param>
  7. /// <returns></returns>
  8. public static ChangeResult<TSource> CompareTo<TSource, TKey>(this IEnumerable<TSource> local, IEnumerable<TSource> remote, Func<TSource, TKey> keySelector)
  9. {
  10. if (local == null)
  11. throw new ArgumentNullException("local");
  12. if (remote == null)
  13. throw new ArgumentNullException("remote");
  14. if (keySelector == null)
  15. throw new ArgumentNullException("keySelector");
  16.  
  17. var remoteKeyValues = remote.ToDictionary(keySelector);
  18.  
  19. var deleted = new List<TSource>();
  20. var changed = new List<TSource>();
  21. var localKeys = new HashSet<TKey>();
  22.  
  23. foreach (var localItem in local)
  24. {
  25. var localKey = keySelector(localItem);
  26. localKeys.Add(localKey);
  27.  
  28. /* Check if primary key exists in both local and remote
  29. * and if so check if changed, if not it has been deleted
  30. */
  31. TSource changeCandidate;
  32. if (remoteKeyValues.TryGetValue(localKey, out changeCandidate))
  33. {
  34. if (!changeCandidate.Equals(localItem))
  35. changed.Add(changeCandidate);
  36. }
  37. else
  38. {
  39. deleted.Add(localItem);
  40. }
  41. }
  42. var inserted = remoteKeyValues
  43. .Where(x => !localKeys.Contains(x.Key))
  44. .Select(x => x.Value)
  45. .ToList();
  46.  
  47. return new ChangeResult<TSource>(deleted, changed, inserted);
  48. }
  49.  
  50. /// <summary>
  51. /// Immutable class containing changes
  52. /// </summary>
  53. public class ChangeResult<T>
  54. {
  55. public ChangeResult(IList<T> deleted, IList<T> changed, IList<T> inserted)
  56. {
  57. Deleted = new ReadOnlyCollection<T>(deleted);
  58. Changed = new ReadOnlyCollection<T>(changed);
  59. Inserted = new ReadOnlyCollection<T>(inserted);
  60. }
  61.  
  62. public IList<T> Deleted { get; private set; }
  63. public IList<T> Changed { get; private set; }
  64. public IList<T> Inserted { get; private set; }
  65. }
  66.  
  67. var changes = Col1.CompareTo(Col2, x => x.UniqueId);
  68.  
  69. /// <summary>
  70. /// Immutable class containing changes
  71. /// </summary>
  72. public sealed class ChangeResult<T> : IChangeResult<T>
  73. {
  74. private readonly ReadOnlyCollection<T> deleted;
  75.  
  76. private readonly ReadOnlyCollection<T> changed;
  77.  
  78. private readonly ReadOnlyCollection<T> inserted;
  79.  
  80. public ChangeResult(IList<T> deleted, IList<T> changed, IList<T> inserted)
  81. {
  82. this.deleted = new ReadOnlyCollection<T>(deleted);
  83. this.changed = new ReadOnlyCollection<T>(changed);
  84. this.inserted = new ReadOnlyCollection<T>(inserted);
  85. }
  86.  
  87. public IList<T> Deleted
  88. {
  89. get
  90. {
  91. return this.deleted;
  92. }
  93. }
  94.  
  95. public IList<T> Changed
  96. {
  97. get
  98. {
  99. return this.changed;
  100. }
  101. }
  102.  
  103. public IList<T> Inserted
  104. {
  105. get
  106. {
  107. return this.inserted;
  108. }
  109. }
  110. }
  111.  
  112. public interface IChangeResult<T>
  113. {
  114. IList<T> Deleted
  115. {
  116. get;
  117. }
  118.  
  119. IList<T> Changed
  120. {
  121. get;
  122. }
  123.  
  124. IList<T> Inserted
  125. {
  126. get;
  127. }
  128. }
  129.  
  130. /// <summary>
  131. /// Gets the changes [Deleted, changed, inserted] comparing this collection to another.
  132. /// </summary>
  133. /// <typeparam name="TSource"></typeparam>
  134. /// <typeparam name="TKey"></typeparam>
  135. /// <param name="local">The source collection.</param>
  136. /// <param name="remote">The remote collection to compare against.</param>
  137. /// <param name="keySelector">The primary key selector function</param>
  138. /// <returns></returns>
  139. public static IChangeResult<TSource> CompareTo<TSource, TKey>(
  140. this IEnumerable<TSource> local,
  141. IEnumerable<TSource> remote,
  142. Func<TSource, TKey> keySelector)
  143. {
  144. if (local == null)
  145. {
  146. throw new ArgumentNullException("local");
  147. }
  148.  
  149. if (remote == null)
  150. {
  151. throw new ArgumentNullException("remote");
  152. }
  153.  
  154. if (keySelector == null)
  155. {
  156. throw new ArgumentNullException("keySelector");
  157. }
  158.  
  159. local = local.ToList();
  160.  
  161. var remoteKeyValues = remote.ToDictionary(keySelector);
  162. var deleted = new List<TSource>(local.Count());
  163. var changed = new List<TSource>(local.Count());
  164. var localKeys = new HashSet<TKey>();
  165.  
  166. Parallel.ForEach(
  167. local,
  168. localItem =>
  169. {
  170. var localKey = keySelector(localItem);
  171.  
  172. lock (localKeys)
  173. {
  174. localKeys.Add(localKey);
  175. }
  176.  
  177. /* Check if primary key exists in both local and remote
  178. * and if so check if changed, if not it has been deleted
  179. */
  180. TSource changeCandidate;
  181.  
  182. if (remoteKeyValues.TryGetValue(localKey, out changeCandidate))
  183. {
  184. if (changeCandidate.Equals(localItem))
  185. {
  186. return;
  187. }
  188.  
  189. lock (changed)
  190. {
  191. changed.Add(changeCandidate);
  192. }
  193. }
  194. else
  195. {
  196. lock (deleted)
  197. {
  198. deleted.Add(localItem);
  199. }
  200. }
  201. });
  202.  
  203. var inserted = remoteKeyValues
  204. .AsParallel()
  205. .Where(x => !localKeys.Contains(x.Key))
  206. .Select(x => x.Value)
  207. .ToList();
  208.  
  209. return new ChangeResult<TSource>(deleted, changed, inserted);
  210. }
  211.  
  212. /// <summary>
  213. /// Gets the changes [Deleted, changed, inserted] comparing this collection to another.
  214. /// </summary>
  215. /// <param name="local">The source collection.</param>
  216. /// <param name="remote">The remote collection to comare agains.</param>
  217. /// <param name="keySelector">The primary key selector function</param>
  218. /// <param name="compareFunc">Optional camparing function between 2 objects of type TSource</param>
  219. /// <returns>List of changes as Added, Removed and Changed items of type TSource</returns>
  220. public static ChangeResult<TSource> CompareTo<TSource, TKey>(
  221. this IEnumerable<TSource> local, IEnumerable<TSource> remote, Func<TSource, TKey> keySelector, Func<TSource, TSource, bool> compareFunc = null)
  222. {
  223. if (local == null)
  224. throw new ArgumentNullException("local");
  225. if (remote == null)
  226. throw new ArgumentNullException("remote");
  227. if (keySelector == null)
  228. throw new ArgumentNullException("keySelector");
  229.  
  230. var remoteKeyValues = new ConcurrentDictionary<TKey, TSource>(remote.ToDictionary(keySelector));
  231. var localKeyValues = new ConcurrentDictionary<TKey, TSource>(local.ToDictionary(keySelector));
  232. var changed = new ConcurrentBag<Tuple<TSource, TSource>>();
  233.  
  234. Parallel.ForEach(
  235. local,
  236. localItem =>
  237. {
  238. var localItemKey = keySelector(localItem);
  239.  
  240. //1. Check if item is both in local and remote
  241. if (remoteKeyValues.TryRemove(localItemKey, out var remoteItemValue))
  242. {
  243. //1.a item is in both collections -> check if they are different
  244. var isItemChanged = compareFunc != null ? !compareFunc(localItem, remoteItemValue) :
  245. !localItem.Equals(remoteItemValue);
  246.  
  247. if (isItemChanged)
  248. {
  249. //1.b are different -> mark a change
  250. changed.Add(new Tuple<TSource, TSource>(localItem, remoteItemValue));
  251. }
  252.  
  253. //1.c remove the item from local list as it's prensent in remote list too
  254. //this should never return false
  255. localKeyValues.TryRemove(localItemKey, out var localItemValue);
  256. }
  257.  
  258. //2. if item is not in remote list means it has been removed
  259. });
  260.  
  261. var deleted = localKeyValues.Values;
  262. var inserted = remoteKeyValues.Values;
  263.  
  264. return new ChangeResult<TSource>(deleted, changed, inserted);
  265. }
  266.  
  267. /// <summary>
  268. /// Immutable class containing changes
  269. /// </summary>
  270. public sealed class ChangeResult<T>
  271. {
  272. public ChangeResult(IEnumerable<T> deleted, IEnumerable<Tuple<T, T>> changed, IEnumerable<T> inserted)
  273. {
  274. Deleted = deleted;
  275. Changed = changed;
  276. Inserted = inserted;
  277. }
  278.  
  279. public IEnumerable<T> Deleted { get; }
  280.  
  281. public IEnumerable<Tuple<T, T>> Changed { get; }
  282.  
  283. public IEnumerable<T> Inserted { get; }
  284. }
  285.  
  286. [TestClass]
  287. public class ListUtilsTests
  288. {
  289. class User
  290. {
  291. public string Key { get; set; }
  292.  
  293. public string Name { get; set; }
  294. }
  295.  
  296. [TestMethod]
  297. public void ListUtilsCompare()
  298. {
  299. var local = new[] { new User() { Key = "A", Name = "John" }, new User() { Key = "B", Name = "Red" } };
  300. var remote = new[] { new User() { Key = "B", Name = "Red (edited)" }, new User() { Key = "C", Name = "Tizio" } };
  301.  
  302. var changes = local.CompareTo(remote, _ => _.Key, (i1, i2) => i1.Name == i2.Name);
  303.  
  304. Assert.IsNotNull(changes);
  305. Assert.AreEqual(1, changes.Inserted.Count());
  306. Assert.AreEqual("Tizio", changes.Inserted.First().Name);
  307. Assert.AreEqual(1, changes.Deleted.Count());
  308. Assert.AreEqual("John", changes.Deleted.First().Name);
  309. Assert.AreEqual(1, changes.Changed.Count());
  310. Assert.AreEqual("Red (edited)", changes.Changed.First().Item2.Name);
  311. }
  312. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement