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}"); } } } }
本文来自博客园,作者:LI小白,转载请注明原文链接:https://www.cnblogs.com/LittleBai/p/14132511.html