SHARE
TWEET

Untitled

a guest Feb 21st, 2019 45 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top