static void Main(string[] args)
        {
#if false
            // X-DOM Overview 文档对象模型 概述
            {
                XElement config = XElement.Parse(
                    @"<configuration>
	                                <client enabled='true'>
		                                <timeout>30</timeout>
	                                </client>
                                </configuration>");

                foreach (XElement child in config.Elements())
                {
                    child.Name.Dump("Child element name");
                }

                XElement client = config.Element("client");

                bool enabled = (bool)client.Attribute("enabled");   // Read attribute
                enabled.Dump("enabled attribute");

                client.Attribute("enabled").SetValue(!enabled);     // Update attribute

                int timeout = (int)client.Element("timeout");       // Read element
                timeout.Dump("timeout element");

                client.Element("timeout").SetValue(timeout * 2);    // Update element

                client.Add(new XElement("retries", 3));             // Add new elememt
            }
            // Instantiation X-DOM 实例化 X-DOM
            {
                // 方法 添加 元素节点 节点值 属性 熟悉值
                XElement lastName = new XElement("lastname", "Bloggs");
                lastName.Add(new XComment("nice name"));

                XElement customer = new XElement("customer");
                customer.Add(new XAttribute("id", 123));
                customer.Add(new XElement("firstname", "Joe"));
                customer.Add(lastName);

                customer.Dump();

                // Functional Construction 函数式构建
                new XElement("customer", new XAttribute("id", 123),
                             new XElement("firstname", "joe"),
                             new XElement("lastname", "bloggs",
                                          new XComment("nice name")
                                          )
                             );

                // Fucntional Construction from Database Query 为每个实体对象 创建 元素节点
                NutshellEntities dataContext = new NutshellEntities();
                new XElement("customers",
                             from c in dataContext.Customer
                             select new XElement("customer",
                                                 new XAttribute("id", c.ID),
                                                 new XElement("name", c.Name,
                                                              new XComment("nice name"))));
                // Automatic Deep Cloing 自动深度克隆
                var address =
                    new XElement("address",
                                 new XElement("street", "Lawley St"),
                                 new XElement("town", "North Beach")
                                 );

                var customer1 = new XElement("customer1", address);
                var customer2 = new XElement("customer2", address);

                customer1.Element("address").Element("street").Value = "Another St";
                Console.WriteLine(customer2.Element("address").Element("street").Value);
            }
#elif false
            // Navigating and Querying 导航查询
            {
                // FirstNode LastNode and Nodes
                var bench =
                    new XElement("bench",
                                 new XElement("toolbox",
                                              new XElement("handtool", "Hammer"),
                                              new XElement("handtool", "Rasp")
                                              ),
                                 new XElement("toolbox",
                                              new XElement("handtool", "Saw"),
                                              new XElement("powertool", "Nailgun")
                                              ),
                                 new XComment("Be careful with the nailgun")
                                 );

                bench.FirstNode.Dump("FirstNode");
                bench.LastNode.Dump("LastNode");

                foreach (XNode node in bench.Nodes())
                {
                    Console.WriteLine(node.ToString(SaveOptions.DisableFormatting) + "."); //  toolbox = HammerRasp
                }
                //  toolbox = SawNailgun
                // Enumerating Elements 检索元素
                foreach (XElement e in bench.Elements())
                {
                    Console.WriteLine(e.Name + "=" + e.Value);
                }
                // Quering Elements
                IEnumerable <string> toolboxWithNailgun = from toolbox in bench.Elements()
                                                          where toolbox.Elements().Any(tool => tool.Value == "Nailgun")
                                                          select toolbox.Value;
                var handTools = from toolbox in bench.Elements()
                                from tool in toolbox.Elements()
                                where tool.Name == "handtool"
                                select tool.Value;

                int toolboxCount = bench.Elements("toolbox").Count();

                var handTools2 =
                    from tool in bench.Elements("toolbox").Elements("handtool")
                    select tool.Value.ToUpper();

                toolboxWithNailgun.Dump("The toolbox with the nailgun");
                handTools.Dump("The hand tools in all toolboxes");
                toolboxCount.Dump("Number of toolboxes");
                handTools2.Dump("The hand tools in all toolboxes");

                XElement singnal       = bench.Element("toolbox").Element("xyc");
                string   nullableValue = singnal?.Value;

                // Quering Elements - Recursive
                // Descendants     返回所有叶子节点 并 可筛选
                // DescendantNodes 返回包含所有的父节点 和 叶子节点
                bench.Descendants("handtool").Count().Dump();
                foreach (XNode node in bench.DescendantNodes())
                {
                    Console.WriteLine(node.ToString(SaveOptions.DisableFormatting));
                }
                (
                    from c in bench.DescendantNodes().OfType <XComment>()
                    where c.Value.Contains("careful")
                    orderby c.Value
                    select c.Value
                ).Dump("Comments anywhere in the X-DOM containing the word 'careful'");
            }
#elif false
            // Updating X-DOM 更新 X-DOM
            {
                // SetValue Replaces Child Content
                XElement settings =
                    new XElement("settings",
                                 new XElement("timeout", 30));

                settings.Dump("Original XML");
                settings.SetValue("blah");
                settings.Dump("Notice the timeout node has disappeared");

                // SetElementValue 添加或覆盖
                XElement settings2 = new XElement("settings");

                settings.SetElementValue("timeout", 30); settings2.Dump("Adds child element");
                settings.SetElementValue("timeout", 60); settings2.Dump("Updates child element");

                // AddAfterSelf Remove Replace
                XElement items =
                    new XElement("items",
                                 new XElement("one"),
                                 new XElement("three")
                                 );

                items.Dump("Original XML");
                items.FirstNode.AddAfterSelf(new XElement("two"));
                items.Dump("After calling items.FirstNode.AddAfterSelf");
                items.FirstNode.ReplaceWith(new XComment("One was here"));
                items.Dump("After calling ReplaceWith");


                XElement contacts = XElement.Parse(@"
                                        <contacts>
	                                        <customer name='Mary'/>
	                                        <customer name='Chris' archived='true'/>
	                                        <supplier name='Susan'>
		                                        <phone archived='true'>012345678<!--confidential--></phone>
	                                        </supplier>
                                        </contacts>");

                contacts.Dump("Before");
                contacts.Elements("customer").Remove();
                contacts.Dump("After");

                contacts.Dump("Before");
                contacts.Elements()         // <customer name='Chris' archived='true'/> 被移除
                .Where(e1 => (bool?)e1.Attribute("archived") == true)
                .Remove();
                contacts.Dump("After");

                contacts.Dump("Before");
                contacts.Descendants()      // 所有 属性archived的值为 true 的元素被移除
                .Where(e2 => (bool?)e2.Attribute("archived") == true)
                .Remove();
                contacts.Dump("After");

                contacts.Dump("Before");
                contacts.Elements()
                .Where(e3 => e3.DescendantNodes().OfType <XComment>().Any(c => c.Value == "confidential"))
                .Remove();
                contacts.Dump("After");

                contacts.Descendants().OfType <XComment>().Remove(); // 移除整个树🌳中的注释节点
                contacts.DescendantNodes().OfType <XComment>().Remove();

                // Get and Set Value
                var e = new XElement("date", DateTime.Now);
                e.SetValue(DateTime.Now.AddDays(1));
                e.Value.Dump();

                DateTime   dt  = (DateTime)e;
                XAttribute a   = new XAttribute("resolution", 1.234);
                double     res = (double)a;

                // Nullables 可空类型
                int?   timeout    = (int?)e.Element("timeout");
                double resolution = (double?)e.Attribute("resolution") ?? 1.0;

                var nullXe = new XElement("Empty");
                try
                {
                    int timeout1 = (int)nullXe.Element("timeout");
                }
                catch (Exception ex)
                {
                    ex.Message.Dump("Element (\"timeout\") returns null so the result cannot be cast to int");
                }
                int?timeout2 = (int?)nullXe.Element("timeout");
                timeout2.Dump("Casting to a nullable type solve this problem");

                var data = XElement.Parse(@"
                            <data>
	                            <customer id='1' name='Mary' credit='100' />
	                            <customer id='2' name='John' credit='150' />
	                            <customer id='3' name='Anne' />
                            </data>");

                IEnumerable <string> query =
                    from cust in data.Elements()
                    where (int?)cust.Attribute("credit") > 100  // 属性值转换为 nullable,避免 NullReferenceException
                    select cust.Attribute("name").Value;

                query.Dump();

                IEnumerable <string> query2 =
                    from cust in data.Elements()
                    where cust.Attributes("credit").Any() && (int)cust.Attribute("credit") > 100
                    select cust.Attribute("name").Value;

                query2.Dump();

                // XText 与 XElement 混合内容
                XElement summary =
                    new XElement("summary",
                                 new XText("An XAttribute is "),
                                 new XElement("bold", "not"),
                                 new XText(" an XNode")
                                 );

                summary.Dump(); // An XAttribute is not an XNode
                {
                    var e1 = new XElement("test", "Hello");
                    e1.Add("World");
                    var e2 = new XElement("test", "Hello", "World");
                    var e3 = new XElement("test", new XText("Hello"), new XText("World"));
                    e1.Dump(); e2.Dump(); e3.Dump();                     // 均输出 Hello World
                    e1.Nodes().Count().Dump("Number of children in e1"); // 均为 2
                    e2.Nodes().Count().Dump("Number of children in e2");
                    e3.Nodes().Count().Dump("Number of children in e3");
                }
            }
#elif false
            // Documents and Declarations 文档和声明
            {
                // Building an XHTML document 建立一个XHTML 文档{
                {
                    var styleInstruction = new XProcessingInstruction(
                        "xml-stylesheet", "href='styles.css' type='text/css'");

                    var docType = new XDocumentType("html",
                                                    "-//W3C//DTD XHTML 1.0 Strict//EN",
                                                    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null);

                    XNamespace ns   = "http://www.w3.org/1999/xhtml";
                    var        root =
                        new XElement(ns + "html",
                                     new XElement(ns + "head",
                                                  new XElement(ns + "title", "An XHTML page")),
                                     new XElement(ns + "body",
                                                  new XElement(ns + "h1", "This is a heading."),
                                                  new XElement(ns + "p", "This is some content."))
                                     );

                    var doc =
                        new XDocument(
                            new XDeclaration("1.0", "utf-8", "no"),
                            new XComment("Reference a stylesheet"),
                            styleInstruction,
                            docType,
                            root
                            );

                    string tempPath = Path.Combine(Path.GetTempPath(), "sample.html");
                    doc.Save(tempPath);
                    Process.Start(tempPath);                      // This will display the page in IE or FireFox
                    File.ReadAllText(tempPath).Dump();

                    doc.Root.Name.LocalName.Dump("Root element's local name");

                    XElement bodyNode = doc.Root.Element(ns + "body");
                    (bodyNode.Document == doc).Dump("bodyNode.Document == doc");

                    (doc.Root.Parent == null).Dump("doc.Root.Parent is null");

                    foreach (XNode node in doc.Nodes())
                    {
                        Console.Write(node.Parent == null);
                    }
                }
                // Declarations 声明
                {
                    var doc =
                        new XDocument(
                            new XDeclaration("1.0", "utf-16", "yes"),
                            new XElement("test", "data")
                            );

                    string tempPath = Path.Combine(Path.GetTempPath(), "test.xml");
                    doc.Save(tempPath);
                    File.ReadAllText(tempPath).Dump();


                    var output   = new StringBuilder();
                    var settings = new XmlWriterSettings {
                        Indent = true
                    };
                    using (XmlWriter xw = XmlWriter.Create(output, settings))
                        doc.Save(xw);

                    output.ToString().Dump("Notice the encoding is utf-16 and not utf-8");
                }
                // Names and Namespaces 名称和命名空间
                {
                    // 直接使用 {}
                    new XElement("{http://domain.com/xmlspace}customer", "Bloggs");

                    // 使用 XName 和 XNamespace
                    XName localName = "customer";
                    localName.Dump("localName");
                    XName fullName1 = "{http://domain.com/xmlspace}customer";
                    fullName1.Dump("fullname1");

                    XNamespace ns        = "http://domain.com/xmlspace";
                    XName      fullName2 = ns + "customer";
                    fullName2.Dump("fullname2 - same result, but cleaner and more efficient");

                    var data =
                        new XElement(ns + "data",
                                     new XAttribute(ns + "id", 123)
                                     );

                    data.Dump();
                    XElement x = data.Element(ns + "customer");     // OK
                    XElement y = data.Element("customer");          // null
                    // 为每一个元素分配命名空间
                    var data2 =
                        new XElement(ns + "data",
                                     new XElement("customer", "Bloggs"),
                                     new XElement("purchase", "Bicycle")
                                     );

                    data.Dump("Before");
                    foreach (XElement e in data2.DescendantsAndSelf())
                    {
                        if (e.Name.Namespace == "")
                        {
                            e.Name = ns + e.Name.LocalName;
                        }
                    }
                    data.Dump("After");

                    // Prefixes 前缀
                    XNamespace ns1 = "http://domain.com/space1";
                    XNamespace ns2 = "http://domain.com/space2";

                    var mix =
                        new XElement(ns1 + "data",
                                     new XElement(ns2 + "element", "value"),
                                     new XElement(ns2 + "element", "value"),
                                     new XElement(ns2 + "element", "value")
                                     );
                    mix.Dump("Without prefixes");
                    mix.SetAttributeValue(XNamespace.Xmlns + "ns1", ns1);
                    mix.SetAttributeValue(XNamespace.Xmlns + "ns2", ns2);
                    mix.Dump("With prefixes");

                    XNamespace xsi  = "http://www.w3.org/2001/XMLSchema-instance";
                    var        nil  = new XAttribute(xsi + "nil", true);
                    var        cust =
                        new XElement("customers",
                                     new XAttribute(XNamespace.Xmlns + "xsi", xsi),
                                     new XElement("customer",
                                                  new XElement("lastname", "Bloggs"),
                                                  new XElement("dob", nil),
                                                  new XElement("credit", nil)
                                                  )
                                     );
                    cust.Dump();
                }
            }
#elif true
            // Annotations 注解
            {
#if true
                {
                    XElement e = new XElement("test");
                    e.AddAnnotation("Hello");
                    e.Annotation <string>().Dump("String annotations");
                    e.RemoveAnnotations <string>();
                    e.Annotation <string>().Dump("String annotations");
                }
                {
                    XElement e = new XElement("test");
                    e.AddAnnotation(new Customer {
                        ID = 13, Name = "Linaggan", Purchase = null
                    });
                    e.Annotations <Customer>().First().Name.Dump();
                    e.RemoveAnnotations <Customer>();
                    e.Annotations <Customer>().Count();
                }
#endif
            }

            // Projecting into an X-DOM 将数据映射到X-DOM中
            {
                var customers =
                    new XElement("Customers",
                                 new XElement("Customer", new XAttribute("id", 13),
                                              new XElement("name", "Lianggan13"),
                                              new XElement("buys", 3)));
                NutshellEntities dataContext = new NutshellEntities();
                var customers2 =
                    new XElement("Customers",
                                 from c in dataContext.Customer.ToList() // 注:此处 要有 ToList()
                                 select new XElement("Customer", new XAttribute("id", c.ID),
                                                     new XElement("name", c.Name),
                                                     new XElement("buys", (c.Purchase.Count))));

                {
                    IEnumerable <XElement> queryxe =
                        from c in dataContext.Customer
                        select new XElement("Customer", new XAttribute("id", c.ID),
                                            new XElement("name", c.Name),
                                            new XElement("buys", c.Purchase.Count));


                    //var customers3 = new XElement("Customers", queryxe.AsQueryable());
                }
                {
                    // 在查询的客户信息中还包含客户最近的高价购买记录的详细信息 (排除空元素)
                    IEnumerable <XElement> queryxe2 =
                        from c in dataContext.Customer
                        let lastBigBuy = (
                            from p in c.Purchase
                            where p.Price > 1000
                            orderby p.Date descending
                            select p
                            ).FirstOrDefault()
                                         select new XElement("Customer", new XAttribute("id", c.ID),
                                                             new XElement("name", c.Name),
                                                             new XElement("buys", c.Purchase.Count),
                                                             lastBigBuy == null ? null : // 客户没有lastBigBuy时,返回null,其内容自动被忽略
                                                             new XElement("LastBigBuy",
                                                                          new XElement("Description", lastBigBuy.Description),
                                                                          new XElement("Price", lastBigBuy.Price)
                                                                          ));


                    // var customers4 = new XElement("Customers", queryxe2);
                }

                // Transfer X-DOM 转换 X-DOM
                // 获取项目文件中的文件信息
                XElement   project    = XElement.Parse(@"
                                    <Project DefaultTargets=""Build"" xmlns=""http://schemas.microsoft.com/developer/msbuild/2003"">
	                                    <PropertyGroup>
		                                    <Platform Condition="" '$(Platform)' == '' "">AnyCPU</Platform>
		                                    <ProductVersion>9.0.11209</ProductVersion>
	                                    </PropertyGroup>
	                                    <ItemGroup>
		                                    <Compile Include=""ObjectGraph.cs"" />
		                                    <Compile Include=""Program.cs"" />
		                                    <Compile Include=""Properties\AssemblyInfo.cs"" />
		                                    <Compile Include=""Tests\Aggregation.cs"" />
		                                    <Compile Include=""Tests\Advanced\RecursiveXml.cs"" />
	                                    </ItemGroup>
                                    </Project>");
                XNamespace ns         = project.Name.Namespace;
                var        queryFiles =
                    new XElement("ProjectReport",
                                 from complieItem in project.Elements(ns + "ItemGroup").Elements(ns + "Compile")
                                 let include = complieItem.Attribute("Include")
                                               where include != null
                                               select new XElement("File", include.Value));
                queryFiles.Save(Directory.GetCurrentDirectory() + "\\" + "Files.xml");

                var paths =
                    from complieItem in project.Elements(ns + "ItemGroup").Elements(ns + "Compile")
                    let include = complieItem.Attribute("Include")
                                  where include != null
                                  select include.Value;
                var queryFoderFiles = new XElement("ProjectReport", ExpandPaths(paths));
                queryFoderFiles.Save(Directory.GetCurrentDirectory() + "\\" + "FoderFiles .xml");
            }
#endif
            Console.ReadKey();
        }
        static void Main(string[] args)
        {
            // 序列 --> 序列
            {
                // 筛选运算符
                // where Take TakeWhile Skip SkipWhile Distinct
                {
                    string[] names = { "Tom", "Dick", "Harry", "Mary", "Jay" };

                    var query = from n in names
                                where n.Length > 3
                                let u = n.ToUpper() // let:引入变量作用域
                                        where u.EndsWith("Y")
                                        select u;

                    var query2 = names.Where((n, i) => i % 2 == 0); // 索引筛选 仅适用本地查询

                    int[] numbers = { 3, 5, 2, 234, 4, 1 };
                    numbers.TakeWhile(n => n < 100);
                    numbers.SkipWhile(n => n < 100);
                }


                // 映射运算符
                // Select SelectMany
                {
                    {
                        // 返回 “我的文档” 下所有目录的描述,而每个子集合包含相应目录下的文件
                        string sampleDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

                        DirectoryInfo[] dirs = new DirectoryInfo(sampleDirectory).GetDirectories();

                        // 关联子查询:子查询引用外部查询对象
                        var query =
                            from d in dirs
                            where (d.Attributes & FileAttributes.System) == 0
                            select new
                        {
                            DirectoryName = d.FullName,
                            Created       = d.CreationTime,
                            Files         = from f in d.GetFiles() // 此处引用 外部 查询变量
                                            where (f.Attributes & FileAttributes.Hidden) == 0
                                            select new { FileName = f.Name, f.Length, }
                        };

                        // Here's how to enumerate the results manually:

                        foreach (var dirFiles in query)
                        {
                            Console.WriteLine("Directory: " + dirFiles.DirectoryName);
                            foreach (var file in dirFiles.Files)
                            {
                                Console.WriteLine("    " + file.FileName + "Len: " + file.Length);
                            }
                        }

                        NutshellEntities dataContext = new NutshellEntities();
                        // 检索每个客户的姓名及其 大额购买记录
                        // 外连接:不论客户有没有购买记录,所有的客户都会被查询出来
                        var query2 = from c in dataContext.Customer
                                     select new
                        {
                            c.Name,
                            Purchases = from p in c.Purchase                 // 利用导航属性
                                        where p.Price > 1000
                                        select new { p.Description, p.Price }
                        };
                        // 内连接:只查询大额购买的客户,其他客户不会被查询出来、
                        var query3 = from c in dataContext.Customer
                                     where c.Purchase.Any(p => p.Price > 1000)  // 增加一个筛选条件
                                     select new
                        {
                            c.Name,
                            Purchases = from p in c.Purchase                 // 利用导航属性
                                        where p.Price > 1000
                                        select new { p.Description, p.Price }
                        };
                        // 使用let 改进 内连接
                        var query4 = from c in dataContext.Customer
                                     let highValueP = from p in c.Purchase
                                                      where p.Price > 1000
                                                      select new { p.Description, p.Price }
                        where highValueP.Any()
                        select new { c.Name, Purchases = highValueP };
                        // 映射自定义实体类: 将结果 映射为 【多层对象层次结构】
                        var query5 = from c in dataContext.Customer
                                     select new MyCustomerEntity
                        {
                            Name      = c.Name,
                            Purchases =
                                (
                                    from p in c.Purchase
                                    where p.Price > 1000
                                    select new MyPurchaseEntity
                            {
                                Description = p.Description,
                                Price = p.Price
                            }
                                ).ToList()
                        };
                        List <MyCustomerEntity> result = query5.ToList();
                    }

                    {
                        // SelectMany 返回平面结果集 Select返回层次化结果集
                        // 如果实体类为特定关系定义了关联属性,那么可以扩展该子集合(非筛选交叉连接),实现关联查询效果
                        string[] fullNames = { "Anne Williams", "John Fred Smith", "Sue Green" };
                        var      query1    = fullNames.SelectMany(n => n.Split());
                        var      query2    = fullNames.SelectMany(n => n.Split().Select(n2 => new { n2, n }));
                        var      query3    = fullNames.SelectMany(n => n.Split(), (n, n2) => new { n, n2 });

                        IEnumerable <string> query4 =
                            from fullName in fullNames
                            from x in fullName.Split().Select(name => new { name, fullName })
                            orderby x.fullName, x.name
                        select x.name + " came from " + x.fullName;


                        IEnumerable <string> query5 = fullNames
                                                      .SelectMany(fName => fName.Split().Select(name => new { name, fName }))
                                                      .OrderBy(x => x.fName)
                                                      .ThenBy(x => x.name)
                                                      .Select(x => x.name + " came from " + x.fName);

                        // 在 LINQ to SQL 和 EF 中使用 “SelectMany”
                        var players = new[] { "Tom", "Jay", "Mary" }.AsQueryable();

                        IEnumerable <string> query6 =
                            from name1 in players
                            from name2 in players
                            where name1.CompareTo(name2) < 0
                            orderby name1, name2
                        select name1 + " vs " + name2;

                        NutshellEntities dataContext = new NutshellEntities();
                        // 内连接
                        var query7 = from c in dataContext.Customer
                                     from p in c.Purchase
                                     where p.Price > 1000
                                     from pi in p.PurchaseItem
                                     select new { c.Name, p.Description, p.Price, pi.Detail };

                        // 外连接
                        var query8 = from c in dataContext.Customer
                                     from p in c.Purchase.Where(p => p.Price > 1000).DefaultIfEmpty()
                                     //from pi in p.PurchaseItem.DefaultIfEmpty()
                                     select new
                        {
                            c.Name,
                            Description = p == null ? null : p.Description,
                            Price       = p == null ? (decimal?)null : p.Price,
                            //   Detail = pi == null ? null : pi.Detail
                        };
                    }
                }

                // 连接运算符
                // Join GroupJoin Zip
                {
                    {
                        {
                            // Join GroupJoin 将两个输入序列连接为一个输出序列,Join 生成平面的输出,GroupJoin 产生层次化的输出
                            NutshellEntities dataContext = new NutshellEntities();
                            var query1 = from c in dataContext.Customer
                                         join p in dataContext.Purchase
                                         on c.ID equals p.CustomerID
                                         select c.Name + " bought a " + p.Description;

                            Customer[] localCustomers = dataContext.Customer.ToArray(); // 转化到本地
                            Purchase[] localPurchases = dataContext.Purchase.ToArray();
                            // 【SelectMany】  vs. 【Join】
                            var slowQuery2 = from c in localCustomers
                                             //from p in c.Purchase where c.ID == p.CustomerID
                                             from p in localPurchases where c.ID == p.CustomerID
                                             select c.Name + " bought a " + p.Description;
                            var fastQuery3 = from c in localCustomers // 外部序列 localCustomers
                                             join p in localPurchases // 内部序列 localPurchases
                                             on c.ID equals p.CustomerID
                                             select c.Name + " bought a " + p.Description;
                            // Join 在流式语法中的使用
                            var query4 = localCustomers.Join(localPurchases,
                                                             c => c.ID,
                                                             p => p.CustomerID, // IDE 不会给出提示
                                                             (c, p) => new { c.Name, p.Description, p.Price }
                                                             );
                            var query5 = localCustomers.Join(localPurchases,
                                                             c => c.ID,
                                                             p => p.CustomerID,
                                                             (c, p) => new { c, p })
                                         .OrderBy(x => x.p.Price)
                                         .Select(x => x.c.Name + " bought a " + x.p.Description);
                        }
                        {
                            // GroupJoin (join + into) 返回外部元素分组的层次化结果结果
                            NutshellEntities dataContext = new NutshellEntities();
                            IEnumerable <IEnumerable <Purchase> > query1 =
                                from c in dataContext.Customer
                                join p in dataContext.Purchase
                                on c.ID equals p.CustomerID
                                into custPurchases
                                select custPurchases;
                            // 左外连接(层次)
                            var query2 =
                                from c in dataContext.Customer
                                join p in dataContext.Purchase
                                on c.ID equals p.CustomerID
                                into custPurchases
                                select new { CustName = c.Name, custPurchases };
                            // 内连接 查询仅有购买记录的客户
                            var query3 = from c in dataContext.Customer
                                         join p in dataContext.Purchase.Where(p => p.Price > 1000)
                                         on c.ID equals p.CustomerID
                                         into custPurchases
                                         where custPurchases.Any()
                                         select new { CustName = c.Name, custPurchases };

                            // 左外连接(平面) DefaultIfEmpty 需要有允许为 null 的地方
                            var query4 = from c in dataContext.Customer
                                         join p in dataContext.Purchase
                                         on c.ID equals p.CustomerID
                                         into custPurchases
                                         from pg in custPurchases.DefaultIfEmpty()
                                         select new
                            {
                                CustName    = c.Name,
                                Description = (pg == null) ? null : pg.Description,
                                Price       = (pg == null) ? (decimal?)null : pg.Price
                            };
                            // 连接查找表
                            // 将所有的购买记录都加载进查找表中
                            ILookup <int?, Purchase> purchLookup =
                                dataContext.Purchase.ToLookup(p => p.CustomerID, p => p);
                            var query5 = from c in dataContext.Customer
                                         from p in purchLookup[c.ID]
                                         select new { c.Name, p.Description, p.Price };
                            // + DefaultIfEmpty --> 外连接
                            var query6 = from c in dataContext.Customer
                                         from p in purchLookup[c.ID].DefaultIfEmpty()
                                         select new
                            {
                                CustName    = c.Name,
                                Description = (p == null) ? null : p.Description,
                                Price       = (p == null) ? (decimal?)null : p.Price
                            };
                        }
                        {
                            // Zip 同时枚举两个集合中的元素(像拉链一下)
                            int[]                numbers = { 3, 5, 7 };
                            string[]             words   = { "three", "five", "seven", "none" };
                            IEnumerable <string> zip     = numbers.Zip(words, (n, w) => n + "=" + w);
                        }
                    }
                }

                // 排序运算符
                // Ordery ThenBy Reverse
                {
                    var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable();

                    // By length, then alphabetically
                    var query1 = names.OrderBy(s => s.Length).ThenBy(s => s);

                    // By length, then second character, then first character
                    var query2 = names.OrderBy(s => s.Length).ThenBy(s => s[1]).ThenBy(s => s[0]);

                    var query3 =
                        from s in names
                        orderby s.Length, s[1], s[0]
                    select s;

                    NutshellEntities dataContext = new NutshellEntities();
                    var query4 = dataContext.Purchase
                                 .OrderByDescending(p => p.Price)
                                 .ThenBy(p => p.Description);

                    var query5 = from p in dataContext.Purchase
                                 orderby p.Price descending, p.Description
                    select p;
                    // IComparer
                    var query6 = names.OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase);

                    var query7 =
                        from c in dataContext.Customer
                        orderby c.Name.ToUpper()
                        select c.Name;

                    // IOrderedEnumerable IOrderedQueryable
                    IOrderedEnumerable <string> query8 = names.AsEnumerable().OrderBy(s => s.Length);
                    IOrderedEnumerable <string> query9 = query8.ThenBy(s => s);

                    var query10 = names.OrderBy(s => s.Length).AsEnumerable();
                    query10 = query10.Where(n => n.Length > 3);
                }

                // 分组运算符
                // GroupBy
                {
                    string[] files = Directory.GetFiles(Path.GetTempPath()).Take(100).ToArray();

                    IEnumerable <IGrouping <string, string> > query1 =
                        files.GroupBy(file => Path.GetExtension(file));

                    foreach (IGrouping <string, string> grouping in query1)
                    {
                        Console.WriteLine("Extension: " + grouping.Key);
                        foreach (string filename in grouping)
                        {
                            Console.WriteLine("   - " + filename);
                        }
                    }

                    // 指定 elementSelector 参数对输入元素处理,但与 排序主键选择器 keySelector 无关
                    var query2 = files.GroupBy(file => Path.GetExtension(file), file => file.ToUpper());

                    var query3 = from file in files
                                 group file.ToUpper() by Path.GetExtension(file);

                    // + into --> 对分组结果 筛选
                    var query4 = from file in files
                                 group file.ToUpper() by Path.GetExtension(file)
                                 into grouping
                                     where grouping.Count() < 5
                                 select grouping;

                    // 分组求和: 分组统计一年销售额
                    NutshellEntities dataContext = new NutshellEntities();
                    var query5 = from p in dataContext.Purchase
                                 group p.Price by p.Date.Year
                                 into salesByYear
                                 select new
                    {
                        Year       = salesByYear.Key,
                        TotalPrice = salesByYear.Sum()
                    };
                    // 对多个键进行分组
                    var query6 = from n in new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable()
                    group n by new
                    {
                        FirstLetter = n[0],
                        Length      = n.Length
                    };
                    // The following groups purchases by year, then returns only those groups where
                    // the average purchase across the year was greater than $1000:

                    var query7 = from p in dataContext.Purchase
                                 group p.Price by p.Date.Year into salesByYear
                                 where salesByYear.Average(x => x) > 1000
                                 select new
                    {
                        Year       = salesByYear.Key,
                        TotalSales = salesByYear.Count(),
                        AvgSale    = salesByYear.Average(),
                        TotalValue = salesByYear.Sum()
                    };
                }

                // 集合运算符
                // Concat Union Intersect Except
                {
                    int[] seq1 = { 1, 2, 3 }, seq2 = { 3, 4, 5 };

                    // 合集
                    IEnumerable <int> concat = seq1.Concat(seq2);
                    // 合集 不含共有
                    IEnumerable <int> union = seq1.Union(seq2);
                    // 交接
                    seq1.Intersect(seq2);
                    // 补集
                    seq1.Except(seq2);
                    seq2.Except(seq1);
                    var query1 = from s1 in seq1
                                 where !seq2.Contains(s1)
                                 select s1;
                }

                // 转换方方法:导入,导出
                // OfType Cast , ToArray ToList ToDictionary ToLookup AsEnumerable AsQueryable
                {
                    // Cast 兼容类型,抛异常     OfType 忽略不兼容类型
                    ArrayList classicList = new ArrayList();
                    classicList.AddRange(new int[] { 3, 4, 5 });

                    DateTime offender = DateTime.Now;
                    classicList.Add(offender);

                    IEnumerable <int>
                    ofTypeSequence   = classicList.OfType <int>(),
                        castSequence = classicList.Cast <int>();

                    // ofTypeSequence.Dump("Notice that the offending DateTime element is missing");

                    try
                    {
                        castSequence.ToList();
                    }
                    catch (InvalidCastException ex)
                    {
                        // ex.Message.Dump("Notice what the offending DateTime element does to the Cast sequence");
                    }
                }
            }
            // 序列 --> 元素或标量值
            {
                // 元素运算符
                // Fisrt FirstOfDefault Last LastOfDefault Single SingleOrDefault ElementAt DefaultIfEmpty
                {
                    // DefaultIfEmpty
                    // 输入序列为空时,返回一个default元素列表,否则返回原始输入序列
                    // 实现展平结果的外连接
                }
                // 聚合方法
                // Aggregate Average Count LongCount Sum Max Min
                {
                    // Average
                    // 查询返回了平均购买记录大于$500的用户
                    NutshellEntities dataContext = new NutshellEntities();
                    var query1 = from c in dataContext.Customer
                                 where c.Purchase.Average(p => p.Price) > 500
                                 select c;
                    // Aggregate
                    int[] numbers = { 1, 2, 3, 4 };
                    int   sum1    = numbers.Aggregate(0, (total, n) => total + n);
                    int   sum2    = numbers.Select(n => n * n).Aggregate((total, n) => total + n);
                }
                // 量词运算符
                // All Any Contains SequenceEqual
                {
                    int[]            numbers         = { 1, 2, 3, 4 };
                    bool             hasAThree       = numbers.Contains(3);
                    bool             hasABiggerThree = numbers.Any(n => n > 3);
                    NutshellEntities dataContext     = new NutshellEntities();

                    IQueryable <Customer> query1 = dataContext.Customer.Where(c => c.Purchase.All(p => p.Price < 100));
                    var query2 = "Hello".Distinct();
                    query2.SequenceEqual("Helo");
                }
            }
            // Void --> 序列
            {
                // 生成集合方法
                // Empty Rnage Repeatxdd
                {
                    int[][] numbers =
                    {
                        new int[] { 1, 2, 3 },
                        new int[] { 4, 5, 6 },
                        null                   // this null makes the query below fail.
                    };

                    IEnumerable <int> flatExp = numbers.SelectMany(innerArray => innerArray);
                    IEnumerable <int> flat    = numbers.SelectMany(innerArray => innerArray ?? Enumerable.Empty <int>());
                    Enumerable.Range(5, 5);
                    Enumerable.Repeat(true, 3);
                }
            }

            Console.ReadLine();
        }