Exemple #1
0
        static void Main(string[] args)
        {
            Console.BackgroundColor = ConsoleColor.White;
            Console.ForegroundColor = ConsoleColor.Black;
            Console.SetWindowSize(Console.LargestWindowWidth * 8 / 10, Console.LargestWindowHeight * 9 / 10);
            Console.WriteLine("StorageLib Get Started Application");
            Console.WriteLine("==================================");
            Console.WriteLine();
            var directoryInfo = new DirectoryInfo("GetSetartedData");

            if (directoryInfo.Exists)
            {
                Console.WriteLine("Found existing directory for data: " + directoryInfo.FullName);
            }
            else
            {
                directoryInfo.Create();
                //it is enough just to start with an empty directory. The data context will create
                //the necessary files if they are missing.
                Console.WriteLine("Created new directory for data: " + directoryInfo.FullName);
            }

            try {
                var    dc = new DC(new CsvConfig(directoryInfo.FullName, reportException: reportException));
                Parent parent0;
                Parent parent1;
                Child  child0;
                if (dc.IsNew)
                {
                    //new data context, we come here only the very first time Program runs or when the .csv gets deleted
                    parent0 = new Parent("Parent0");                   //per default, new() stores instance in Data Context
                    parent1 = new Parent("Parent1", isStoring: false); //example where new instance is not stored yet
                    parent1.Store();                                   //this can be done much later, but before application shuts down
                    child0 = new Child("Child0", parent0);             //this adds child0 automatically to parent0.Children
                    consoleWriteLine("Newly created data", parent0, parent1, child0);
                }
                else
                {
                    //access already existing data
                    parent0 = DC.Data.Parents[0]; //access without local DC reference, just through static variable DC.Data
                    parent1 = dc.Parents[1];
                    child0  = dc.Children[0];
                    consoleWriteLine("Exisiting data", parent0, parent1, child0);
                }

                //update without transaction
                child0.Update(child0.Name + " updated", parent1);
                consoleWriteLine("After simple update", parent0, parent1, child0);

                //a normal transaction
                dc.StartTransaction();
                try {
                    child0.Update("Child0", parent0);
                    //could have many more statements
                    dc.CommitTransaction();
                } catch (Exception exception) {
                    dc.RollbackTransaction();
                    reportException(exception);
                }
                consoleWriteLine("After update with transaction", parent0, parent1, child0);

                //showing that transaction rollback really works
                dc.StartTransaction();
                child0.Update(child0.Name + " updated before rollback", parent1);
                consoleWriteLine("After update, before rollback", parent0, parent1, child0);
                dc.RollbackTransaction(); //normally, a dc.CommitTransaction() would be here
                consoleWriteLine("After transaction rollback", parent0, parent1, child0);

                child0.Release(); // removing child0 from dc.Children
                consoleWriteLine("After child0.Release", parent0, parent1, child0);

                //child0 is still in parent0.Children.
                //Since parent0 is stored, not stored child0 will not show when using:
                //foreach (var child in parent0.Children) {
                // To see all children when a parent is stored, use:
                //foreach (var child in parent0.Children.GetAll()) {

                //Similarly, parent0.Children.Count will only count stored children when parent is stored.
                //Use, parent0.Children.Count will only count stored children when parent is stored.


                child0.Store();
                consoleWriteLine("After child0.Store", parent0, parent1, child0);
            } catch (Exception exception) {
                Console.WriteLine("Exception occured: " + exception);
            } finally {
                //It is important that your application calls DC.DisposeData() when it shuts down. This
                //flushes any not yet written data to that CSV files.
                DC.DisposeData();
            }
        }
