Commits

sake  committed acf1534

test failed

  • Participants
  • Parent commits fa552e7

Comments (0)

Files changed (2)

File Usually/Resolve.cs

     {
         public string TableName = null;
         protected TransactionRecord() : this(null) { }
-        protected TransactionRecord(string tableName)
-        {
-            this.TableName = @tableName;
-        }
+        protected TransactionRecord(string tableName) { this.TableName = @tableName; }
         public abstract TransactionRecord CloneRecord();
         public abstract void Execute(ITransactor actor);
     }
             actor.Delete(this);
         }
     }
+    public class TransactionJournal<T>
+    {
+        public TransactionRecord Record;
+        public T Old;
+        public T New;
+    }
     public interface IKeyBuilder<T>
     {
         void Key(string name, Func<T, object> getKey, Func<object, string> generator = null);
     {
         string TableName { get; }
         object[] GetKey(T obj);
+        object[] GetKey(Dictionary<string, object> dict);
+        object[] GetExtraKey(string group, T obj);
+        object[] GetExtraKey(string group, Dictionary<string, object> dict);
         void ForEachKey(T obj, Action<string, object> action);
         void ForEachExtraKey(string group, T obj, Action<string, object> action);
         void ForEachInsertValue(T obj, Action<string, object> action);
                     values[i] = this.getters[this.names[i]](obj);
                 return values;
             }
+            public object[] GetFrom(Dictionary<string, object> dict)
+            {
+                var values = new object[this.names.Count];
+                for (int i = 0; i < this.names.Count; i++)
+                    values[i] = dict[this.names[i]];
+                return values;
+            }
         }
         private class KeysDef: ColumnsDef, IKeyBuilder<T>
         {
         {
             return this.keys.GetFrom(obj);
         }
+        object[] IMetaInfo<T>.GetKey(Dictionary<string, object> dict)
+        {
+            return this.keys.GetFrom(dict);
+        }
+        object[] IMetaInfo<T>.GetExtraKey(string group, T obj)
+        {
+            return this.extraKeys[group].GetFrom(obj);
+        }
+        object[] IMetaInfo<T>.GetExtraKey(string group, Dictionary<string, object> dict)
+        {
+            return this.extraKeys[group].GetFrom(dict);
+        }
         void IMetaInfo<T>.ForEachKey(T obj, Action<string, object> action)
         {
             this.keys.ForEach(obj, action);
         {
             this.values.ForEachChange(o, n, action);
         }
-        public Reconciler<T> CreateReconciler(IEnumerable<T> olds, IEnumerable<T> news)
-        {
-            return new Reconciler<T>(this, olds, news);
-        }
         #endregion
     }
     public delegate void Meta<T>(IMetaBuilder<T> m);
-    public class Reconciler<T>
+    class Reconciler<T>
     {
-        public struct Insertion
-        {
-            public TransactionRecord Record;
-            public object Object;
-        }
         private IMetaInfo<T> info = null;
         private IEnumerable<T> olds = null;
         private IEnumerable<T> news = null;
             if (r.Count != list.Count()) throw new Exception("Duplicate keys");
             return r;
         }
-        public IEnumerable<Insertion> ReconcileInsertion()
+        private TransactionJournal<T> CreateJournal(T o, T n)
+        {
+            if (o == null && n == null) throw new Exception("To create TransactionJournal, Old and New object cannot be both null");
+            if (o == null && n != null)
+            {
+                var r = new InsertRecord(this.info.TableName);
+                this.info.ForEachInsertValue(n, (k, v) => r.Values[k] = v);
+                if (!r.Values.Any()) throw new Exception("Not enough meta information to create TransactionJournal");
+                return new TransactionJournal<T> { Record = r, Old = o, New = n };
+            }
+            if (o != null && n != null)
+            {
+                var r = new UpdateRecord(this.info.TableName);
+                r.AtMostOne = true;
+                this.info.ForEachKey(n, r.Keys.Add);
+                this.info.ForEachValueChange(o, n, (k, ov, nv) => r.Values[k] = nv);
+                if (r.Values.Any())
+                {
+                    if (!r.Keys.Any()) throw new Exception("Not enough meta information to create TransactionJournal");
+                    return new TransactionJournal<T> { Record = r, Old = o, New = n };
+                }
+                else
+                {
+                    return new TransactionJournal<T> { Record = null, Old = o, New = n };
+                }
+            }
+            if (o != null && n == null)
+            {
+                var r = new DeleteRecord(this.info.TableName);
+                r.AtMostOne = true;
+                this.info.ForEachKey(o, r.Keys.Add);
+                if (!r.Keys.Any()) throw new Exception("Not enough meta information to create TransactionJournal"); 
+                return new TransactionJournal<T> { Record = r, Old = o, New = n };
+            }
+            throw new Exception("Should not reah here");
+        }
+        public IEnumerable<TransactionJournal<T>> ReconcileNonDelete()
         {
             foreach (var n in this.news)
             {
                 var key = this.info.GetKey(n);
-                T o = default(T);
-                if (!this.oldLookup.TryGetValue(key, out o))
-                {
-                    var r = new InsertRecord(this.info.TableName);
-                    this.info.ForEachInsertValue(n, (k, v) => r.Values[k] = v);
-                    if (r.Values.Any()) yield return new Insertion { Record = r, Object = n };
-                }
+                T o;
+                if (! this.oldLookup.TryGetValue(key, out o)) o = default(T);
+                yield return this.CreateJournal(o, n);
             }
         }
-        public IEnumerable<TransactionRecord> ReconcileInsert()
+        public IEnumerable<TransactionJournal<T>> ReconcileInsert()
         {
-            return from i in this.ReconcileInsertion() select i.Record;
+            return from j in this.ReconcileNonDelete()
+                   where j.Record is InsertRecord
+                   select j;
         }
-        public IEnumerable<TransactionRecord> ReconcileUpdateOrDelete()
+        public IEnumerable<TransactionJournal<T>> ReconcileNonInsert()
         {
             foreach (var o in this.olds)
             {
                 var key = this.info.GetKey(o);
-                T n = default(T);
-                if (newLookup.TryGetValue(key, out n))
-                {
-                    var r = new UpdateRecord(this.info.TableName);
-                    r.AtMostOne = true;
-                    this.info.ForEachKey(n, (k, v) => r.Keys[k] = v);
-                    this.info.ForEachValueChange(o, n, (k, ov, nv) => r.Values[k] = nv);
-                    if (r.Keys.Any() && r.Values.Any()) yield return r;
-                }
-                else
-                {
-                    var r = new DeleteRecord(this.info.TableName);
-                    r.AtMostOne = true;
-                    this.info.ForEachKey(o, (k, v) => r.Keys[k] = v);
-                    if (r.Keys.Any()) yield return r;
-                }
+                T n;
+                if (! this.newLookup.TryGetValue(key, out n)) n = default(T);
+                yield return this.CreateJournal(o, n);
             }
         }
-        public IEnumerable<TransactionRecord> Reconcile()
+        public IEnumerable<TransactionJournal<T>> ReconcileDelete()
         {
-            return this.ReconcileInsert().Union(this.ReconcileUpdateOrDelete());
+            return from j in this.ReconcileNonInsert()
+                   where j.Record is DeleteRecord
+                   select j;
         }
     }
