Ef core 一些基础查询
在进行查询介绍之前,我们要先把一些数据插入到数据库当中
csharp
private static void InitData()
{
List<Student> students = GetStudents();
var sp = GetServiceProvider(); // 获取依赖注入的容器
var malemaDbContext = sp.GetService<MalemaDbContext>();
if (malemaDbContext.Students.Count() < 1)
{
malemaDbContext.AddRange(students);
malemaDbContext.SaveChanges();
}
}
private static List<Student> GetStudents()
{
var student1 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-12), ClassId = 1, Grade = 2, Name = "张三", TotalScore = 200 };
var student2 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-13), ClassId = 1, Grade = 2, Name = "李四", TotalScore = 198 };
var student3 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-13), ClassId = 2, Grade = 2, Name = "王五", TotalScore = 197 };
var student4 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-13), ClassId = 2, Grade = 2, Name = "赵七", TotalScore = 196 };
var student5 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-13), ClassId = 2, Grade = 2, Name = "狗蛋", TotalScore = 195 };
var student6 = new Student() { BirthDate = DateTime.UtcNow.AddYears(-13), ClassId = 2, Grade = 2, Name = "江小牙", TotalScore = 180 };
var students = new List<Student>()
{
student1,
student2,
student3,
student4,
student5,
student6
};
return students;
}
private static ServiceProvider GetSserviceProvider()
{
var connectionString = "Data Source=127.0.0.1;Initial Catalog=MalemaEFCoreExample;Persist Security Info=True;User Id=sa;Password=xxxxxxx";
var optionBuilder = new DbContextOptionsBuilder<MalemaDbContext>();
optionBuilder.UseSqlServer(connectionString);
var services = new ServiceCollection();
// services.AddDbContext<MalemaDbContext>(options => options.UseSqlServer(connectionString)); //注入DbContext
// services.AddDbContextFactory<MalemaDbContext>(options => options.UseSqlServer(connectionString)); // 注入 DbContext factory
services.AddDbContextPool<MalemaDbContext>(options => options.UseSqlServer(connectionString), poolSize: 64); //注入 DbContext 池
// 注入 DbContext factory 来从池中生成 Dbcontext
services.AddPooledDbContextFactory<MalemaDbContext>(options => options.UseSqlServer(connectionString), poolSize: 64);
// services.AddScoped<StudentService, StudentService>();
var sp = services.BuildServiceProvider();
return sp;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
GetServiceProvider() 是依赖注入的东西 https://www.malema.net/efcore/advanced/dbcontext-di.html 看到相关的代码
First FirstOrDefault 取出第一条
取出第一条,默认是按照 Id 升序的, 如果没有会抛出异常 System.InvalidOperationException:“Sequence contains no elements”
csharp
var student = malemaDbContext.Students.First();
Console.WriteLine(student);1
2
2
生成的sql如下, 会取出我们数据库的第一条 Id:1,Name:张三,TotalScore:200.00
sql
SELECT TOP(1) [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]1
2
2
在条件过滤写在FirstOrDefault里面 (当然First也是支持条件过滤的) 取出Id为2的第一条。如果没有的话会返回Null
csharp
var studentWithId2_1 = malemaDbContext.Students.FirstOrDefault(x => x.Id == 2);
Console.WriteLine(studentWithId2_1);1
2
2
生成的 sql如下
csharp
SELECT TOP(1) [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
WHERE [s].[Id] = 21
2
3
2
3
在where里面进行条件过滤 如果没有这条记录的话返回null
csharp
var studentWithId2_2 = malemaDbContext.Students.Where(x => x.Id == 2).FirstOrDefault();
Console.WriteLine(studentWithId2_2);1
2
2
生成的 sql如下,跟上面是一样的
csharp
SELECT TOP(1) [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
WHERE [s].[Id] = 21
2
3
2
3
取出来的数据是 Id:2,Name:李四,TotalScore:198.00
Last LastOrDefault 取出最后一条
Last跟first有点区别,使用前得先用OderBy进行排序
csharp
var lastStudent = malemaDbContext.Students.OrderBy(x => x.Id).Last();
Console.WriteLine(lastStudent);1
2
2
生成的sql如下
sql
SELECT TOP(1) [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
ORDER BY [s].[Id] DESC1
2
3
2
3
取出了最后一条 Id:6,Name:江小牙,TotalScore:180.00 Last如果不存在的话是会抛异常的。我们可以用LastOrDefault替代它
Single SingleOrDefault 取出一条
Single 跟 First有一个重大的区别是, Single是只允许一条,如果有多条满足条件则会抛出异常 System.InvalidOperationException:“Sequence contains more than one element”
csharp
var over198 = malemaDbContext.Students.SingleOrDefault(x => x.Name == "张三");
Console.WriteLine(over198);1
2
2
生成的sql语句如下
sql
SELECT TOP(2) [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
WHERE [s].[Name] = N'张三'1
2
3
2
3
是直接用了 Top 2的方式来查询。如果能查出两条的话,就会抛出异常了。
Where ToList 取出所有的数据
把满足条件的全部取出来,List正常要放在Where之后,或者take之后。不然会把所有的数据都取出来。这个通常是不行的。太慢了。 有很多写代码的时候没有注意到这会出现这个问题。还有的人会先ToList之后再进行where的过滤,这个也是会有性能问题的。
csharp
var class1List = malemaDbContext.Students.Where(x => x.ClassId == 1).ToList();1
生成的sql语句如下。可以看到 top没了
sql
SELECT [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
WHERE [s].[ClassId] = 11
2
3
2
3
所有class为1的都会被取出来 有2条
Count 统计满足条件的数量
统计 classId为1的数量。 当然count也是可以放在where之后的
csharp
var class1Count = malemaDbContext.Students.Count(x => x.ClassId == 1);
Console.WriteLine(class1Count);1
2
2
生成的sql语句如下
sql
SELECT COUNT(*)
FROM [Students] AS [s]
WHERE [s].[ClassId] = 11
2
3
2
3
Min 取得最小值
必须得指定用哪一个属性来比较
csharp
var minScore = malemaDbContext.Students.Min(it => it.TotalScore);
Console.WriteLine(minScore); // 180
var minScore2 = malemaDbContext.Students.Min(it => it.TotalScore + it.Grade); //两个可以一起运算的属性也是没有问题的1
2
3
2
3
生成的sql语句如下
SELECT MIN([s].[TotalScore])
FROM [Students] AS [s]
SELECT MIN([s].[TotalScore] + CAST([s].[Grade] AS decimal(18,2)))
FROM [Students] AS [s]1
2
3
4
5
2
3
4
5
Max 取得最大值
csharp
var maxScore = malemaDbContext.Students.Max(it => it.TotalScore);
Console.WriteLine(maxScore); // 2001
2
2
生成的sql语句如下
SELECT MAX([s].[TotalScore])
FROM [Students] AS [s]1
2
2
Average 取得平均值
csharp
var average = malemaDbContext.Students.Average(it => it.TotalScore);
Console.WriteLine(average); // 194.3333331
2
2
生成的sql语句如下
SELECT AVG([s].[TotalScore])
FROM [Students] AS [s]1
2
2
Min Max Average 没数据的时候
当数据库没有满足条件的记录时,Min Max Average 上面的写法是会抛出异常的System.InvalidOperationException:“Sequence contains no elements” 我们可以把它转成可空类型,如下。
csharp
var average2 = malemaDbContext.Students.Average(it => (decimal?)it.TotalScore);1
2
2
生成的sql语句如下
SELECT AVG([s].[TotalScore])
FROM [Students] AS [s]1
2
2
GroupBy 分组
csharp
var group = malemaDbContext.Students.GroupBy(x => x.ClassId).Select(x => new { x.Key, count = x.Count() });
foreach (var item in group)
{
Console.WriteLine($"key:{item.Key} count:{item.count}");
}1
2
3
4
5
2
3
4
5
生成的sql语句如下
SELECT [s].[ClassId] AS [Key], COUNT(*) AS [count]
FROM [Students] AS [s]
GROUP BY [s].[ClassId]1
2
3
2
3
输出:
shell
key:1 count:2
key:2 count:41
2
2
OrderBy OrderByDescending ThenBy ThenByDescending 排序
前面我们在Last 和 LastOrDefault的时候就知道我们必须用这个来进行排序。 下面的代码还展示了降序排序。还有排序完后再次跟据某个条件进行排序,这个很多时候是很有用的。
csharp
var orderByList = malemaDbContext.Students.OrderByDescending(x => x.ClassId).ThenByDescending(x => x.Id).ToList();
foreach (var item in orderByList)
{
Console.WriteLine($"classId:{item.ClassId} " + item);
}1
2
3
4
5
2
3
4
5
生成的sql语句如下
SELECT [s].[Id], [s].[BirthDate], [s].[ClassId], [s].[Grade], [s].[Name], [s].[TotalScore]
FROM [Students] AS [s]
ORDER BY [s].[ClassId] DESC, [s].[Id] DESC1
2
3
2
3
输出
shell
classId:2 Id:6,Name:江小牙,TotalScore:180.00
classId:2 Id:5,Name:狗蛋,TotalScore:195.00
classId:2 Id:4,Name:赵七,TotalScore:196.00
classId:2 Id:3,Name:王五,TotalScore:197.00
classId:1 Id:2,Name:李四,TotalScore:198.00
classId:1 Id:1,Name:张三,TotalScore:200.001
2
3
4
5
6
2
3
4
5
6
如果上面的 ThenByDescending 被换成了 OrderByDescending 哪排序就只会 最后一个的 Id进行降序排序了
上面的完整代码可以在分支querying/basic看到
powershell
git clone https://gitee.com/malema/Examples
git checkout querying/basic1
2
2
Select 更少的返回更快的速度
很多时候我们不需要返回所有的字段,所以我们需要用select来选择返回的字段。
csharp
public class StudentScoreDto
{
public int Id { get; set; }
public decimal Score { get; set; }
}
var malemaDbContext = sp.GetService<MalemaDbContext>();
var list = malemaDbContext.Students.Select(x => new StudentScoreDto()
{
Id = x.Id,
Score = x.TotalScore
});1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
上面的代码中我们先创建了一个 StudentScoreDto 类。 生成的sql语句如下, 我们会发现它确实只取了两个字段。
sql
SELECT [s].[Id], [s].[TotalScore] AS [Score]
FROM [Students] AS [s]1
2
2
有时候我们为了方便我们并不相定义dto对象。 可以写成如下的形式. 返回的是一个匿名对象,Unit test会变得不太好写,sql跟上面是一样的
csharp
var list2 = malemaDbContext.Students.Select(x => new
{
Id = x.Id,
Score = x.TotalScore
}).ToList();1
2
3
4
5
2
3
4
5
上面的完整代码可以在分支querying/select看到
powershell
git clone https://gitee.com/malema/Examples
git checkout querying/select1
2
2
代码在 ConsoleApp\Program.cs
除了上面的这些呢。 EfCore还支持 预先加载 Include ThenInclude 还Include多次。这些可以在下面的章节看到。 同样它还支持 Projection query 同样可以下面的章节看到
