• 2.4.5 EF Core 查询


    一、EF Core查询

    1.1示例代码:

    关联数据加载、学员和助教都在项目分组中,调整模型,删除 Assistant,ProjectGroup 添加 Member 列表

    public List<Member> Members { get; set; }

    Member 添加 是否助教判断,分组信息

    public bool IsAssistant { get; set; }
    
    public string GroupId { get; set; }
    
    public ProjectGroup Group { get; set; }

    Task 添加 学员信息

    public Member Member { get; set; }

    接下来为每一个表添加一个控制器,一个 Project 对应多个 ProjectGroup,ProjectGroup

    namespace LighterApi.Controller
    {
        [ApiController]
        [Route("api/[controller]")]
        public class ProjectGroupController : ControllerBase
        {
            private readonly LighterDbContext _lighterDbContext;
    
            public ProjectGroupController(LighterDbContext lighterDbContext)
            {
                _lighterDbContext = lighterDbContext;
            }
    
            [HttpPost]
            public async Task<IActionResult> Create([FromBody] ProjectGroup group)
            {
                _lighterDbContext.ProjectGroups.Add(group);
                await _lighterDbContext.SaveChangesAsync();
    
                return StatusCode((int) HttpStatusCode.Created, group);
            }
            
            [HttpGet]
            [Route("{id}")]
            public async Task<IActionResult> GetAsync(string id, CancellationToken cancellationToken)
            {
                var project = await _lighterDbContext.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
                return Ok(project);
            }
        }
    }

    迁移脚本

    dotnet ef migrations add RefactoryProjectEntities
    
    dotnet ef database update

    Entity 主键添加自动生成

    /// <summary>
    /// 主键Id
    /// </summary>
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public string Id { get; set; }

    1.2EF Core为我们提供了三种加载数据的方式

    官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/related-data/

    • 预先加载
    • 显式加载
    • 延迟加载

    1.2.1预先加载

    表示从数据库中加载关联数据,作为初始查询的一部分。

    在以下示例中,结果中返回的blogs将使用关联的posts填充其 Posts 属性

    using (var context = new BloggingContext())
    {
        var blogs = context.Blogs
            .Include(blog => blog.Posts)
            .ToList();
    }

    1.2.2显式加载

    显式加载表示稍后从数据库中显式加载关联数据。

    可以通过 DbContext.Entry(...) API 显式加载导航属性。

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

    ProjectController

    // 显式加载
    var project = await _lighterDbContext.Projects.FirstOrDefaultAsync(p => p.Id == id, cancellationToken);
    await _lighterDbContext.Entry(project).Collection(p => p.Groups).LoadAsync(cancellationToken);

    1.2.3延迟加载 

    延迟加载表示在访问导航属性时,从数据库中以透明方式加载关联数据。

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

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseLazyLoadingProxies()
            .UseSqlServer(myConnectionString);

    或在使用 AddDbContext 时: 

    .AddDbContext<BloggingContext>(
        b => b.UseLazyLoadingProxies()
              .UseSqlServer(myConnectionString));

    EF Core 接着会为可重写的任何导航属性(即,必须是 virtual 且在可被继承的类上)启用延迟加载。 例如,在以下实体中,Post.Blog 和 Blog.Posts 导航属性将被延迟加载。

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

    Project

    public virtual ICollection<ProjectGroup> Groups { get; set; }

    ProjectController

    // 延迟加载
    project.Groups// 引用到属性时才加载

    二、客户端与服务端运算

    官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/client-eval 

    由于 SQL Server 提供程序不了解此方法的实现方式,因此无法将其转换为 SQL。 查询的所有其余部分是在数据库中评估的,但通过此方法传递返回的 URL 却是在客户端上完成。

    var blogs = context.Blogs
        .OrderByDescending(blog => blog.Rating)
        .Select(blog => new
        {
            Id = blog.BlogId,
            Url = StandardizeUrl(blog.Url)// 服务端转换SQL,不了解客户端方法实现
        })
        .ToList();
    
    public static string StandardizeUrl(string url)
    {
        url = url.ToLower();
    
        if (!url.StartsWith("http://"))
        {
            url = string.Concat("http://", url);
        }
    
        return url;
    }

    需要区分数据运算最终在客户端,还是服务端运行

    循环中获取分组会导致多次查询数据库

    foreach (var project in _lighterDbContext.Projects)
    {
        project.Groups// 多次查询数据库
    }

    应该一次性查询 

    var projects = _lighterDbContext.Projects.ToList();

    三、EF Core跟踪与不跟踪

    官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/tracking

    默认情况下,跟踪返回实体类型的查询。 这表示可以更改这些实体实例,然后通过 SaveChanges() 持久化这些更改。

    非跟踪查询

    var blogs = context.Blogs
        .AsNoTracking()
        .ToList();

    还可以在上下文实例级别更改默认跟踪行为:

    context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
    
    var blogs = context.Blogs.ToList();

    四、复杂查询运算 

    官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/complex-query-operators

    Join

    var query = from photo in context.Set<PersonPhoto>()
                join person in context.Set<Person>()
                    on photo.PersonPhotoId equals person.PhotoId
                select new { person, photo };

    GroupJoin

    var query = from b in context.Set<Blog>()
                join p in context.Set<Post>()
                    on b.BlogId equals p.PostId into grouping
                select new { b, grouping };

    SelectMany

    var query = from b in context.Set<Blog>()
                from p in context.Set<Post>()
                select new { b, p };

    GroupBy

    var query = from p in context.Set<Post>()
                group p by p.AuthorId into g
                select new
                {
                    g.Key,
                    Count = g.Count()
                };

    Left Join

    var query = from b in context.Set<Blog>()
                join p in context.Set<Post>()
                    on b.BlogId equals p.BlogId into grouping
                from p in grouping.DefaultIfEmpty()
                select new { b, p };

    五、原生 SQL 查询

     官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/raw-sql

    var blogs = context.Blogs
        .FromSqlRaw("SELECT * FROM dbo.Blogs")
        .ToList();

    六、全局查询筛选器

    官方文档:https://docs.microsoft.com/zh-cn/ef/core/querying/filters

    modelBuilder.Entity<Blog>().HasQueryFilter(b => EF.Property<string>(b, "_tenantId") == _tenantId);
    modelBuilder.Entity<Post>().HasQueryFilter(p => !p.IsDeleted);

    所有实体都继承了基类 Entity,所以这样会把过滤器添加在所有查询上面

    modelBuilder.Entity<Entity>().HasQueryFilter(x => x.TenantId == "");
  • 相关阅读:
    activeMQ
    @Autowired与@Resource的区别
    maven工程下get的URI中带中文名称乱码解决
    linux下安装jdk
    Redis集群之Jedis的使用
    格式化Json数据
    拷贝chrome控制台打印的对象
    为什么有的代码容易理解,有的难
    ant design pro总是跨域,proxy也没设置错误,原来是浏览器缓存,清理Chrome缓存就可以了
    VScode:保存格式化问题,ESLint插件和编辑器本身冲突
  • 原文地址:https://www.cnblogs.com/duyao/p/15010988.html
Copyright © 2020-2023  润新知