Exemple #2
0
    #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor.
        public CollectionTestBase()
        {
    #pragma warning restore CS8618
      #pragma warning disable IDE0045 // Convert to conditional expression
            if (typeof(TChild) == typeof(ListChild))
            {
                collectionType = CollectionTypeEnum.list;
            }
            else if (typeof(TChild) == typeof(DictionaryChild))
            {
                collectionType = CollectionTypeEnum.dictionary;
            }
            else if (typeof(TChild) == typeof(SortedListChild))
            {
                collectionType = CollectionTypeEnum.sortedList;
            }
            else
            {
                throw new NotSupportedException();
            }
      #pragma warning restore IDE0045

            var directoryInfo = new DirectoryInfo("TestCsv");
            if (directoryInfo.Exists)
            {
                directoryInfo.Delete(recursive: true);
                directoryInfo.Refresh();
            }

            directoryInfo.Create();
            //initVariables();


            try {
                csvConfig         = new CsvConfig(directoryInfo.FullName, reportException: reportException);
                bakCsvFileSwapper = new BakCsvFileSwapper(csvConfig);
                DC.Trace          = dcTrace;
                dc = new DC(csvConfig);
                initialiseDcParents();
                assertData("");

                // Create
                // ======
                // Create parent
                // -------------
                traceHeader("create not stored parent");
                dc.StartTransaction();
                var parent0_ = createParent("p_Temp", isStoring: false);
                dc.RollbackTransaction();
                assertData("");

                dc.StartTransaction();
                parent0_ = createParent("p_", isStoring: false);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("");


                traceHeader("create stored parent");
                dc.StartTransaction();
                parent0 = createParent("p0Temp", isStoring: true);
                dc.RollbackTransaction();
                assertData("");

                dc.StartTransaction();
                parent0 = createParent("p0", isStoring: true);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|");

                // Create child with Parent==parent0, ParentN==null, ParentR==parentR0, ParentNR=parentNR0
                // ---------------------------------------------------------------------------------------
                traceHeader("create not stored child with not stored parent");
                var parentR0_  = createParentR("pR0_", isStoring: false);
                var parentNR0_ = createParentNR("pRN0_", isStoring: false);
                dc.StartTransaction();
                var child0_ = createChild("c0_Temp", parent0_, null, parentR0_, parentNR0_, isStoring: false);
                dc.RollbackTransaction();
                Assert.AreEqual(0, parent0_.CountAllChildren);
                Assert.AreEqual(0, parentR0_.CountAllChildren);
                Assert.AreEqual(0, parentNR0_.CountAllChildren);
                assertData("p0|");

                dc.StartTransaction();
                child0_ = createChild("c0_", parent0_, null, parentR0_, parentNR0_, isStoring: false);
                dc.CommitTransaction();
                Assert.AreEqual("c0_", child0_.Text);
                Assert.AreEqual(child0_, parent0_.AllChildrenFirst);
                Assert.AreEqual(child0_, parentR0_.AllChildrenFirst);
                Assert.AreEqual(child0_, parentNR0_.AllChildrenFirst);
                assertDataDisposeDCRecreateDCassertData("p0|");


                traceHeader("create not stored child with stored parent");
                parentR0  = createParentR("pR0", isStoring: true);
                parentNR0 = createParentNR("pNR0", isStoring: true);
                dc.StartTransaction();
                var child0__ = createChild("c0__Temp", parent0, null, parentR0, parentNR0, isStoring: false);
                dc.RollbackTransaction();
                Assert.AreEqual(0, parent0.CountAllChildren);
                Assert.AreEqual(0, parentR0.CountAllChildren);
                Assert.AreEqual(0, parentNR0.CountAllChildren);
                assertData("p0|pR0|pNR0|");

                dc.StartTransaction();
                child0__ = createChild("c0__", parent0, null, parentR0, parentNR0, isStoring: false);
                dc.CommitTransaction();
                Assert.AreEqual("c0__", child0__.Text);
                Assert.AreEqual(parent0, child0__.Parent);
                Assert.AreEqual(parentR0, child0__.ParentR);
                Assert.AreEqual(parentNR0, child0__.ParentNR);
                assertDataDisposeDCRecreateDCassertData("p0:c0__|pR0:c0__|pNR0:c0__|", "p0|pR0|pNR0|");


                traceHeader("create stored child with stored parent");
                dc.StartTransaction();
                child0 = createChild("c0Temp", parent0, null, parentR0, parentNR0, isStoring: true);
                dc.RollbackTransaction();
                assertData("p0|pR0|pNR0|");

                dc.StartTransaction();
                child0 = createChild("c0", parent0, null, parentR0, parentNR0, isStoring: true);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0:;c0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");

                //Fail to create stored child with not stored parent
                traceHeader("fail to create stored child with not stored parents");
                try {
                    var parent0___   = createParent("p0___", isStoring: false);
                    var parentN0___  = createParentN("pN0___", isStoring: false);
                    var parentR0___  = createParentR("pR0___", isStoring: false);
                    var parentNR0___ = createParentNR("pRN0___", isStoring: false);
                    var _            = createChild("failed child", parent0___, parentN0___, parentR0___, parentNR0___);
                    Assert.Fail();
                } catch {
                }
                //Todo: Ideally, an exception during create, store or remove should not change any data. Is additional code needed undoing
                //any potentially changed data ?
                //Assert.AreEqual(0, parent0_.CountAllChildren);
                assertDataDisposeDCRecreateDCassertData("p0:;c0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");

                // Update
                // ======
                // Update child.Parent to parent1, child.ParentN to parentN0
                // ---------------------------------------------------------

                traceHeader("not stored child: update with not stored parents");
                var parent1_  = createParent("p1_", isStoring: false);
                var parentN0_ = createParentN("pN0___", isStoring: false);
                //var parentR1_ = createParent("pR1_", isStoring: false);
                //var parentNR0_ = createParentN("pNR0_", isStoring: false);
                dc.StartTransaction();
                child0_.Update("c0_.1Temp", (TP)parent1_, (TPN)parentN0_);
                dc.RollbackTransaction();
                Assert.AreEqual(child0_, parent0_.AllChildrenFirst);
                Assert.AreEqual(0, parentN0_.CountAllChildren);
                Assert.AreEqual(0, parent1_.CountAllChildren);
                assertData("p0:;c0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.1", (TP)parent1_, (TPN)parentN0_);
                dc.CommitTransaction();
                Assert.AreEqual(0, parent0_.CountAllChildren);
                Assert.AreEqual(child0_, parentN0_.AllChildrenFirst);
                Assert.AreEqual(child0_, parent1_.AllChildrenFirst);
                assertDataDisposeDCRecreateDCassertData("p0:;c0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");


                traceHeader("not stored child: update with stored parents");
                parent1  = createParent("p1", isStoring: true);
                parentN0 = createParentN("pN0", isStoring: true);
                dc.StartTransaction();
                child0__.Update("c0__.1Temp", (TP)parent1, (TPN)parentN0);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__", child0__.Text);
                Assert.AreEqual(parent0.Text, child0__.Parent.Text);
                Assert.IsNull(child0__.ParentN);
                assertData("p0:;c0|p1|pN0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.1", (TP)parent1, (TPN)parentN0);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.1", child0__.Text);
                Assert.AreEqual(parent1, child0__.Parent);
                Assert.AreEqual(parentN0, child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0:;c0|p1:c0__.1|pN0:c0__.1|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|",
                    "p0:;c0|p1|pN0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");


                traceHeader("stored child: update with stored parents");
                dc.StartTransaction();
                child0.Update("c0.1Temp", (TP)parent1, (TPN)parentN0);
                dc.RollbackTransaction();
                assertData("p0:;c0|p1|pN0|pR0:;c0|pNR0:;c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.1", (TP)parent1, (TPN)parentN0);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.1|pN0:;c0.1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");

                // Update child.ParentN to parentN1
                // --------------------------------

                traceHeader("not stored child: update not stored ParentN");
                var parentN1_ = createParentN("pN1_", isStoring: false);
                dc.StartTransaction();
                child0_.Update("c0_.2Temp", (TP)parent1_, (TPN)parentN1_);
                dc.RollbackTransaction();
                Assert.AreEqual("c0_.1", child0_.Text);
                Assert.AreEqual(child0_, parent1_.AllChildrenFirst);
                Assert.AreEqual(child0_, parentN0_.AllChildrenFirst);
                Assert.AreEqual(0, parentN1_.CountAllChildren);
                assertData("p0|p1:;c0.1|pN0:;c0.1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.2", (TP)parent1_, (TPN)parentN1_);
                dc.CommitTransaction();
                Assert.AreEqual("c0_.2", child0_.Text);
                Assert.AreEqual(0, parent0_.CountAllChildren);
                Assert.AreEqual(0, parentN0_.CountAllChildren);
                Assert.AreEqual(child0_, parent1_.AllChildrenFirst);
                Assert.AreEqual(child0_, parentN1_.AllChildrenFirst);
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.1|pN0:;c0.1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");


                traceHeader("not stored child: update stored ParentN");
                parentN1 = createParentN("pN1", isStoring: true);
                dc.StartTransaction();
                child0__.Update("c0__.2Temp", (TP)parent1, (TPN)parentN1);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__.1", child0__.Text);
                Assert.AreEqual(parent1.Text, child0__.Parent.Text);
                Assert.AreEqual(parentN0.Text, child0__.ParentN !.Text);
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.1|pN0:;c0.1|pN1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.2", (TP)parent1, (TPN)parentN1);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.2", child0__.Text);
                Assert.AreEqual(parent1, child0__.Parent);
                Assert.AreEqual(parentN1, child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p1:c0__.2;,c0.1|pN0:;c0.1|pN1:c0__.2|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|",
                    "p0|p1:;c0.1|pN0:;c0.1|pN1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");


                traceHeader("stored child: update stored ParentN");
                dc.StartTransaction();
                child0.Update("c0.2Temp", (TP)parent1, (TPN)parentN1);
                dc.RollbackTransaction();
                assertData("p0|p1:;c0.1|pN0:;c0.1|pN1|pR0:;c0.1|pNR0:;c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.2", (TP)parent1, (TPN)parentN1);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");

                // Update child.ParentN to null
                // ----------------------------

                traceHeader("not stored child: update not stored ParentN to null");
                dc.StartTransaction();
                child0_.Update("c0_.3Temp", (TP)parent1_, null);
                dc.RollbackTransaction();
                Assert.AreEqual("c0_.2", child0_.Text);
                Assert.AreEqual(child0_, parent1_.AllChildrenFirst);
                Assert.AreEqual(child0_, parentN1_.AllChildrenFirst);
                assertData("p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.3", (TP)parent1_, null);
                dc.CommitTransaction();
                Assert.AreEqual("c0_.3", child0_.Text);
                Assert.AreEqual(0, parentN0_.CountAllChildren);
                Assert.AreEqual(child0_, parent1_.AllChildrenFirst);
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");


                traceHeader("not stored child: update stored ParentN to null");
                dc.StartTransaction();
                child0__.Update("c0__.3Temp", (TP)parent1, null);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__.2", child0__.Text);
                Assert.AreEqual(parent1.Text, child0__.Parent.Text);
                Assert.AreEqual(parentN1.Text, child0__.ParentN !.Text);
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.3", (TP)parent1, null);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.3", child0__.Text);
                Assert.AreEqual(parent1, child0__.Parent);
                Assert.IsNull(child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p1:c0__.3;,c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|",
                    "p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");


                traceHeader("stored child: update stored ParentN to null");
                dc.StartTransaction();
                child0.Update("c0.3Temp", (TP)parent1, null);
                dc.RollbackTransaction();
                assertData("p0|p1:;c0.2|pN0|pN1:;c0.2|pR0:;c0.2|pNR0:;c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.3", (TP)parent1, null);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");
                bakCsvFileSwapper.DeleteBakFiles();


                // Release
                // =======
                // Fail to release parent with stored child
                // ----------------------------------------
                traceHeader("stored child: fail to release parent");
                try {
                    parent1.Release();
                    Assert.Fail();
                } catch {
                }
                assertData("p0|p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");

                //Release child
                //-------------
                traceHeader("stored child: release child");
                child1 = createChild("c1", parent1, parentN1, parentR0, parentNR0, isStoring: true);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p1:;c0.3,c1|pN0|pN1:;c1|pR0:;c0.3,c1|pNR0:;c0.3,c1|c0.3:p1,pR0,pNR0|c1:p1,pN1,pR0,pNR0|");
                dc.StartTransaction();
                child1.Release();
                dc.RollbackTransaction();
                assertData("p0|p1:;c0.3,c1|pN0|pN1:;c1|pR0:;c0.3,c1|pNR0:;c0.3,c1|c0.3:p1,pR0,pNR0|c1:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child1.Release();
                dc.CommitTransaction();
                child1 = null;
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p1:;c0.3,c1|pN0|pN1:c1|pR0:;c0.3,c1|pNR0:;c0.3,c1|c0.3:p1,pR0,pNR0|",
                    "p0|p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");

                //Release parent
                //--------------
                traceHeader("stored parent: release parent");
                dc.StartTransaction();
                parent0.Release();
                dc.RollbackTransaction();
                assertData("p0|p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");

                dc.StartTransaction();
                parent0.Release();
                dc.CommitTransaction();
                parent0 = null;
                assertDataDisposeDCRecreateDCassertData("p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");


                //test .bak file with complicated data structure
                // =============================================
                traceHeader("create, update and release complicated data structure");
                var parent2   = createParent("p2", isStoring: true);
                var parent3   = createParent("p3", isStoring: true);
                var parentN2  = createParentN("pN2", isStoring: true);
                var parentR2  = createParentR("pR2", isStoring: true);
                var parentNR2 = createParentNR("pRN2", isStoring: true);
                var child2    = createChild("c2", parent2, null, parentR2, parentNR2, isStoring: true);
                child2.Update("c2.0", (TP)parent2, null);
                parent2.Update("p2.0");
                parent3.Update("p3.0");
                parentN2.Update("pN2.0");
                parentR2.Update("pR2.0");
                parentNR2.Update("pRN2.0");
                child2.Update("c2.0", (TP)parent3, (TPN)parentN2);
                child2.Release();
                parent2.Release();
                parent3.Release();
                parentN2.Release();
                parentR2.Release();
                parentNR2.Release();
                assertDataDisposeDCRecreateDCassertData("p1:;c0.3|pN0|pN1|pR0:;c0.3|pNR0:;c0.3|c0.3:p1,pR0,pNR0|");
            } finally {
                DC.DisposeData();
            }
        }
        public void TestSingleChild()
        {
            /* Test explanation:
             * items can be stored or not stored, legal combinations:
             * child variable name | parent variable name
             * --------------------+--------------------
             * child_: not stored  | parent_: not stored
             * child__: not stored | parent__:stored
             * child: stored       | parent: stored
             * child___: stored    | parent___: not stored    this combination is illegal. Test: is an exception thrown ?
             *
             * for each combination above some test code is written
             *
             * parent variabls names:
             * Parent: the parent property in the child is not nullable
             * ParentN: the parent property in the child is nullable
             * ParentR: the parent property in the child is readonly
             * ParentNR: the parent property in the child is nullable and readonly
             *
             * each activity like create, update or delete is done first in a rooled back transaction and none of the data should
             * be changed, then the same activity is executed in a commited transactions and the data should change accordingly.
             *
             * After a transaction has been committed, the datacontext gets disposed, opened again and verified, that the data
             * is still the same. This is done twice, first using the .bak files, then the .csv files. There is one exception:
             * stored parents might have some not stored children. In the new data context, those parents have no longer those
             * children.
             *
             * For convenience, the variables parent0, parentN1, child1, etc. contain always the data from the latest data context. They
             * get updated, each time assertDataDisposeDCRecreateDCassertData() gets called.
             *
             * child_, child__, parent_ and parent___ are not stored in the data context. They do not get updated by
             * assertDataDisposeDCRecreateDCassertData() and can therefore contain children or parents stored in previous data
             * contexts.
             */
            var directoryInfo = new DirectoryInfo("TestCsv");

            if (directoryInfo.Exists)
            {
                directoryInfo.Delete(recursive: true);
                directoryInfo.Refresh();
            }

            directoryInfo.Create();


            try {
                csvConfig         = new CsvConfig(directoryInfo.FullName, reportException: reportException);
                bakCsvFileSwapper = new BakCsvFileSwapper(csvConfig);
                DC.Trace          = dcTrace;
                dc = new DC(csvConfig);
                assertData("");

                // Create
                // ======
                // Create parent
                // -------------
                traceHeader("create not stored parent");
                dc.StartTransaction();
                var parent0_ = new SingleChildParent("p_Temp", isStoring: false);
                dc.RollbackTransaction();
                assertData("");

                dc.StartTransaction();
                parent0_ = new SingleChildParent("p_", isStoring: false);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("");


                traceHeader("create stored parent");
                dc.StartTransaction();
                parent0 = new SingleChildParent("p0Temp", isStoring: true);
                dc.RollbackTransaction();
                assertData("");

                dc.StartTransaction();
                parent0 = new SingleChildParent("p0", isStoring: true);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|");

                // Create child with Parent==parent0, ParentN==null, ParentR==parentR0, ParentNR=parentNR0
                // ---------------------------------------------------------------------------------------
                traceHeader("create not stored child with not stored parent");
                var parentR0_  = new SingleChildParentR("pR0_", isStoring: false);
                var parentNR0_ = new SingleChildParentNR("pRN0_", isStoring: false);
                dc.StartTransaction();
                var child0_ = new SingleChildChild("c0_Temp", parent0_, null, parentR0_, parentNR0_, isStoring: false);
                dc.RollbackTransaction();
                Assert.IsNull(parent0_.Child);
                Assert.IsNull(parentR0_.Child);
                Assert.IsNull(parentNR0_.Child);
                assertData("p0|");

                dc.StartTransaction();
                child0_ = new SingleChildChild("c0_", parent0_, null, parentR0_, parentNR0_, isStoring: false);
                dc.CommitTransaction();
                Assert.AreEqual("c0_", child0_.Text);
                Assert.AreEqual(child0_, parent0_.Child);
                Assert.AreEqual(child0_, parentR0_.Child);
                Assert.AreEqual(child0_, parentNR0_.Child);
                assertDataDisposeDCRecreateDCassertData("p0|");


                traceHeader("create not stored child with stored parent");
                parent0__   = new SingleChildParent("p0__", isStoring: true);
                parentR0__  = new SingleChildParentR("pR0__", isStoring: true);
                parentNR0__ = new SingleChildParentNR("pNR0__", isStoring: true);
                dc.StartTransaction();
                var child0__ = new SingleChildChild("c0__Temp", parent0__, null, parentR0__, parentNR0__, isStoring: false);
                dc.RollbackTransaction();
                Assert.IsNull(parent0__.Child);
                Assert.IsNull(parentR0__.Child);
                Assert.IsNull(parentNR0__.Child);
                assertData("p0|p0__|pR0__|pNR0__|");

                dc.StartTransaction();
                child0__ = new SingleChildChild("c0__", parent0__, null, parentR0__, parentNR0__, isStoring: false);
                dc.CommitTransaction();
                Assert.AreEqual("c0__", child0__.Text);
                Assert.AreEqual(parent0__, child0__.Parent);
                Assert.AreEqual(parentR0__, child0__.ParentR);
                Assert.AreEqual(parentNR0__, child0__.ParentNR);
                assertDataDisposeDCRecreateDCassertData("p0|p0__:c0__|pR0__:c0__|pNR0__:c0__|", "p0|p0__|pR0__|pNR0__|");


                traceHeader("create stored child with stored parent");
                parentR0  = new SingleChildParentR("pR0", isStoring: true);
                parentNR0 = new SingleChildParentNR("pNR0", isStoring: true);
                dc.StartTransaction();
                child0 = new SingleChildChild("c0Temp", parent0, null, parentR0, parentNR0, isStoring: true);
                dc.RollbackTransaction();
                assertData("p0|p0__|pR0__|pR0|pNR0__|pNR0|");

                dc.StartTransaction();
                child0 = new SingleChildChild("c0", parent0, null, parentR0, parentNR0, isStoring: true);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0:c0|p0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");

                //Fail to create stored child with not stored parent
                traceHeader("fail to create stored child with not stored parents");
                try {
                    var parent0___   = new SingleChildParent("p0___", isStoring: false);
                    var parentN0___  = new SingleChildParentN("pN0___", isStoring: false);
                    var parentR0___  = new SingleChildParentR("pR0___", isStoring: false);
                    var parentNR0___ = new SingleChildParentNR("pRN0___", isStoring: false);
                    var child___     = new SingleChildChild("failed child", parent0___, parentN0___, parentR0___, parentNR0___);
                    Assert.Fail();
                } catch {
                }
                //Todo: Ideally, an exception during create, store or remove should not change any data. Is additional code needed undoing
                //any potentially changed data ?
                //Assert.AreEqual(0, parent0_.Children.Count);
                assertDataDisposeDCRecreateDCassertData("p0:c0|p0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");

                // Update
                // ======
                // Update child.Parent to parent1, child.ParentN to parent0N
                // ---------------------------------------------------------

                traceHeader("not stored child: update with not stored parents");
                var parent1_  = new SingleChildParent("p1_", isStoring: false);
                var parentN0_ = new SingleChildParentN("pN0___", isStoring: false);
                dc.StartTransaction();
                child0_.Update("c0_.1Temp", parent1_, parentN0_);
                dc.RollbackTransaction();
                Assert.AreEqual(child0_, parent0_.Child);
                Assert.IsNull(parentN0_.Child);
                Assert.IsNull(parent1_.Child);
                assertData("p0:c0|p0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.1", parent1_, parentN0_);
                dc.CommitTransaction();
                Assert.IsNull(parent0_.Child);
                Assert.AreEqual(child0_, parentN0_.Child);
                Assert.AreEqual(child0_, parent1_.Child);
                assertDataDisposeDCRecreateDCassertData("p0:c0|p0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");


                traceHeader("not stored child: update with stored parents");
                parent1__  = new SingleChildParent("p1__", isStoring: true);
                parentN0__ = new SingleChildParentN("pN0__", isStoring: true);
                dc.StartTransaction();
                child0__.Update("c0__.1Temp", parent1__, parentN0__);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__", child0__.Text);
                Assert.AreEqual(parent0__.Text, child0__.Parent.Text);
                Assert.IsNull(child0__.ParentN);
                assertData("p0:c0|p0__|p1__|pN0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.1", parent1__, parentN0__);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.1", child0__.Text);
                Assert.AreEqual(parent1__, child0__.Parent);
                Assert.AreEqual(parentN0__, child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0:c0|p0__|p1__:c0__.1|pN0__:c0__.1|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|",
                    "p0:c0|p0__|p1__|pN0__|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");


                traceHeader("stored child: update with stored parents");
                parent1  = new SingleChildParent("p1", isStoring: true);
                parentN0 = new SingleChildParentN("pN0", isStoring: true);
                dc.StartTransaction();
                child0.Update("c0.1Temp", parent1, parentN0);
                dc.RollbackTransaction();
                assertData("p0:c0|p0__|p1__|p1|pN0__|pN0|pR0__|pR0:c0|pNR0__|pNR0:c0|c0:p0,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.1", parent1, parentN0);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData("p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");

                // Update child.ParentN to parent1N
                // --------------------------------

                traceHeader("not stored child: update not stored ParentN");
                var parentN1_ = new SingleChildParentN("pN1_", isStoring: false);
                dc.StartTransaction();
                child0_.Update("c0_.2Temp", parent1_, parentN1_);
                dc.RollbackTransaction();
                Assert.AreEqual("c0_.1", child0_.Text);
                Assert.AreEqual(child0_, parent1_.Child);
                Assert.AreEqual(child0_, parentN0_.Child);
                Assert.IsNull(parentN1_.Child);
                assertData("p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.2", parent1_, parentN1_);
                dc.CommitTransaction();
                Assert.AreEqual("c0_.2", child0_.Text);
                Assert.IsNull(parent0_.Child);
                Assert.IsNull(parentN0_.Child);
                Assert.AreEqual(child0_, parent1_.Child);
                Assert.AreEqual(child0_, parentN1_.Child);
                assertDataDisposeDCRecreateDCassertData("p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");


                traceHeader("not stored child: update stored ParentN");
                parentN1__ = new SingleChildParentN("pN1__", isStoring: true);
                dc.StartTransaction();
                child0__.Update("c0__.2Temp", parent1__, parentN1__);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__.1", child0__.Text);
                Assert.AreEqual(parent1__.Text, child0__.Parent.Text);
                Assert.AreEqual(parentN0__.Text, child0__.ParentN !.Text);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pN1__|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.2", parent1__, parentN1__);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.2", child0__.Text);
                Assert.AreEqual(parent1__, child0__.Parent);
                Assert.AreEqual(parentN1__, child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__:c0__.2|p1:c0.1|pN0__|pN0:c0.1|pN1__:c0__.2|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|",
                    "p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pN1__|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");


                traceHeader("stored child: update stored ParentN");
                parentN1 = new SingleChildParentN("pN1", isStoring: true);
                dc.StartTransaction();
                child0.Update("c0.2Temp", parent1, parentN1);
                dc.RollbackTransaction();
                assertData("p0|p0__|p1__|p1:c0.1|pN0__|pN0:c0.1|pN1__|pN1|pR0__|pR0:c0.1|pNR0__|pNR0:c0.1|c0.1:p1,pN0,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.2", parent1, parentN1);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");

                // Update child.ParentN to null
                // ----------------------------

                traceHeader("not stored child: update not stored ParentN to null");
                dc.StartTransaction();
                child0_.Update("c0_.3Temp", parent1_, null);
                dc.RollbackTransaction();
                Assert.AreEqual("c0_.2", child0_.Text);
                Assert.AreEqual(child0_, parent1_.Child);
                Assert.AreEqual(child0_, parentN1_.Child);
                assertData("p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0_.Update("c0_.3", parent1_, null);
                dc.CommitTransaction();
                Assert.AreEqual("c0_.3", child0_.Text);
                Assert.IsNull(parentN0_.Child);
                Assert.AreEqual(child0_, parent1_.Child);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");


                traceHeader("not stored child: update stored ParentN to null");
                dc.StartTransaction();
                child0__.Update("c0__.3Temp", parent1__, null);
                dc.RollbackTransaction();
                Assert.AreEqual("c0__.2", child0__.Text);
                Assert.AreEqual(parent1__.Text, child0__.Parent.Text);
                Assert.AreEqual(parentN1__.Text, child0__.ParentN !.Text);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0__.Update("c0__.3", parent1__, null);
                dc.CommitTransaction();
                Assert.AreEqual("c0__.3", child0__.Text);
                Assert.AreEqual(parent1__, child0__.Parent);
                Assert.IsNull(child0__.ParentN);
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__:c0__.3|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|",
                    "p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");


                traceHeader("stored child: update stored ParentN to null");
                dc.StartTransaction();
                child0.Update("c0.3Temp", parent1, null);
                dc.RollbackTransaction();
                assertData("p0|p0__|p1__|p1:c0.2|pN0__|pN0|pN1__|pN1:c0.2|pR0__|pR0:c0.2|pNR0__|pNR0:c0.2|c0.2:p1,pN1,pR0,pNR0|");

                dc.StartTransaction();
                child0.Update("c0.3", parent1, null);
                dc.CommitTransaction();
                assertDataDisposeDCRecreateDCassertData(
                    "p0|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1|pR0__|pR0:c0.3|pNR0__|pNR0:c0.3|c0.3:p1,pR0,pNR0|");


                // Release
                // =======
                // Fail to release parent with stored child
                // ----------------------------------------
                traceHeader("stored child: fail to release parent");
                try {
                    parent1.Release();
                    Assert.Fail();
                } catch {
                }
                assertData("p0|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1|pR0__|pR0:c0.3|pNR0__|pNR0:c0.3|c0.3:p1,pR0,pNR0|");
                bakCsvFileSwapper.DeleteBakFiles();

                //Release child
                //-------------
                traceHeader("stored child: release child");
                parentR1  = new SingleChildParentR("pR1", isStoring: true);
                parentNR1 = new SingleChildParentNR("pNR1", isStoring: true);
                child1    = new SingleChildChild("c1", parent0, parentN1, parentR1, parentNR1, isStoring: true);
                assertDataDisposeDCRecreateDCassertData(
                    "p0:c1|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1:c1|pR0__|pR0:c0.3|pR1:c1|pNR0__|pNR0:c0.3|pNR1:c1|c0.3:p1,pR0,pNR0|c1:p0,pN1,pR1,pNR1|");
                dc.StartTransaction();
                child1.Release();
                dc.RollbackTransaction();
                assertData("p0:c1|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1:c1|pR0__|pR0:c0.3|pR1:c1|pNR0__|pNR0:c0.3|pNR1:c1|c0.3:p1,pR0,pNR0|c1:p0,pN1,pR1,pNR1|");

                dc.StartTransaction();
                child1.Release();
                dc.CommitTransaction();
                child1 = null;
                assertDataDisposeDCRecreateDCassertData(
                    "p0:c1|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1:c1|pR0__|pR0:c0.3|pR1:c1|pNR0__|pNR0:c0.3|pNR1:c1|c0.3:p1,pR0,pNR0|",
                    "p0" + "|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1" + "|pR0__|pR0:c0.3|pR1" + "|pNR0__|pNR0:c0.3|pNR1" + "|c0.3:p1,pR0,pNR0|");

                //Release parent
                //--------------
                traceHeader("stored parent: release parent");
                dc.StartTransaction();
                parent0.Release();
                dc.RollbackTransaction();
                assertData("p0" + "|p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1" + "|pR0__|pR0:c0.3|pR1" + "|pNR0__|pNR0:c0.3|pNR1" + "|c0.3:p1,pR0,pNR0|");

                dc.StartTransaction();
                parent0.Release();
                dc.CommitTransaction();
                parent0 = null;
                assertDataDisposeDCRecreateDCassertData(
                    "p0__|p1__|p1:c0.3|pN0__|pN0|pN1__|pN1|pR0__|pR0:c0.3|pR1|pNR0__|pNR0:c0.3|pNR1|c0.3:p1,pR0,pNR0|");
            } finally {
                DC.DisposeData();
            }
        }