Entity Framework 实体数据模型——LINQ 技巧之二

一、转换

  无论是 Select 还是 Group,最后返回的都是可枚举的集合对象。

  而 LINQ 有一组专门用于元素转换的操作,可以将指定的序列转换成特定的类型。

  1、AsEnumerable

    如果想要将一个特定的集合对象转换为 IEnumerable<T> 类型的对象,也可以通过调用 AsEnumerable 方法达到转换效果。

using System;
using System.Collections.Generic;
using System.Linq;

namespace LINQDemo.Demo
{
    class ConvertDemo
    {
        public void DoAsEnumerable()
        {
            EmployeeList<Employee> employees = new EmployeeList<Employee>()
            {
                new Employee {ID=104,Name="赵老师",Salary=5000,Department="体育组"},
                new Employee {ID=101,Name="张老师",Salary=6000,Department="数学组"},
                new Employee {ID=102,Name="王老师",Salary=7000,Department="化学组"},
                new Employee {ID=103,Name="李老师",Salary=80000,Department="历史组"}
            };

            Console.WriteLine(employees.Average(e => e.Salary));
            Console.WriteLine(employees.AsEnumerable().Average(e => e.Salary));
        }
    }

    class EmployeeList<T> : List<T>
    {
        public decimal Average(Func<T, decimal> s)
        {
            var list = (List<T>)this;
            decimal age = list.Average(s) * Decimal.Parse("0.1");
            return age;
        }
    }

    public class Employee
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public int Salary { get; set; }
        public string Department { get; set; }
    }
}

    AsEnumerable() 方法可以让调用此方法的对象从一般的集合对象转换成 IEnumerable 的集合对象,以支持 LINQ 运算。

    比如 ADO.NET 的 DataTable 不支持 LINQ 运算,我们可以通过调用 DataTableExtensions.AsEnumerable 方法进行转换。

    转换后将返回 EnumerableRowCollection<DataRow> 对象,该对象支持 LINQ 运算。

using System;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

namespace LINQDemo.Demo
{

    class ConvertDemo
    {
        public void TableAsEnumrtable()
        {
            string conStr = @"data source=...";   //数据库的链接地址
            SqlConnection conn = new SqlConnection(conStr);
            SqlCommand cmd = new SqlCommand("select * from Student", conn);
            conn.Open();
            SqlDataAdapter da = new SqlDataAdapter(cmd);
            DataTable dt = new DataTable();
            da.Fill(dt);
            conn.Close();
            da.Dispose();

            EnumerableRowCollection<DataRow> stus = from stu in dt.AsEnumerable()
                                                    select stu;
            foreach (var st in stus)
            {
                Console.WriteLine($"姓名:{st.Field<string>("Name")} \t年龄:{st.Field<int>("Age")}");
            }
        }
    }
}

    我们还可以优化下取值结构:

var stus = from stu in dt.AsEnumerable()
            select new { 
                Name = stu.Field<string>("Name"),
                Age = stu.Field<int>("Age")
            };
foreach (var st in stus)
{
   Console.WriteLine($"姓名:{st.Name} \t年龄:{st.Age}");
}

  2、Array 与 List

    Array 和 List 是常见的集合对象种类,我们可以通过 ToArray 或 ToList 这两个方法进行集合的转换。

    ToArray 和 ToList 都是将指定的 source 转换为对应的类型对象来返回的。这两个方法返回的是 source

    序列内容元素的复本。

    无论是 Array 还是 List,转换操作都非常有用,特别是我们想改变 LINQ 查询运算结果的类型时,

    他提供的转换功能可以让我们很方便地在各种类型之间进行切换。

    先来看看 Array 与 List 之间的转换:

public void DoArrayList()
{
    List<string> weekDay = new List<string>() { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
    string[] weekDayArray = weekDay.ToArray();
    for (int i = 0; i < weekDayArray.Length; i++)
    {
        Console.WriteLine($"{weekDayArray[i]} ");
    }
    Console.WriteLine("\n");
    List<string> weekDayList = weekDayArray.ToList();
    foreach (var day in weekDayList)
    {
        Console.WriteLine($"{day} ");
    }
}

    在具体的实现中,ToList 方法经常被运用于 List<T> 的转换。

using System;
using System.Data;
using System.Linq;

namespace LINQDemo.Demo
{

    class ConvertDemo
    {
        public void ToList()
        {
            using (var context = new StudentInfoModel())
            {
                IQueryable<Student> list = from stu in context.Student
                                           where CheckAge(stu.Age ?? 0)
                                           select stu;

                Console.WriteLine($"大于 16 岁的学生有 {list.Count()} 人。");
            }
        }

        bool CheckAge(int age)
        {
            if (age > 16) return true;
            else return false;
        }
    }
}

    执行之后会报错:

    

    这个错误是因为程序尝试将 CheckAge 方法转换成数据库引擎认可的合法语句时失败了导致的。

    在这种情况下,我们可以先将其转换成 List 对象(先取数据),然后再对 List 对象进行 were 运算。

using System;
using System.Data;
using System.Linq;

namespace LINQDemo.Demo
{

    class ConvertDemo
    {
        public void ToList()
        {
            using (var context = new StudentInfoModel())
            {
                IQueryable<Student> query = from stu in context.Student
                                            select stu;
                var list = from stu in query.ToList()
                           where CheckAge(stu.Age ?? 0)
                           select stu;

                Console.WriteLine($"大于 16 岁的学生有 {list.Count()} 人。");
            }
        }

        bool CheckAge(int age)
        {
            if (age > 16) return true;
            else return false;
        }
    }
}

    如果需要将复杂的对象集合转换成对应的单个属性数组,ToArray 也相当有用:

public void ToArray()
{
    using (var context = new StudentInfoModel())
    {
        string[] list = (from stu in context.Student select stu.Name).ToArray();
        for (int i = 0; i < list.Length; i++)
        {
            Console.WriteLine(list[i]);
        }
    }
}

    我们也可以将 List 对象转换成 IQueryable 对象。

public void ToList()
{
    using (var context = new StudentInfoModel())
    {
        IQueryable<Student> query = from stu in context.Student
                                    select stu;
        var list = from stu in query.ToList()
                    where CheckAge(stu.Age ?? 0)
                    select stu;
        list.AsQueryable();    // 直接调用AsQueryable() 方法即可
        Console.WriteLine($"大于 16 岁的学生有 {list.Count()} 人。");
    }
}

  3、ToDictionary 方法

    我们可以使用 ToDictionary 方法将指定的序列集合转换成 Dictionary 类型对象。

/// <param name="source">需要转换的源对象</param>
/// <param name="keySelector">用来获取对应到每一个元素项的 key 值。
///                           Dictionary 是以 key/value 类型存储的集合元素,因此必须指定这个参数作为key值的源,且不能重复。</param>
public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);

    想要获取 Dictionary 集合中的 key 及其相应的元素,必须通过一个名为 KeyValuePair 的结构进行存取。

    KeyValuePair 结构被定义在 System.Collections.Generic 命名空间中,它被定义成了 key/value 对(键值对)。

    屏上得来终觉浅,觉知此事得执行:

