public async Task TestPersonInsertion3()
        {
            string connStr = CreatePeopleTable(3);

            var f = new PeopleFlow(DataflowOptions.Default);

            //will take effect
            TypeAccessorConfig.RegisterMapping <Person, string>(p => p.Name, new DBColumnMapping("PersonTarget", "NameCol", "N/A2", ColumnMappingOption.Overwrite));
            //will take effect
            TypeAccessorConfig.RegisterMapping <Person, int?>(p => p.Age, new DBColumnMapping("PersonTarget", "AgeCol", -2));

            var dbInserter = new DbBulkInserter <Person>(connStr, "dbo.People", DataflowOptions.Default, "PersonTarget");

            f.LinkTo(dbInserter);

            f.Post("{Name: 'aaron', Age: 20}");
            f.Post("{Name: 'bob', Age: 30}");
            f.Post("{Age: 80}");      //Name will be default value: "N/A"
            f.Post("{Name: 'neo' }"); // Age will be default value: -2
            await f.SignalAndWaitForCompletionAsync();

            await dbInserter.CompletionTask;

            using (var conn = LocalDB.GetLocalDB("ExternalMappingTest3"))
            {
                Assert.AreEqual(4, conn.ExecuteScalar <int>("select count(*) from dbo.People"));
                Assert.AreEqual(1, conn.ExecuteScalar <int>("select count(*) from dbo.People where NameCol = 'N/A2'"));
                Assert.AreEqual(0, conn.ExecuteScalar <int>("select count(*) from dbo.People where NameCol = 'N/A'"));
                Assert.AreEqual(1, conn.ExecuteScalar <int>("select count(*) from dbo.People where AgeCol = -2"));
            }
        }
        public async Task TestPersonInsertion3()
        {
            string connStr = CreatePeopleTable(3);

            var f = new PeopleFlow(DataflowOptions.Default);

            //will take effect
            TypeAccessorConfig.RegisterMapping<Person, string>(p => p.Name, new DBColumnMapping("PersonTarget", "NameCol", "N/A2", ColumnMappingOption.Overwrite));
            //will take effect
            TypeAccessorConfig.RegisterMapping<Person, int?>(p => p.Age, new DBColumnMapping("PersonTarget", "AgeCol", -2));

            var dbInserter = new DbBulkInserter<Person>(connStr, "dbo.People", DataflowOptions.Default, "PersonTarget");
            f.LinkTo(dbInserter);

            f.Post("{Name: 'aaron', Age: 20}");
            f.Post("{Name: 'bob', Age: 30}");
            f.Post("{Age: 80}"); //Name will be default value: "N/A"
            f.Post("{Name: 'neo' }"); // Age will be default value: -2
            await f.SignalAndWaitForCompletionAsync();
            await dbInserter.CompletionTask;

            using (var conn = LocalDB.GetLocalDB("ExternalMappingTest3"))
            {
                Assert.AreEqual(4, conn.ExecuteScalar<int>("select count(*) from dbo.People"));
                Assert.AreEqual(1, conn.ExecuteScalar<int>("select count(*) from dbo.People where NameCol = 'N/A2'"));
                Assert.AreEqual(0, conn.ExecuteScalar<int>("select count(*) from dbo.People where NameCol = 'N/A'"));
                Assert.AreEqual(1, conn.ExecuteScalar<int>("select count(*) from dbo.People where AgeCol = -2"));
            }
        }
        public static async Task BulkInserterDemo()
        {
            string connStr;

            //initialize table
            using (var conn = LocalDB.GetLocalDB("BulkInserterDemo"))
            {
                var cmd = new SqlCommand(@"
                IF OBJECT_id('dbo.People', 'U') IS NOT NULL
                    DROP TABLE dbo.People;
                
                CREATE TABLE dbo.People
                (
                    Id INT IDENTITY(1,1) NOT NULL,
                    NameCol nvarchar(50) NOT NULL,
                    AgeCol INT           NOT NULL
                )
                ", conn);
                cmd.ExecuteNonQuery();
                connStr = conn.ConnectionString;
            }

            var f = new PeopleFlow(DataflowOptions.Default);
            var dbInserter = new DbBulkInserter<Person>(connStr, "dbo.People", DataflowOptions.Default, "PersonTarget");
            f.LinkTo(dbInserter);

            f.Post("{Name: 'aaron', Age: 20}");
            f.Post("{Name: 'bob', Age: 30}");
            f.Post("{Age: 80}"); //Name will be default value: "N/A"
            f.Post("{Name: 'neo' }"); // Age will be default value: -1
            await f.SignalAndWaitForCompletionAsync();
            await dbInserter.CompletionTask;
        }
        public static async Task RecorderDemo()
        {
            var f = new PeopleFlow(DataflowOptions.Default);
            var sayHello = new ActionBlock<Person>(p => Console.WriteLine("Hello, I am {0}, {1}", p.Name, p.Age)).ToDataflow(name: "sayHello");
            f.LinkTo(sayHello, p => p.Age > 0);
            f.LinkLeftToNull(); //object flowing here will be recorded by GarbageRecorder
            
            f.Post("{Name: 'aaron', Age: 20}");
            f.Post("{Name: 'bob', Age: 30}");
            f.Post("{Name: 'carmen', Age: 80}");
            f.Post("{Name: 'neo', Age: -1}");
            await f.SignalAndWaitForCompletionAsync();
            await sayHello.CompletionTask;

            Console.WriteLine("Total people count: " + f.PeopleRecorder[typeof(Person)]);
            Console.WriteLine(f.PeopleRecorder.DumpStatistics());
            Console.WriteLine(f.GarbageRecorder.DumpStatistics());
        }