static void LinqInnerJoin()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var innerJoin =
                from p in MyProduct.GetProducts()
                join c in MyCategory.GetCategories() on p.CategoryId equals c.CategoryId
                select new { p.ProductName, c.CategoryName };

            // Fluent expression equivalent.
            // var innerJoin = MyProduct.GetProducts().Join(MyCategory.GetCategories(), p => p.CategoryId, c => c.CategoryId, (p, c) => new { p.ProductName, c.CategoryName });

            Console.WriteLine("Inner join:");
            foreach (var v in innerJoin)
            {
                Console.WriteLine(v);
            }
        }
        static void LinqCrossJoin()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var crossJoin =
                from p in MyProduct.GetProducts()
                from c in MyCategory.GetCategories()
                select new { p.ProductName, c.CategoryName };

            // Fluent expression equivalent.
            // var crossJoin = MyProduct.GetProducts().SelectMany(p => MyCategory.GetCategories().Select(c => new { p.ProductName, c.CategoryName }));

            Console.WriteLine("Cross join:");
            foreach (var v in crossJoin)
            {
                Console.WriteLine(v);
            }
        }
        static void LinqGroupJoin2()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var groupJoin =
                from c in MyCategory.GetCategories()
                join p in MyProduct.GetProducts() on c.CategoryId equals p.CategoryId into pc
                from prod in pc
                select new { c.CategoryName, prod.ProductName };

            // Fluent expression equivalent.
            // var groupJoin = MyCategory.GetCategories().GroupJoin(MyProduct.GetProducts(), c => c.CategoryId, p => p.CategoryId, (c, ps) => new { c.CategoryName, Products = ps }).SelectMany(tmp => tmp.Products, (tmp, prod) => new { tmp.CategoryName, prod.ProductName });

            Console.WriteLine("Group join with flat collection:");
            foreach (var v in groupJoin)
            {
                Console.WriteLine(v.CategoryName + " - " + v.ProductName);
            }
        }
        static void LinqRightOuterJoin()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var rightOuterJoin =
                from c in MyCategory.GetCategories()
                join p in MyProduct.GetProducts() on c.CategoryId equals p.CategoryId into pc
                from prod in pc.DefaultIfEmpty(MyProduct.Empty)
                select new { prod.ProductName, c.CategoryName };

            // Fluent expression equivalent.
            // var rightOuterJoin = MyCategory.GetCategories().GroupJoin(MyProduct.GetProducts(), c => c.CategoryId, p => p.CategoryId, (c, ps) => new { Products = ps, c.CategoryName }).SelectMany(tmp => tmp.Products.DefaultIfEmpty(MyProduct.Empty), (tmp, prod) => new { prod.ProductName, tmp.CategoryName });

            Console.WriteLine("Right outer join:");
            foreach (var v in rightOuterJoin)
            {
                Console.WriteLine(v);
            }
        }
        static void LinqLeftOuterJoin()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var leftOuterJoin =
                from p in MyProduct.GetProducts()
                join c in MyCategory.GetCategories() on p.CategoryId equals c.CategoryId into pc
                from cat in pc.DefaultIfEmpty(MyCategory.Empty)
                select new { p.ProductName, cat.CategoryName };

            // Fluent expression equivalent.
            // var leftOuterJoin = MyProduct.GetProducts().GroupJoin(MyCategory.GetCategories(), p => p.CategoryId, c => c.CategoryId, (p, cs) => new { p.ProductName, Categories = cs }).SelectMany(tmp => tmp.Categories.DefaultIfEmpty(MyCategory.Empty), (tmp, cat) => new { tmp.ProductName, cat.CategoryName });

            Console.WriteLine("Left outer join:");
            foreach (var v in leftOuterJoin)
            {
                Console.WriteLine(v);
            }
        }
        static void LinqGroupJoin1()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            /*
             * var groupJoin =
             *  from c in MyCategory.GetCategories()
             *  join p in MyProduct.GetProducts() on c.CategoryId equals p.CategoryId into pc
             *  select new { c.CategoryName, Products = pc };
             */

            // Fluent expression equivalent.
            var groupJoin = MyCategory.GetCategories().GroupJoin(MyProduct.GetProducts(), c => c.CategoryId, p => p.CategoryId, (c, ps) => new { c.CategoryName, Products = ps });

            Console.WriteLine("Group join:");
            foreach (var c in groupJoin)
            {
                Console.WriteLine("  Category: " + c.CategoryName);
                foreach (var p in c.Products)
                {
                    Console.WriteLine("    " + p.ProductName);
                }
            }
        }
        static void LinqFullOuterJoin()
        {
            Console.WriteLine("=== " + MethodInfo.GetCurrentMethod().Name + " ===");

            var fullOuterJoin = (
                from p in MyProduct.GetProducts()
                join c in MyCategory.GetCategories() on p.CategoryId equals c.CategoryId into pc
                from cat in pc.DefaultIfEmpty(MyCategory.Empty)
                select new { p.ProductName, cat.CategoryName }).Union(
                from c in MyCategory.GetCategories()
                join p in MyProduct.GetProducts() on c.CategoryId equals p.CategoryId into pc
                from prod in pc.DefaultIfEmpty(MyProduct.Empty)
                select new { prod.ProductName, c.CategoryName });

            // Fluent expression equivalent.
            // var fullOuterJoin = (MyProduct.GetProducts().GroupJoin(MyCategory.GetCategories(), p => p.CategoryId, c => c.CategoryId, (p, cs) => new { p.ProductName, Categories = cs }).SelectMany(tmp => tmp.Categories.DefaultIfEmpty(MyCategory.Empty), (tmp, cat) => new { tmp.ProductName, cat.CategoryName })).Union(
            //                      MyCategory.GetCategories().GroupJoin(MyProduct.GetProducts(), c => c.CategoryId, p => p.CategoryId, (c, ps) => new { Products = ps, c.CategoryName }).SelectMany(tmp => tmp.Products.DefaultIfEmpty(MyProduct.Empty), (tmp, prod) => new { prod.ProductName, tmp.CategoryName }));

            Console.WriteLine("Full outer join:");
            foreach (var v in fullOuterJoin)
            {
                Console.WriteLine(v);
            }
        }