-    public class TransactionJournal
-    {        
-    }
-    public class InsertJournal: TransactionJournal
-    {
-        public InsertRecord Record { get; set; }
-        public List<InsertJournal> DetailInsert = new List<InsertJournal>();
-        public List<UpdateRecord> DetailUpdate = new List<UpdateRecord>();
-    }
-    public class DeleteJournal: TransactionJournal
-    {
-        public DeleteRecord Record { get; set; }
-        public List<DeleteJournal> DetailDelete = new List<DeleteJournal>();
-        public List<DeleteJournal> DetailCascade = new List<DeleteJournal>();
-    }
     public class Resolver
     {
         public const int DEFAULT_DUMMY_START = -1;
         private List<IResolverItem> items = new List<IResolverItem>();
-        public ResolverItem<T> Add<T>(Meta<T> meta, IEnumerable<T> olds, IEnumerable<T> news)
+        public ResolverItem<T> Add<T>(Meta<T> meta, IEnumerable<T> olds, IEnumerable<T> news) where T : class 
         {
             var r = new ResolverItem<T>(meta, null, null, olds, news);
             this.items.Add(r);
         }
         public IEnumerable<TransactionRecord> Resolve()
         {
-            return null;
+            return this.items.SelectMany((i) => i.Resolve());
         }
-        public static IEnumerable<TransactionRecord> Reconcile<T>(Meta<T> meta, IEnumerable<T> olds, IEnumerable<T> news)
+        public static IEnumerable<TransactionRecord> Resolve<T>(Meta<T> meta, IEnumerable<T> olds, IEnumerable<T> news) where T: class
         {
-            var m = new MetaInfoBuilder<T>();
-            meta(m);
-            return m.CreateReconciler(olds, news).Reconcile();
+            var r = new Resolver();
+            r.Add(meta, olds, news);
+            return r.Resolve();
         }
     }
