前言:
EFCore的查询方式有几种,
查询的API是比较简单的,
至少不用像原生的ADO.NET那样,需要执行SQL后手动把结果从一张DataTable中设置到数据模型中
这里只讲几种常见的查询方式,
如需了解EFCore数据查询的原理,可以参考文档
https://docs.microsoft.com/zh-cn/ef/core/querying/how-query-works
1.数据库上下文查询
这里用之前的一个多对多的例子来看
class StudentCopy
{
public int id { get; set; }
public string name { get; set; }
public int age { get; set; }
public ICollection<Course> courses { get; set; }
public StudentCopy()
{
courses = new List<Course>();
}
}
class Course
{
public int id { get; set; }
public string course_name { get; set; }
public string teacher_name { get; set; }
public ICollection<StudentCopy> students { get; set; }
public Course()
{
students = new List<StudentCopy>();
}
}
class MMDbContext:DbContext
{
public DbSet<Course> courses { get; set; }
public DbSet<StudentCopy> studentCopies { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var Connection = "server=.;Database=EFCoreTest;uid=sa;pwd=LiHan199968";
optionsBuilder.UseSqlServer(Connection);
//optionsBuilder.LogTo(Console.WriteLine,LogLevel.Debug);
}
}
现有数据如下:
//表 studentCopies
id | name | age
1 | 罗翔 | 21
2 | 孙笑川 | 99
//表 courses
id | course_name | teacher_name
1 | 張三說刑法 | 張三
2 | 這把怎麼輸 | 盧本偉
2 | 李四說民法 | 李四
//中间表 CourseStudentCopy
course_id | student_id
1 | 1
2 | 1
1 | 2
2 | 2
3 | 2
一.查询实体
1.直接在DbContext中维护的跟踪对象中查找(就是在你定义的那个集合中Find)
PS:Find方法的参数都是实体的主键类型数据
static void Main(string[] args)
{
MMDbContext context = new MMDbContext();
StudentCopy luoxiang = context.studentCopies.Find(1);
Console.WriteLine("ID:"+luoxiang.id+"姓名:" + luoxiang.name + "年齡:" + luoxiang.age );
}
结果:
ID:1姓名:羅翔年齡:21
当然你也可以直接在DbContext上调用Find方法(需指定类型,不然可能会有歧义)
static void Main(string[] args)
{
MMDbContext context = new MMDbContext();
StudentCopy luoxiang = context.Find<StudentCopy>(1);
Console.WriteLine("ID:"+luoxiang.id+"姓名:" + luoxiang.name + "年齡:" + luoxiang.age );
}
当然:这里如果你想问为什么不输出关联属性的值,这是因为DbContext默认是懒加载的,即不主动加载关联对象的属性
我们也可以试试看
static void Main(string[] args)
{
MMDbContext context = new MMDbContext();
StudentCopy luoxiang = context.Find<StudentCopy>(1);
Console.WriteLine("ID:"+luoxiang.id+"姓名:" + luoxiang.name + "年齡:" + luoxiang.age + "選課" +luoxiang.courses.Count );
}
结果:
ID:1姓名:羅翔年齡:21選課0
//数据库上下文查找并不查询关联属性
如果你想要查询它的关联属性,你需要再写一条显式查找的语句
static void Main(string[] args)
{
MMDbContext context = new MMDbContext();
StudentCopy luoxiang = context.studentCopies.Find(1);
context.Entry(luoxiang).Collection<Course>(stu=>stu.courses).Load();
Console.WriteLine("ID:"+luoxiang.id+"姓名:" + luoxiang.name + "年齡:" + luoxiang.age + "選課" +luoxiang.courses.Count );
}
结果:
ID:1姓名:羅翔年齡:21選課2
//这样就可以查询出它的关联属性了
二.查询属性(少用)
在对象跟踪的前一章中,我们提到了EntityEntry对象就是一个实体的包装类,可以在里面拿到跟踪信息等等…可以在其中获取实体的属性详情(一般查属性可以直接在对象里拿,不需要使用EntityEntry的API)
static void Main(string[] args)
{
//注意:属性查询不能查询导航字段
MMDbContext context = new MMDbContext();
StudentCopy luoxiang = context.studentCopies.Find(1);
var pv = context.Entry(luoxiang).Property(stu=>stu.age);
Console.WriteLine(pv.CurrentValue);
}
2.构造LINQ表达式查询
LINQ查询是C#的一种语言标准,
用于集合类型和数据库查询
在EFCore中推荐使用LINQ查询
LINQ的详细用法可以参考码友网的教程:
码友网LINQ教程
static void Main(string[] args)
{
MMDbContext context = new MMDbContext();
//使用Include可以预先加载实体的关联属性,如果不是用也不会加载关联属性
StudentCopy luoxiang = context.studentCopies.Include(stu=>stu.courses)
.Where(stu=>stu.age <= 100)
.OrderBy(stu=>stu.id)
.Distinct()
.First();
Console.WriteLine("ID:" + luoxiang.id + "姓名:" + luoxiang.name + "年齡:" + luoxiang.age);
foreach(var course in luoxiang.courses)
{
Console.WriteLine("所選課程ID:" + course.id + "課程名:" + course.course_name + "課程教師:"+ course.teacher_name);
}
}
结果:
ID:1姓名:羅翔年齡:21
所選課程ID:1課程名:張三說刑法課程教師:張三
所選課程ID:2課程名:這把怎麼輸課程教師:盧本偉
如果不想写过多的Include,你也可是自动设置加载关联(FluentAPI)
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//设置自动加载盗汗属性
modelBuilder.Entity<StudentCopy>().Navigation(stu=>stu.courses).AutoInclude();
}
当然如果以个实体的关联属性也包含其他关联属性,也不可传递使用ThenInclude()
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author) //加载关联属性的关联属性
.Include(blog => blog.Posts)
.ThenInclude(post => post.Tags) //分两次加载
.ToList();
}
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Owner.AuthoredPosts)
.ThenInclude(post => post.Blog.Owner.Photo) //一次加载关联属性的两个关联属性
.ToList();
}
同时也可以对关联属性进行LINQ查找,在官网中叫做筛选包含
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(
blog => blog.Posts
.Where(post => post.BlogId == 1)
.OrderByDescending(post => post.Title)
.Take(5))
.ToList();
}
3.原生SQL查询
可以把查询方法下调到原生SQL上,用于SQL查询或执行存储过程
它主要有两种方法
1.FromSqlRow(String sql,params…)
在串外指定参数
var user = "johndoe";
var blogs = context.Blogs
.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser {0}", user)
.ToList();
var user = new SqlParameter("user", "johndoe");
var blogs = context.Blogs
.FromSqlRaw("EXECUTE dbo.GetMostPopularBlogsForUser @user", user)
.ToList();
2.FromSqlInterpolated(String sql)
串内指定参数
var user = "johndoe";
var blogs = context.Blogs
.FromSqlInterpolated($"EXECUTE dbo.GetMostPopularBlogsForUser {user}")
.ToList();