【Entity Framework】你必須要了解EF中數(shù)據(jù)查詢之?dāng)?shù)據(jù)加載

一、概述
Entity Framework Core
允許在模型中使用導(dǎo)航屬性來加載關(guān)聯(lián)實(shí)體。有三種常見的O/RM模式可用于加載關(guān)聯(lián)數(shù)據(jù)。
- 預(yù)先加載表示從數(shù)據(jù)庫中加載關(guān)聯(lián)數(shù)據(jù),作為初始查詢的一部分;
- 顯示加載表示稍后從數(shù)據(jù)庫中顯示加載關(guān)聯(lián)數(shù)據(jù);
- 延遲加載表示在訪問導(dǎo)航屬性時(shí),從數(shù)據(jù)庫中以透明方式加載關(guān)聯(lián)數(shù)據(jù);
二、預(yù)先加載
可以使用Include
方法來指定要包含在查詢結(jié)果中的關(guān)聯(lián)數(shù)據(jù)。如示例,結(jié)果中,結(jié)果中返回的blogs將使用關(guān)聯(lián)的posts
填充其posts
屬性。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ToList();
}
Entity Framework Core 會(huì)根據(jù)之前已加載到上下文實(shí)例中的實(shí)體自動(dòng)填充導(dǎo)航屬性。 因此,即使不顯式包含導(dǎo)航屬性的數(shù)據(jù),如果先前加載了部分或所有關(guān)聯(lián)實(shí)體,則仍可能填充該屬性。
可以在單個(gè)查詢中包含多個(gè)關(guān)系的關(guān)聯(lián)數(shù)據(jù)。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.Include(blog => blog.Owner)
.ToList();
}
2.1 包含多個(gè)層級(jí)
使用ThenInclude
方法可以依循關(guān)系包含多個(gè)層級(jí)的關(guān)聯(lián)數(shù)據(jù)。以下示例加載了所有博客,其相關(guān)文章及每篇文章的作者。
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ToList();
}
可通過鏈?zhǔn)秸{(diào)用ThenInclude
,進(jìn)一步包含更深級(jí)別的關(guān)聯(lián)數(shù)據(jù)。
using(var context = new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => author.Photo)
.ToList();
}
可以將對(duì)來自多個(gè)級(jí)別和多個(gè)根的關(guān)聯(lián)數(shù)據(jù)的所有調(diào)用合并到同一查詢中。
using(var context=new BloggingContext())
{
var blogs = context.Blogs
.Include(blog => blog.Posts)
.ThenInclude(post => post.Author)
.ThenInclude(author => post.Photo)
.Include(blog => blog.Owner)
.ThenInclude(owner => owner.Photo)
.ToList();
}
你可能希望將已包含的某個(gè)實(shí)體的多個(gè)關(guān)聯(lián)實(shí)體都包含進(jìn)來。 例如,當(dāng)查詢 Blogs
時(shí),你會(huì)包含 Posts
,然后希望同時(shí)包含 Posts
的 Author
和 Tags
。 為了包含這兩項(xiàng)內(nèi)容,需要從根級(jí)別開始指定每個(gè)包含路徑。 例如,Blog -> Posts -> Author
和 Blog -> Posts -> Tags
。 這并不意味著會(huì)獲得冗余聯(lián)接查詢,在大多數(shù)情況下,EF 會(huì)在生成 SQL 時(shí)合并相應(yīng)的聯(lián)接查詢。
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();
}
2.2 經(jīng)過篩選的包含
在應(yīng)用包含功能來加載相關(guān)數(shù)據(jù)時(shí),可對(duì)已包含的集合導(dǎo)航應(yīng)用某些可枚舉的操作,這樣就可以對(duì)結(jié)果進(jìn)行篩選和排序。
支持的操作包括:Where
、OrderBy
、OrderByDescending
、ThenBy
、ThenByDescending
、Skip
和Take
。
應(yīng)對(duì)傳遞到Include
方法的Lambda中的集合導(dǎo)航應(yīng)用這類操作。如下例所示:
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();
}
可對(duì)多次包含的每個(gè)導(dǎo)航應(yīng)用相同的操作:
using (var context = new BloggingContext())
{
var filteredBlogs = context.Blogs
.Include(blog => blog.Posts.Where(post => post.BlogId == 1))
.ThenInclude(post => post.Author)
.Include(blog => blog.Posts.Where(post => post.BlogId == 1))
.ThenInclude(post => post.Tags.OrderBy(postTag => postTag.TagId).Skip(3))
.ToList();
}
三、顯示加載
可以通過DbContext.Entry(...)
API顯示加載導(dǎo)航屬性。
using(var context=new BloggingContext())
{
var blog = Context.Blogs.Single(b=>b.BlogId == 1);
context.Entry(blog).Collection(b=>b.Posts).Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}
還可以通過執(zhí)行返回關(guān)聯(lián)實(shí)體的單獨(dú)查詢來顯式加載導(dǎo)航屬性。 如果已啟用更改跟蹤,則在查詢具體化實(shí)體時(shí),EF Core 將自動(dòng)設(shè)置新加載的實(shí)體的導(dǎo)航屬性以引用任何已加載的實(shí)體,并設(shè)置已加載實(shí)體的導(dǎo)航屬性以引用新加載的實(shí)體。
3.1查詢關(guān)聯(lián)實(shí)體
可以獲得表示導(dǎo)航屬性內(nèi)容的LINQ查詢。
這使你可對(duì)查詢應(yīng)用其他運(yùn)算符。示例:
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
var postCount = context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Count();
}
還可以篩選要加載到內(nèi)存中的關(guān)聯(lián)實(shí)體。
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
var goodPosts = context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Where(p => p.Rating > 3)
.ToList();
}
四、延時(shí)加載
使用延遲加載的最簡單方法是通過安裝Microsoft.EntityFrameworkCore.Proxies
包,并通過調(diào)用UseLazyLoadingProxies
來啟動(dòng)該包。
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString);
或在使用 AddDbContext 時(shí):
.AddDbContext<BloggingContext>(
b => b.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString));
EF Core將為可被重寫的任何導(dǎo)航屬性啟用延遲加載。
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public virtual Blog Blog { get; set; }
}
4.1 不使用代理進(jìn)行延遲加載
不使用代理進(jìn)行延遲加載的工作方式是將 ILazyLoader
注入到實(shí)體中,如實(shí)體類型構(gòu)造函數(shù)中所述。例如:
public class Blog
{
private ICollection<Post> _posts;
public Blog()
{
}
private Blog(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
private ILazyLoader LazyLoader { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts
{
get => LazyLoader.Load(this, ref _posts);
set => _posts = value;
}
}
public class Post
{
private Blog _blog;
public Post()
{
}
private Post(ILazyLoader lazyLoader)
{
LazyLoader = lazyLoader;
}
private ILazyLoader LazyLoader { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog
{
get => LazyLoader.Load(this, ref _blog);
set => _blog = value;
}
}
此方法不要求實(shí)體類型為可繼承的類型,也不要求導(dǎo)航屬性必須是虛擬的,且允許通過new
創(chuàng)建的實(shí)體實(shí)例在附加到上下文后可進(jìn)行延遲加載。但它需要對(duì)Microsoft.EntityFrameworkCore.Abstractions
包中定義的ILazyLoader
服務(wù)的引用。此包包含所允許的最少的一組類型,以便將依賴此包時(shí)所產(chǎn)生的影響將到最低。不過,可以將ILazyLoader.Load
方法以委托的形式注入,這樣就可以完全避免依賴于實(shí)體類型的任何EF Core
包。文章來源:http://www.zghlxwxcb.cn/news/detail-853985.html
public class Blog
{
private ICollection<Post> _posts;
public Blog()
{
}
private Blog(Action<object, string> lazyLoader)
{
LazyLoader = lazyLoader;
}
private Action<object, string> LazyLoader { get; set; }
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Post> Posts
{
get => LazyLoader.Load(this, ref _posts);
set => _posts = value;
}
}
public class Post
{
private Blog _blog;
public Post()
{
}
private Post(Action<object, string> lazyLoader)
{
LazyLoader = lazyLoader;
}
private Action<object, string> LazyLoader { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Blog Blog
{
get => LazyLoader.Load(this, ref _blog);
set => _blog = value;
}
}
上述代碼使用 Load
擴(kuò)展方法,以便更干凈地使用委托:文章來源地址http://www.zghlxwxcb.cn/news/detail-853985.html
public static class PocoLoadingExtensions
{
public static TRelated Load<TRelated>(
this Action<object, string> loader,
object entity,
ref TRelated navigationField,
[CallerMemberName] string navigationName = null)
where TRelated : class
{
loader?.Invoke(entity, navigationName);
return navigationField;
}
}
到了這里,關(guān)于【Entity Framework】你必須要了解EF中數(shù)據(jù)查詢之?dāng)?shù)據(jù)加載的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!