第9章 Entity Framework综合应用
9.1 使用LINQ简化查询
9.1.1 LINQ基础
>语法:
>from 变量
> in 数据源对象
> where 条件表达式
> orderby 排序列 [ascending]|[descending]
> group 变量 by 分组条件
> into 临时标识符
> select 选择列
9.1.2 LINQ to Objects
1.LINQ to 字符串
示例:
string strDemo = "HelloWord!";
//定义查询表达式
var query = from n in strDemo select n;
//遍历查询结果并输出
foreach (char ch in query) {
Console.WriteLine(ch);
}
示例:
string strDemo = "HelloWord!";
//定义查询表达式,输出大写字母
var query = from n in strDemo where char.IsUpper(n) select n;
//遍历查询结果并输出
foreach (char ch in query) {
Console.WriteLine(ch);
}
2. LINQ to List<T>
List<Student> students = new List<Student>() {
new Student{Name="张明",Gender="男",Birthday=DateTime.Parse("1996-2-1")},
new Student{Name="王强",Gender="男",Birthday=DateTime.Parse("1995-6-11")},
new Student{Name="张丽丽",Gender="女",Birthday=DateTime.Parse("1996-8-9")},
new Student{Name="陈好",Gender="女",Birthday=DateTime.Parse("1994-12-24")},
new Student{Name="张无忌",Gender="男",Birthday=DateTime.Parse("1994-4-24")}
};
//查询姓张的男学生,并按照出生日期升序排序
var query = from stu in students
where stu.Name.StartsWith("张") && stu.Gender == "男"
orderby stu.Birthday ascending
select stu;
//遍历查询结果并输出
foreach (Student stu in query)
{
Console.WriteLine(stu.Name);
}
3.LINQ to ArrayList
ArrayList students = new ArrayList() {
new Student{Name="张明",Gender="男",Birthday=DateTime.Parse("1996-2-1")},
new Student{Name="王强",Gender="男",Birthday=DateTime.Parse("1995-6-11")},
new Student{Name="张丽丽",Gender="女",Birthday=DateTime.Parse("1996-8-9")},
new Student{Name="陈好",Gender="女",Birthday=DateTime.Parse("1994-12-24")},
new Student{Name="张无忌",Gender="男",Birthday=DateTime.Parse("1994-4-24")}
};
//查询姓张的男学生,并按照出生日期升序排序
var query = from Student stu in students
where stu.Name.StartsWith("张") && stu.Gender == "男"
orderby stu.Birthday ascending
select stu;
//遍历查询结果并输出
foreach (Student stu in query)
{
Console.WriteLine(stu.Name);
}
9.1.3 认识查询方法
查询表达式和查询方法示例 | ||
数据源 | 查询表达式写法 | 查询方法写法 |
String | form n in strDemo select n; | strDemo.Select(n=>n); |
String | from n in strDemo where char.IsUpper(n) selectn; | strDemo.Where(n=>Char. IsUpper(n)); |
List<T> | from stu in students where stu.Name.StartsWith(“张”)&& stu.Gender==”男” orderby stu.Birthday select stu; | students.Where(stu=>stu. Name.StartsWith(“张”)&& stu.Gender==”男”). OrderBy(stu=>stu.Birthday); |
常用查询方法 | ||
分类 | 主要方法 | 说明 |
投影 | Select(),SelectMany() | 将结果集中的元素转化成所需要的形式。对应的查询关键字为where |
筛选 | Where() | 对应的查询关键字为where |
排序 | OrderBy(),OrderByDescending()等 | 对应的查询关键字为orderby,ascending,descending |
集方法 | Union(),Distinct()等 | 对集合进行合并,剔除等操作 |
分组方法 | GroupBy() | 对应查询关键字为group by |
聚合方法 | Average(),Count(),Max(),Min(), Sum()等 | 统计方法 |
分区方法 | Skip(),Take(),First(),FirstOrDefault(),Single(),SingleOrDefault() | 用于选择结果集中指定范围的数据,其Skip(),Take()常用于分页处理;FirstOrDefault()常用于获取第一个实体;SingleOrDefault()用于获取唯一的实体 |
9.2 使用LINQ to Entities查询数据
9.2.1 从LINQ to Objects到LINQ to Entities
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
//查询姓张的男学生,并按照出生日期升序排序
var query = from stu in context.Student
where stu.StudentName.StartsWith("张") && stu.Gender == "男"
orderby stu.Birthday
select stu;
//遍历查询结果并输出
foreach (Student stu in query) {
Console.WriteLine(stu.StudentName);
}
}
常用的LINQ to Entities支持的规范函数 | |
分类 | 支持的方法或属性 |
System.String静态方法 | Concat(),Equals(),IsNullOrEmpty()等 |
String.String实例方法 | Contains(),EndsWith(),StartsWith(),IndexOf() ,Replace(),Substring(),ToLower(),ToUpper(),Trim() 等 |
System.DateTime静态方法 | Equals()等 |
System.DateTime实例方法 | Equals()等 |
System.Math静态方法 | Floor(),Round(),Abs()等 |
Guid静态方法 | NewGuid() |
9.2.2 单表查询和数据投影
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
//按照条件查询学号、姓名和生日信息并输出
DateTime compareDate = DateTime.Parse("1995-1-1");
var query = from stu in context.Student
where stu.Gender == "男" && stu.Birthday >= compareDate
&& stu.StudentName.Contains("张")
orderby stu.StudentNo ascending, stu.Birthday descending
select new { stu.StudentNo, stu.StudentName, stu.Birthday };
Console.WriteLine("学号 姓名 生日");
foreach (var stu in query.ToList())
{
Console.WriteLine("{0} {1} {2}", stu.StudentNo, stu.StudentName, stu.Birthday.ToString());
}
}
>提示:
如果是简单的查询用查询方法有时候会更简洁,例如使用Find()方法通过主
键查询。
9.2.3 查询多表数据
1.使用导航属性
stu.Grade
2.join连接查询
查询方法示例:
context.Student.Join(context.Grade, s => s.GradeID, g => g.GradeID, (s, g) => new{}).ToList();
linq语句示例:
using (MySchoolEntities context = new MySchoolEntities())
{
//获取"学生姓名、年级"
var query = from s in context.Student
join g in context.Grade
on s.GradeID equals g.GradeID
where s.Gender == "男"
select new {s.StudentName,g.GradeName };
//输出信息
foreach (var stu in query) {
Console.WriteLine("姓名:{0} 年级:{1}",stu.StudentName,stu.GradeName);
}
}
3.嵌套查询
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
//查询GradeID为1的学生
var query = from s in context.Student
where s.GradeID == 1
select new { s.Grade.GradeName, s.StudentName, s.StudentNo };
var subQuery = from s in (query) where s.GradeName!="S1" select s;
}
9.2.4 聚合查询
常用聚合函数 | |
方法 | 说明 |
int Count() | 计算个数 |
int Max() | 计算最大值 |
int Min() | 计算最小值 |
int Sum() | 求和 |
int Average() | 求平均 |
9.3 处理关联数据
1.添加管理数据
GradeId=(from g in context.Grade where g.GradeName=”S1” select
g.GradeId).SingleOrDefault();
(from g in context.Grade where g.GradeName=”S1” select g)
.SingleOrDefault().Student.Add(stu);
2.级联删除
using (MySchoolEntities context = new MySchoolEntities())
{
//Y2年级
var grade = (from g in context.Grade where g.GradeName == "Y2" select g).SingleOrDefault();
//删除Y2年级的学生
while (grade.Student.Count > 0) {
context.Student.Remove(grade.Student.FirstOrDefault());
}
//删除Y2年级
context.Grade.Remove(grade);
context.SaveChanges();
}
9.4 优化——从实体框架开始
引起性能低的原因主要在以下几个方面。
1. 复杂的对象管理机制
2. 高度封装的执行机制
3. 低效的SQL语句
9.4.1 从LINQ to Entities到SQL
1.使用ToString()直接输出T-SQL代码
2.使用SQL Server Profiler跟踪SQL
9.4.2 实体框架的状态管理
示例:
Grade grade = new Grade();
//输出当前对象的状态
Console.WriteLine(context.Entry(grade).State.ToString());
状态 | 说明 | 具备该状态的对象 |
Detached | 对象存在,但没有被跟踪 | 新创建的对象 |
Unchanged | 对象尚未经过修改 | 从DbContext 中读取的对象、 使用Attach()方法添加的对象、 执行SaveChanges()后的对象 |
Added | 对象为新对象,并且已添加到对象上下文 | 使用Add()方法添加的对象 |
Deleted | 对象已从上下文中删除 | 使用Remove()方法移除的对象 |
Modified | 对象上的一个属性已更改 | 受DbContext管理,并被修改属性的对象 |
DbEntitiyEntry的主要成员 | |
方法或属性 | 说明 |
DbPropertyValues CurrentValues | 获取由此对象表示的跟踪实体的当前属性值 |
DbPropertyValues OriginalValues | 获取由此对象表示的跟踪实体的原始属性值 |
EntityState State | 获取或设置实体的状态 |
DbCollectionEntry Collection( string navigationProperty) | 获取一个对象,该对象表示从该实体到相关实体的集合导航属性 |
DbReferenceEntry Reference( string navigationProperty) | 获取一个对象,该对象表示从该实体到其他实体的引用(即非集合)导航属性 |
void Reload() | 从数据库重新加载该实体时会使用数据库中的值覆盖任何属性值 |
利用状态管理机制的常用优化方式
1.无跟踪查询
(from s in context.Students select s)
.AsNoTracking().FirstOrDefault();//适用于纯粹的查询数据
2.关闭状态管理
context.Configuration.AutoDetectChangesEnabled = false;
//适用于批量操作数据
3.使用替身删除
var stu = new Student { StudentNo = 23230 };//适用于按主键删除数据
context.Students.Attach(stu);
context.Students.Remove(stu);
9.4.3 延迟加载和贪婪加载
EF数据加载机制
1.延迟加载(默认)-用时加载数据
2.贪婪加载-提前加载关联实体数据
context.Configuration.LazyLoadingEnabled = false;
var stu = (from s in context.Students.Include("Results")
where s.StudentNo == 23221 select s).Single();
9.4.4 使用本地数据缓存
示例:
//查询学生
var query = (from s in context.Students select s);
foreach (var stu in query)
{
Console.WriteLine("学生姓名:{0}", stu.StudentName);
}
//查询并输出学生人数
Console.WriteLine("学生人数为:{0}",
context.Students.Local.Count());
9.4.5 从实体框架回归SQL
DataBase成员 | |
方法或属性 | 说明 |
bool Exists() | 检查服务器上是否存在数据库 |
void Create() | 在数据库服务器上为支持上下文中定义的模型创建一个新的数据库 |
bool CreateIfNotExists() | 在该服务器上没有带相同名称的数据库时创建一个新的数据库 |
bool Delete() | 删除该数据库 |
void SetInitializer<TContext> (IDatabaseIntializer<TContext> stategy) | 设置数据库初始化策略 |
int ExecuteSqlCommand(string sql, params object[] parameters) | 对数据库执行给定的DDL/DML命令,返回命令影响行数 |
IEnumerable<IElement> SqlQuery<TElement>(string sql,params object[] parameters) | 执行sql查询,返回给定泛型类型的元素 |
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
//执行SQL命令
int result = context.Database.ExecuteSqlCommand(
"Update Grade set GradeName='第二学年' where GradeName='Y2'");
if (result > 0)
{
Console.WriteLine("数据更新成功!");
}
//查询某列数据
Console.WriteLine("学好为23214的学生姓名:");
var stuName = context.Database.SqlQuery<string>(
"select StudentName from Student where StudentNo=23214");
Console.WriteLine(stuName.ToList()[0]);
//查询多行数据
var parameter = new System.Data.SqlClient.SqlParameter("@GradeName","S1");
var stus = context.Database.SqlQuery<Student>(
"select * from Student where GradeId in (select GradeId from Grade where GradeName=@GradeName)",parameter);
Console.WriteLine("S1的学生信息: 学号 姓名");
foreach(var stu in stus){
Console.WriteLine("{0} {1}",stu.StudentNo,stu.StudentName);
}
}
除了Database.SqlQuery()方法外,还可以调用DbSet<TEntity>类型的某个方法执行查询。该方法的定义如下。
DbSqlQuery<TEntity> SqlQuery(string sql,params object[] parameters)
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
var parameter = new System.Data.SqlClient.SqlParameter("@GradeName", "S1");
var stus = context.Student.SqlQuery(
"select * from Student where GradeId in (select GradeId from Grade where GradeName=@GradeName)",parameter);
Console.WriteLine("S1的学生信息: 学号 姓名");
foreach(var stu in stus){
Console.WriteLine("{0} {1}",stu.StudentNo,stu.StudentName);
}
}
DBSet<TEntity>类型的SqlQuery()方法查询效率要低一点,因为该方法默认会被Entity Framework状态管理跟踪。
借助SqlQuery()方法,同样可以执行SQL的存储过程。
示例:
using (MySchoolEntities context = new MySchoolEntities())
{
var parameter = new System.Data.SqlClient.SqlParameter("@gender", "男");
var result = from s in context.Student.SqlQuery("execute GetStudents @gender", parameter) select s;
foreach (var stu in result)
{
Console.Write("姓名:{0}", stu.StudentName);
}
}