zoukankan      html  css  js  c++  java
  • 【EFCORE笔记】预先加载&显式加载&延迟加载

    Entity Framework Core 允许使用导航属性来加载相关实体。

    加载关联数据三种方式

    预先加载:表示从数据库中加载关联数据,作为初始查询的一部分。显式加载:稍后手动控制时,从数据库中显式加载导航数据。

    延迟加载:当访问导航属性时,才从数据库中加载导航属性数据。

    IQueryable 未使用时不会执行查询行为,生成一个表达式树,需要时执行。

    默认情况下导航属性不加载任何数据

    var blog = _context.Blogs.Find(1);
    

      

    预先加载

    可以使用 Include 方法来指定要包含在查询结果中的关联数据。

    var blogs = _context.Blogs.Include(blog => blog.Posts).ToList();
    

      

    多个导航属性预先加载

    var blogs = _context.Blogs.Include(blog => blog.Posts)
    .Include(blog => blog.Owner).ToList();
    

      

    多层级导航属性加载

    使用 ThenInclude 方法可以依循关系包含多个层级的关联数据。

    var blogs = _context.Blogs.Include(blog => blog.Posts)
            .ThenInclude(post => post.Author)
            .ThenInclude(author => author.Photo)
            .ToList();
    

      可通过链式调用 ThenInclude 方法,进一步包含更深级别的关联数据。

    var blogs = context.Blogs
            .Include(blog => blog.Posts)
            .ThenInclude(post => post.Author)
            .ThenInclude(author => author.Photo)
            .ToList();
    

      

    可以将来自多个级别和多个根的关联数据合并到同一查询中。

    var blogs = context.Blogs
                    .Include(blog => blog.Posts)
                    .ThenInclude(post => post.Author)
                    .ThenInclude(author => author.Photo)
                    .Include(blog => blog.Owner)
                    .ThenInclude(owner => owner.Photo)
                    .ToList();
    

      

    这些 Incloude 在大多数情况下,会在生成 SQL 的 JOIN 联接查询。

    派生类型上的导航属性加载

    可以使用 Include 和 ThenInclude 包括来自仅在派生类型上定义的导航的相关数据。

    使用强制转换方式:

    context.Peoples.Include(person => ((Student)person).School).ToList()

    使用 as 运算符方式:

    context.Peoples.Include(person => (person as Student).School).ToList()
    

      

    使用采用类型 string 的参数的 Include 的重载:

    context.Peoples.Include("School").ToList()
    

      

    忽略导航属性加载

    如果更改查询,从而使其不再返回查询以之为开头的实体类型的实例,则会忽略 include 运算符。

    以下示例中,include 运算符基于 Blog,但 Select 运算符将查询改变为返回匿名类型。 在这种情况下, include 运算符没有任何效果。

    var blogs = _context.Blogs
            .Include(blog => blog.Posts)
            .Select(b => new
            {
                    b.Name,
                    Posts = b.Posts.ToList()
            });
    

      

    默认情况下,当忽略 include 运算符时,EF Core 将记录警告。

    dbContextOptionsBuilder..ConfigureWarnings(warnings => 
    warnings.Throw(CoreEventId.IncludeIgnoredWarning)))
    

      

    显式加载

    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();
    

      

    导航属性加载时,可生成远程 SQL 查询条件。

    var postCount = _context.Entry(blog).Collection(b => 
    b.Posts).Query().Count();
    

      

    var goodPosts = _context.Entry(blog).Collection(b => 
    b.Posts).Query().Where(p => p.Rating > 3).ToList();
    

      

    延迟加载

    使用代理的最简单延迟加载

    使用延迟加载的最简单方式是通过安装 Microsoft.EntityFrameworkCore.Proxies 包,并通过调用UseLazyLoadingProxies 来启用该包。

    Install-Package Microsoft.EntityFrameworkCore.Proxies optionsBuilder.UseLazyLoadingProxies().UseSqlServer(myConnectionString);

    EF Core 将为可重写的任何导航属性(必须是 virtual 修饰符,且可被继承)的导航属性上启用延迟加载。

    public class Blog
    {
            public virtual ICollection<Post> Posts { get; set; }
    }
    

      

    不使用代理的延迟加载

    不使用代理进行延迟加载的工作方式是将 ILazyLoader 通过构造函数注入到实体中。

    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;
            }
    }
    

      

    这样,将不要求实体类型为可继承的类型,也不要求导航属性必须是虚拟的。

    ILazyLoader 接口依赖于 Microsoft.EntityFrameworkCore.Abstractions 包。

    Install-Package Microsoft.EntityFrameworkCore.Abstractions

    不过,可以将 ILazyLoader.Load 方法以委托的形式注入,这样可以不依赖于任何包。

    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;
            }
    }
    
    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;
            }
    }
    

      

    延迟加载委托的构造函数参数必须名为“lazyLoader” 未来的一个版本中的配置将计划采用另一个名称。

    关联数据和序列化

    由于 EF Core 具有导航属性和反向导航属性,因此在对象图中可能会产生循环引用。某些序列化框架不允许使用循环引用。

    Install-Package Newtonsoft.Json

    var blogs = _context.Blogs.Include(blog => blog.Posts).ToList(); 
    string jsonString = JsonConvert.SerializeObject(blogs);
    

      

    可设置 JSON.NET 行为,忽略并防止循环引用。

    JsonSerializerSettings settings = new JsonSerializerSettings(); 
    settings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
     string jsonString = JsonConvert.SerializeObject(blogs, settings);
    

      

    另一种方法是使用 [JsonIgnore] 特性修饰其中一个导航属性。

    [JsonIgnore]
    public ICollection<Post> Posts { get; set; }
    

      

    ASP.NET CORE 中可通过扩展方式配置 MVC 默认自带的 JSON 框架设置。

    services.AddMvc() .AddJsonOptions(options => 
    options.SerializerSettings.ReferenceLoopHandling 
    = ReferenceLoopHandling.Ignore);
    

      

  • 相关阅读:
    WPF 获得DataTemplate中的控件
    WPF 制作模板页示例
    ListBox 单击变大动画效果(使用模板、样式、绑定数据源等)
    【转】关于“The type **** is not accessible due to restr
    【转】关于“The type **** is not accessible due to restr
    Jquery主要控件的取值、赋值,包括textbox,button,lable,radio,chec
    List转换成为数组
    如何把两个rs结果集中的内容合并到一个结果集中
    Jquery主要控件的取值、赋值,包括textbox,button,lable,radio,chec
    chrome新建标签 打开主页 谷歌浏览器新建标签页自动打开主页
  • 原文地址:https://www.cnblogs.com/lbonet/p/14599479.html
Copyright © 2011-2022 走看看