-    public interface IResolverItem
+    interface IResolverItem
     {
         string Group { get; }
+        IEnumerable<TransactionRecord> Resolve();
+        IEnumerable<TransactionRecord> ResolveForMasterInsert(object[] refKey, object[] actualKey);
+        IEnumerable<TransactionRecord> ResolveForMasterStatic(object[] refKey);
+        IEnumerable<TransactionRecord> ResolveForMasterDelete(object[] refKey);
     }
-    public class ResolverItem<T> : IResolverItem
+    public class ResolverItem<T> : IResolverItem where T: class
     {
         private List<IResolverItem> items = new List<IResolverItem>();
         private IMetaInfo<T> info = null;
         private string group = null;
-        private Func<T, object> getRef = null;
+        private Func<T, object[]> getRef = null;
         private IEnumerable<T> olds = null;
         private IEnumerable<T> news = null;
+        private Dictionary<object[], List<TransactionJournal<T>>> _journals = null;
+        protected Dictionary<object[], List<TransactionJournal<T>>> CreateJournals()
+        {
+            var r = new Dictionary<object[], List<TransactionJournal<T>>>();
+            var reconciler = new Reconciler<T>(this.info, this.olds, this.news);
+            foreach (var j in reconciler.ReconcileNonDelete())
+            {
+                var k = this.getRef(j.New);
+                List<TransactionJournal<T>> list = null;
+                if (! r.TryGetValue(k, out list)) 
+                {
+                    list = new List<TransactionJournal<T>>();
+                    r.Add(k, list);
+                }
+                list.Add(j);
+            }
+            foreach (var j in reconciler.ReconcileDelete())
+            {
+                var k = this.getRef(j.Old);
+                List<TransactionJournal<T>> list = null;
+                if (! r.TryGetValue(k, out list)) 
+                {
+                    list = new List<TransactionJournal<T>>();
+                    r.Add(k, list);
+                }
+                list.Add(j);
+            }
+            return r;
+        }
+        protected IEnumerable<TransactionJournal<T>> GetJournals(object[] masterKey)
+        {
+            if (this._journals == null) this._journals = this.CreateJournals();
+            List<TransactionJournal<T>> r = null;
+            if (!this._journals.TryGetValue(masterKey, out r)) return Enumerable.Empty<TransactionJournal<T>>();
+            return r;
+        }
         string IResolverItem.Group { get { return this.group; } }
-        public ResolverItem(Meta<T> meta, string group, Func<T, object> getRef, IEnumerable<T> olds, IEnumerable<T> news)
+        public ResolverItem(Meta<T> meta, string group, Func<T, object[]> getRef, IEnumerable<T> olds, IEnumerable<T> news)
         {
             var m = new MetaInfoBuilder<T>();
             meta(m);
             this.olds = @olds;
             this.news = @news;
         }
-        public ResolverItem<D> Add<D>(Meta<D> meta, Func<D, object> getRef, IEnumerable<D> olds, IEnumerable<D> news)
+        public ResolverItem<D> Add<D>(Meta<D> meta, Func<D, object[]> getRef, IEnumerable<D> olds, IEnumerable<D> news) where D: class
         {
             return this.Add(meta, null, getRef, olds, news);
         }
-        public ResolverItem<D> Add<D>(Meta<D> meta, string group, Func<D, object> getRef, IEnumerable<D> olds, IEnumerable<D> news)
+        public ResolverItem<D> Add<D>(Meta<D> meta, Func<D, object> getRef, IEnumerable<D> olds, IEnumerable<D> news) where D : class
+        {
+            return this.Add(meta, null, (x) => new object[] { getRef(x) }, olds, news);
+        }
+        public ResolverItem<D> Add<D>(Meta<D> meta, string group, Func<D, object[]> getRef, IEnumerable<D> olds, IEnumerable<D> news) where D : class
         {
             var i = new ResolverItem<D>(meta, group, getRef, olds, news);
             this.items.Add(i);
             return i;
         }
-        public IEnumerable<TransactionRecord> Resolve()
+        public ResolverItem<D> Add<D>(Meta<D> meta, string group, Func<D, object> getRef, IEnumerable<D> olds, IEnumerable<D> news) where D : class
+        {
+            return this.Add(meta, group, (x) => new object[] { getRef(x) }, olds, news);
+        }
+        IEnumerable<TransactionRecord> IResolverItem.Resolve()
+        {
+            return this.Resolve2();
+            //var r = new Reconciler<T>(this.info, this.olds, this.news);
+            //return from j in r.ReconcileInsert().Concat(r.ReconcileNonInsert())
+            //       where j.Record != null
+            //       select j.Record;
+        }
+        private void GenerateKey(InsertRecord ins)
+        {
+        }
+        private object[] GetKeyForDetail(IResolverItem i, T obj)
+        {
+            return String.IsNullOrEmpty(i.Group)
+                 ? this.info.GetKey(obj)
+                 : this.info.GetExtraKey(i.Group, obj);
+        }
+        private object[] GetKeyForDetail(IResolverItem i, Dictionary<string, object> dict)
+        {
+            return String.IsNullOrEmpty(i.Group)
+                 ? this.info.GetKey(dict)
+                 : this.info.GetExtraKey(i.Group, dict);
+        }
+        protected void SetActualKey(TransactionRecord t, object[] actualKey)
+        {
+        }
+        IEnumerable<TransactionRecord> IResolverItem.ResolveForMasterInsert(object[] refKey, object[] actualKey)
+        {
+            foreach (var j in this.GetJournals(refKey))
+            {
+                if (j.Record is InsertRecord)
+                {
+                    foreach (var t in this.ResolveForInsert(j))
+                    {
+                        this.SetActualKey(t, actualKey);
+                        yield return t;
+                    }
+                }
+                else
+                {
+                    foreach (var t in this.ResolveForStatic(j)) yield return t;
+                }
+            }
+        }
+        IEnumerable<TransactionRecord> IResolverItem.ResolveForMasterStatic(object[] refKey)
+        {
+            foreach (var j in this.GetJournals(refKey))
+            {
+                if (j.Record is InsertRecord)
+                {
+                    foreach (var t in this.ResolveForInsert(j)) yield return t;
+                }
+                if ((j.Record == null) || (j.Record is UpdateRecord))
+                {
+                    foreach (var t in this.ResolveForStatic(j)) yield return t;
+                }
+                if (j.Record is DeleteRecord)
+                {
+                    foreach (var t in this.ResolveForDelete(j)) yield return t;
+                }
+            }
+        }
+        IEnumerable<TransactionRecord> IResolverItem.ResolveForMasterDelete(object[] refKey)
+        {
+            foreach (var j in this.GetJournals(refKey))
+            {
+                if (j.Record is InsertRecord)
+                {
+                    continue;
+                }
+                if ((j.Record == null) || (j.Record is UpdateRecord))
+                {
+                    var del = new DeleteRecord(this.info.TableName);
+                    this.info.ForEachKey(j.Old, del.Keys.Add);
+                    var jdel = new TransactionJournal<T> { Record = del, Old = j.Old, New = null };
+                    foreach (var t in this.ResolveForDelete(jdel)) yield return t;
+                }
+                if (j.Record is DeleteRecord)
+                {
+                    foreach (var t in this.ResolveForDelete(j)) yield return t;
+                }
+            }
+        }
+        protected IEnumerable<TransactionRecord> ResolveForInsert(TransactionJournal<T> j)
+        {
+            var ins = (InsertRecord)j.Record;
+            this.GenerateKey(ins);
+            yield return ins;
+            foreach (var i in this.items)
+            {
+                var refKey = this.GetKeyForDetail(i, j.New);
+                var actualKey = this.GetKeyForDetail(i, ins.Values);
+                foreach (var t in i.ResolveForMasterInsert(refKey, actualKey))
+                {
+                    yield return t;
+                }
+            }
+        }
+        protected IEnumerable<TransactionRecord> ResolveForStatic(TransactionJournal<T> j)
+        {
+            if (j.Record != null) yield return j.Record;
+            foreach (var i in this.items)
+            {
+                var refKey = this.GetKeyForDetail(i, j.New);
+                foreach (var t in i.ResolveForMasterStatic(refKey))
+                {
+                    yield return t;
+                }
+            }
+        }
+        protected IEnumerable<TransactionRecord> ResolveForDelete(TransactionJournal<T> j)
+        {
+            foreach (var i in this.items)
+            {
+                var del = j.Record as DeleteRecord;
+                var refKey = this.GetKeyForDetail(i, j.Old);
+                foreach (var t in i.ResolveForMasterDelete(refKey))
+                {
+                    yield return t;
+                }
+                yield return del;
+            }
+        }
+        public IEnumerable<TransactionRecord> Resolve2()
         {
             var r = new Reconciler<T>(this.info, this.olds, this.news);
-            foreach (var ins in r.ReconcileInsertion())
+            foreach (var j in r.ReconcileNonDelete())
             {
-                foreach (var item in this.items)
+                if (j.Record is InsertRecord)
                 {
-                    if (String.IsNullOrEmpty(item.Group))
-                    {
-                    }
-                    else
-                    {
-                    }
+                    foreach (var t in this.ResolveForInsert(j)) yield return t;
+                }
+                else
+                {
+                    foreach (var t in this.ResolveForStatic(j)) yield return t;
                 }
             }
-            return r.ReconcileInsert().Union(r.ReconcileUpdateOrDelete());
+            foreach (var j in r.ReconcileDelete())
+            {
+                foreach (var t in this.ResolveForDelete(j)) yield return t;
+            }
+        }
+        private class DetailReconcilerForInsertingMaster
+        {
+            private ResolverItem<T> item = null;
+            private InsertRecord ins = null;
+            public DetailReconcilerForInsertingMaster(ResolverItem<T> item, InsertRecord ins)
+            {
+                this.item = @item;
+                this.ins = @ins;
+            }
+        }
+        private class DetailReconcilerForDeletingMaster
+        {
+            private ResolverItem<T> item = null;
+            private DeleteRecord del = null;
+            public DetailReconcilerForDeletingMaster(ResolverItem<T> item, DeleteRecord del)
+            {
+                this.item = @item;
+                this.del = @del;
+            }
         }
     }
 }

File UsuallyTests/TestResolve.cs

             news.Add(new OrderLine { LineId = -1, UniqueId=900, OrderId = 101, ItemId = 8008, ItemName = "Eight Thousand Eight", Quantity = 8, Unit = "Piece" });
             news.Add(new OrderLine { LineId = 9, UniqueId=-7, OrderId = 101, ItemId = 9009, ItemName = "Nine Thousand Nine", Quantity = 9, Unit = "Square Metre" });
             Assert.AreEqual(8, news.Count);
-            var changes = Resolver.Reconcile(OrderLine.Meta, olds, news);
+            var changes = Resolver.Resolve(OrderLine.Meta, olds, news);
             Assert.AreEqual(8, changes.Count());
             var e = changes.GetEnumerator();
             Assert.True(e.MoveNext());