IEnumerable和IQueryable
对普通集合使用where等方法查询出来的返回值为IEnumerable类型
但是对DbSet使用用where等方法出查询出来的返回值为IQueryable类型
IQueryable继承自IEnumberable,对于普通集合,Where方法会在内存中对数据进行过滤,但是EF Core如果把数据表中的所有元素加载到内存然后再过滤的话,会导致占用内存过大。所以,使用IQueryable是把where方法转换成SQL语句,使得可以在数据库中进行过滤查询。
IQueryable延迟执行
对于IQueryable接口,调用“非立即执行”方法不会立刻执行查询,调用“立即执行”方法才会立刻执行查询
立即执行方法非立即执行方法遍历IQueryable方法WhereToArrayGroupByToListOrderByMinIncludeMaxSkipCountTake
判断是否为立即执行方法:如果返回值类型是IQueryable则是非立即执行方法
延迟执行的好处是可以拼接IQueryable和复用
void QueryBooks(string searchWords, bool searchAll, bool orderByPrice, double upperPrice)
{
using TestDbContext ctx = new TestDbContext();
IQueryable<Book> books = ctx.Books.Where(b => b.Price <= upperPrice);//此时并没有执行sql语句
if (searchAll)//匹配书名或、作者名
{
books = books.Where(b => b.Title.Contains(searchWords) || b.AuthorName.Contains(searchWords));//复用books
}
else//只匹配书名
{
books = books.Where(b => b.Title.Contains(searchWords));
}
if (orderByPrice)//按照价格排序
{
books = books.OrderBy(b => b.Price);
}
//此时才调用sql语句
foreach (Book b in books)
{
Console.WriteLine(#34;{b.Id},{b.Title},{b.Price},{b.AuthorName}");
}
}
分页查询
void OutputPage(int pageIndex, int pageSize)
{
using TestDbContext ctx = new TestDbContext();
IQueryable<Book> books = ctx.Books.Where(b => !b.Title.Contains("张三"));
long count = books.LongCount();//总条数
long pageCount = (long)Math.Ceiling(count * 1.0 / pageSize);//页数
Console.WriteLine("页数:" + pageCount);
//skip(n)跳过n条数据 Take(n)取最多n条数据
var pagedBooks = books.Skip((pageIndex - 1) * pageSize).Take(pageSize);
foreach (var b in pagedBooks)
{
Console.WriteLine(b.Id + "," + b.Title);
}
}
IQueryable底层执行
ADO.NET中的DataReader中是分批从数据库中读取数据,而DataTable则是一次性读取数据
在遍历IQueryable时,是使用DataReader的方式读取数据,这样会一直占用一个数据库的连接。如果使用ToArray等方法,则使用了DataTable方式。除非遍历IQueryable并进行数据处理的过程很耗时,否则一般不需要一次性读取所有的结果,除了下面两种场景:
- ? 场景1:如果方法需要返回查询结果,并在方法内销毁上下文。
//错误,因为上下文在方法执行完成就销毁了
IQueryable<Book> QueryBooks()
{
using TestDbContext ctx = new TestDbContext();
return ctx.Books.Where(b => b.Id > 5);
}
//正确做法
IEnumerable<Book> QueryBooks()
{
using (TestDbContext ctx = new TestDbContext())
{
return ctx.Books.Where(b => b.Id > 5).ToArray();
}
}
- ? 场景2:IQueryable嵌套
DataReader方式,有些数据库不支持多个DataReader同时执行
//错误,有两个DataReader
var books = ctx.Books.Where(b=>b.Id>1);
foreach(var b in books)
{
foreach(var a in ctx.Authors)
{
Console.WriteLine(a.Id);
}
}
//可以将某个IQueryable使用ToList()加载到内存中来解决
EF Core异步方法
大部分异步方法定义在Microsoft.EntityFrameworkCore命名空间下
只有立即执行方法才有对应的异步方法
执行原生的SQL语句
- 1. 执行SQL非查询语句
dbCtx.Database.ExecuteSqlInterpolated或者异步的ctx.Database.ExecuteSqlInterpolatedAsync方法执行SQL语句
using TestDbContext ctx = new TestDbContext();
Console.WriteLine("请输入最低价格");
double price = double.Parse(Console.ReadLine());
Console.WriteLine("请输入姓名");
string aName = Console.ReadLine();
int rows = await ctx.Database.ExecuteSqlInterpolatedAsync(@#34;
insert into T_Books (Title,PubTime,Price,AuthorName)
select Title, PubTime, Price,{aName} from T_Books where Price>{price}");
//参数是FormattableString类型,会自动把{aName}、{price}翻译为@p0,@p1
- 1. 执行实体类SQL查询语句
如果SQL是一个查询语句,并且查询结果能对应一个实体类,则可以调用DbSet的FromSqlInterpolated执行sql语句
using TestDbContext ctx = new TestDbContext();
int year = int.Parse(Console.ReadLine());
//参数是同样是FormattableString类型,会自动把{year}翻译为@p0
IQueryable<Book> books = ctx.Books.FromSqlInterpolated(@#34;select * from T_Books
where DatePart(year,PubTime)>{year} order by newid()");
foreach (Book b in books)
{
Console.WriteLine(b.Title);
}
//返回值是IQueryable类型,可以复用
FromSqlInterpolated的局限下:
- ? SQL查询必须返回实体类型对应数据库表的所有列
- ? 查询结果集中的列明必须与属性映射到列明匹配
- ? 只能进行单表查询,不能使用过Join进行关联查询,但是可以在查询后面使用Include方法进行查询
实体类的跟踪
EF Core采用“快照更改跟踪”实现实体类改变的检测,当执行SaveChanges方法的时候,把存储在快照中的值与实体类的当前值进行比较,已确定哪些属性被更改。
实体类有一下5个可能的状态:
- ? 已添加。上下文正在跟踪实体类,但是还没有加到数据库表中
- ? 未改变。上下文正在跟踪实体类,且实体类存在于数据库,其属性值和数据库中一致
- ? 已修改。上下文正在跟踪实体类,且实体类存在于数据库,某些属性值已被修改
- ? 已删除。上下文正在跟踪实体类,且实体类存在于数据库,但下次调用SaveChanges时会在数据库中删除
- ? 分离。上下文未跟踪实体类
对于不同状态的实体类,执行SaveChange,会有不同的操作。
- ? 对于分离和未改变的实体类:直接忽略
- ? 对于已添加的实体类:插入数据库
- ? 对于已修改的实体类:修改后更新到数据库
- ? 对于已删除的实体类:从数据库删除
using TestDbContext ctx = new TestDbContext();
Book[] books = ctx.Books.Take(3).ToArray();
Book b1 = books[0];
Book b2 = books[1];
Book b3 = books[2];
Book b4 = new Book { Title = "零基础趣学C语言", AuthorName = "杨中科" };
Book b5 = new Book { Title = "百年孤独", AuthorName = "马尔克斯" };
b1.Title = "abc";
ctx.Remove(b3);
ctx.Add(b4);
EntityEntry entry1 = ctx.Entry(b1);
EntityEntry entry2 = ctx.Entry(b2);
EntityEntry entry3 = ctx.Entry(b3);
EntityEntry entry4 = ctx.Entry(b4);
EntityEntry entry5 = ctx.Entry(b5);
Console.WriteLine("b1.State:" + entry1.State);//更改
Console.WriteLine("b1.DebugView:" + entry1.DebugView.LongView);//可以看出旧值和新值
Console.WriteLine("b2.State:" + entry2.State);//未改变
Console.WriteLine("b3.State:" + entry3.State);//删除
Console.WriteLine("b4.State:" + entry4.State);//增加
Console.WriteLine("b5.State:" + entry5.State);//未跟踪
本文暂时没有评论,来添加一个吧(●'◡'●)