// Adds random employee data to the database by using dataflow.
        // This method is similar to AddEmployees except that it uses batching
        // to add multiple employees to the database at a time.
        static void AddEmployeesBatched(string connectionString, int batchSize, int count)
        {
            // Create a BatchBlock<Employee> that holds several Employee objects and
            // then propagates them out as an array.
            BatchBlock <Employee> batchEmployees = new BatchBlock <Employee>(batchSize);

            // Create an ActionBlock<Employee[]> object that adds multiple
            // employee entries to the database.
            ActionBlock <Employee[]> insertEmployees = new ActionBlock <Employee[]>(a =>
            {
                DatabaseUtilities.InsertEmployees(a, connectionString, "AddEmployeesBatched");
            });

            // Link the batch block to the action block.
            batchEmployees.LinkTo(insertEmployees);

            // When the batch block completes, set the action block also to complete.
            batchEmployees.Completion.ContinueWith(obj =>
            {
                insertEmployees.Complete();
            });

            // Post several random Employee objects to the batch block.
            PostRandomEmployees(batchEmployees, count);

            // Set the batch block to the completed state and wait for
            // all insert operations to complete.
            batchEmployees.Complete();
            insertEmployees.Completion.Wait();
        }
        // Adds random employee data to the database by using dataflow.
        static void AddEmployees(string connectionString, int count)
        {
            // Create an ActionBlock<Employee> object that adds a single
            // employee entry to the database.
            ActionBlock <Employee> insertEmployee = new ActionBlock <Employee>(e =>
            {
                DatabaseUtilities.InsertEmployees(new[] { e }, connectionString, "AddEmployees");
            });

            // Post several random Employee objects to the dataflow block.
            PostRandomEmployees(insertEmployee, count);

            // Set the dataflow block to the completed state and wait for
            // all insert operations to complete.
            insertEmployee.Complete();
            insertEmployee.Completion.Wait();
        }
        public static void Run()
        {
            // Create a connection string for accessing the database.
            // The connection string refers to the temporary database location.
            string connectionString = string.Format(@"Data Source={0}", scratchDatabase);

            // Create a Stopwatch object to time database insert operations.
            Stopwatch stopwatch = new Stopwatch();

            // Start with a clean database file by copying the source database to
            // the temporary location.
            File.Copy(sourceDatabase, scratchDatabase, true);

            // Demonstrate multiple insert operations without batching.
            Console.WriteLine("Demonstrating non-batched database insert operations...");
            Console.WriteLine("Original size of Employee table: {0}.", DatabaseUtilities.GetEmployeeCount(connectionString));
            stopwatch.Start();
            AddEmployees(connectionString, insertCount);
            stopwatch.Stop();
            Console.WriteLine("New size of Employee table: {0}; elapsed insert time: {1} ms.", DatabaseUtilities.GetEmployeeCount(connectionString), stopwatch.ElapsedMilliseconds);

            Console.WriteLine();

            // Start again with a clean database file.
            File.Copy(sourceDatabase, scratchDatabase, true);

            // Demonstrate multiple insert operations, this time with batching.
            Console.WriteLine("Demonstrating batched database insert operations...");
            Console.WriteLine("Original size of Employee table: {0}.", DatabaseUtilities.GetEmployeeCount(connectionString));
            stopwatch.Restart();
            AddEmployeesBatched(connectionString, insertBatchSize, insertCount);
            stopwatch.Stop();
            Console.WriteLine("New size of Employee table: {0}; elapsed insert time: {1} ms.", DatabaseUtilities.GetEmployeeCount(connectionString), stopwatch.ElapsedMilliseconds);

            Console.WriteLine();

            // Start again with a clean database file.
            File.Copy(sourceDatabase, scratchDatabase, true);

            // Demonstrate multiple retrieval operations with error reporting.
            Console.WriteLine("Demonstrating batched join database select operations...");
            // Add a small number of employees to the database.
            AddEmployeesBatched(connectionString, insertBatchSize, 16);
            // Query for random employees.
            GetRandomEmployees(connectionString, insertBatchSize, 10);
        }
        // Displays information about several random employees to the console.
        static void GetRandomEmployees(string connectionString, int batchSize, int count)
        {
            // Create a BatchedJoinBlock<Employee, Exception> object that holds
            // both employee and exception data.
            BatchedJoinBlock <Employee, Exception> selectEmployees = new BatchedJoinBlock <Employee, Exception>(batchSize);

            // Holds the total number of exceptions that occurred.
            int totalErrors = 0;

            // Create an action block that prints employee and error information
            // to the console.
            ActionBlock <Tuple <IList <Employee>, IList <Exception> > > printEmployees =
                new ActionBlock <Tuple <IList <Employee>, IList <Exception> > >(data =>
            {
                // Print information about the employees in this batch.
                Console.WriteLine("Received a batch...");
                foreach (Employee e in data.Item1)
                {
                    Console.WriteLine("Last={0} First={1} ID={2}",
                                      e.FirstName, e.LastName, e.EmployeeID);
                }

                // Print the error count for this batch.
                Console.WriteLine("There were {0} errors in this batch...",
                                  data.Item2.Count);

                // Update total error count.
                totalErrors += data.Item2.Count;
            });

            // Link the batched join block to the action block.
            selectEmployees.LinkTo(printEmployees);

            // When the batched join block completes, set the action block also to complete.
            selectEmployees.Completion.ContinueWith(delegate { printEmployees.Complete(); });

            // Try to retrieve the ID for several random employees.
            Console.WriteLine("Selecting random entries from Employees table...");
            for (int i = 0; i < count; i++)
            {
                try
                {
                    // Create a random employee.
                    Employee e = Employee.Random();

                    // Try to retrieve the ID for the employee from the database.
                    e.EmployeeID = DatabaseUtilities.GetEmployeeID(e.LastName, e.FirstName, connectionString);

                    // Post the Employee object to the Employee target of
                    // the batched join block.
                    selectEmployees.Target1.Post(e);
                }
                catch (NullReferenceException e)
                {
                    // GetEmployeeID throws NullReferenceException when there is
                    // no such employee with the given name. When this happens,
                    // post the Exception object to the Exception target of
                    // the batched join block.
                    selectEmployees.Target2.Post(e);
                }
            }

            // Set the batched join block to the completed state and wait for
            // all retrieval operations to complete.
            selectEmployees.Complete();
            printEmployees.Completion.Wait();

            // Print the total error count.
            Console.WriteLine("Finished. There were {0} total errors.", totalErrors);
        }