public void ToDictionaryStu()
{
    List<string> liStu = new List<string>() { "吴二", "张三", "王五", "赵六", "田七" };

    int indexValue = 0;

    Dictionary<string, string> stuDic = liStu.ToDictionary(keyValue => "stu-" + (indexValue++).ToString());
    foreach (KeyValuePair<string, string> dic in stuDic)
    {
        Console.WriteLine($"{dic.Key} \t{dic.Value}");
    }
}

    

    然后再结合ADO.NET数据模型来看看:

public void ToDictionaryAdo()
{
    using (var context = new StudentInfoModel())
    {
        IQueryable<Student> stus = from stu in context.Student select stu;

        Dictionary<string, Student> dic = stus.ToDictionary(key => key.Age + "_" + key.Id);

        foreach (var stu in dic)
        {
            Console.WriteLine($"{stu.Key} \t{stu.Value.Name} \t{stu.Value.Sex}");
        }
    }
}

    

    如果是要取指定的某个值的话(比如取学生名称),我们还可以这么来处理:

public void ToDictionaryAdoGetValue()
{
    using (var context = new StudentInfoModel())
    {
        IQueryable<Student> stus = from stu in context.Student select stu;

        Dictionary<string, string> dic = stus.ToDictionary(key => key.Age + "_" + key.Id, key => key.Name);

        foreach (var stu in dic)
        {
            Console.WriteLine($"{stu.Key} \t{stu.Value}");
        }
    }
}

    

    不同的地方就是转换类型变了,一个返回的是 Dictionary<string, Student> 类型,而另一个则是 Dictionary<string, string> 类型。

  4、ToLookup

    ILookup 是一对多的 Dictionary 类型对象,与 Dictionary 的差异在于:

    ILookup 的 key/value 是一对多的关系,集合中的 key 会映射到一组包含多个项值的集合;

    而 Dictionary 则是一对一的关系。

    直接看示例吧,简单易理解:

public void DoToLookup()
{
    List<string> listStu = new List<string> { "Tom", "Jerry","吴二", "张三", "王五", "赵六", "田七" };

    int i = 0;
    ILookup<string, string> lookup = listStu.ToLookup(key => "name");
    foreach (IGrouping<string, string> igp in lookup)
    {
        Console.WriteLine(igp.Key);
        foreach (string str in igp)
        {
            Console.WriteLine(str);
        }
    }
}

     

    可以看出来,ToLookup 把所有的姓名都归拢到一个 key 里了。

    接下来结合 ADO.NET 实体数据模型来瞧瞧:

public void DoToLookupAdo()
{
    using (var context = new StudentInfoModel())
    {
        IQueryable<Student> stus = from stu in context.Student select stu;

        ILookup<bool?, Student> look = stus.ToLookup(key => key.Sex);

        foreach (var group in look)
        {
            Console.WriteLine($"\n{group.Key}");
            foreach (var gp in group)
            {
                Console.WriteLine($" {gp.Id} \t{gp.Name}");
            }
        }
    }
}

     

    可以看出来,程序按照 True (男)和 False (女)对学生进行了归组分类。

    和 Dictionary 一样,ToLookup 也可以指定取特定的值,比如取名称:

public void DoToLookupAdoGetValue()
{
    using (var context = new StudentInfoModel())
    {
        IQueryable<Student> stus = from stu in context.Student select stu;

        ILookup<bool?, string> look = stus.ToLookup(key => key.Sex, key => key.Name);

        foreach (var group in look)
        {
            Console.WriteLine($"\n{group.Key}:");
            foreach (var gp in group)
            {
                Console.WriteLine($"    {gp}");
            }
        }
    }
}

    

 

 

posted @ 2020-12-20 23:18  LI小白  阅读(180)  评论(0编辑  收藏  举报