一. 原生SQL查询

         接着上篇讲。通过 Entity Framework Core 可以在使用关系数据库时下降到原始 SQL 查询。 在无法使用 LINQ 表达要执行的查询时,或因使用 LINQ 查询而导致低效的 SQL 查询时非常有用。 原始 SQL 查询可返回实体类型,或者从 EF Core 2.1 开始,可返回模型中的查询类型。

  

  1.1 基本的原始SQL查询

    可以使用FromSql扩展方法,基于原始的SQL查询,开始LINQ查询。

        var blogs = context.Blogs
            .FromSql("SELECT * FROM dbo.Blogs")
            .ToList();
        --通过sql server profiler监听的sql,如下所示:
        select * from dbo.blogs

  1.2 原生 SQL 查询可用于执行存储过程(GetMostPopularBlogs)

    var blogs = context.Blogs
        .FromSql("EXECUTE dbo.GetMostPopularBlogs")
        .ToList();

  1.3 传递参数

    在使用FromSql执行原始sql查询时,传递参数要防止SQL注入攻击,可以在SQL查询字符串中包含参数占位符,然后提供参数值作为附加参数。任何参数值将自动转换为DbParameter来防止SQL注入

       var id = 4;
       var blgos=  BloggingContext.Blogs.FromSql("select * from dbo.blogs where BlogId={0}",id).ToList();
        --通过sql server profiler监听的sql,如下所示:
        exec sp_executesql N'select * from dbo.blogs where BlogId=@p0',N'@p0 int',@p0=4

    可以构造 DbParameter 并将其作为参数值提供。 这样可以在 SQL 查询字符串中使用命名参数(与上面占位符实现一样,只不过这里显示提供了命名参数@BlogId)。

       var user = new SqlParameter("@BlogId", 4);
       var blgos=  BloggingContext.Blogs.FromSql("select * from dbo.blogs where BlogId={0}", user).ToList();
        --通过sql server profiler监听的sql,如下所示:
        exec sp_executesql N'select * from dbo.blogs where BlogId=@BlogId',N'@BlogId int',@BlogId=4

    

  1.4 使用 LINQ 编写

    发送到数据库中的 SQL 查询可以是组合的,则可以在原始 SQL 查询后面紧跟着使用 LINQ 运算符。  SELECT 关键字开始的 SQL 查询一般是可组合的以下示例使用原始SQL查询,然后使用LINQ对其进行编写以执行过滤和排序。

          var blgos=  BloggingContext.Blogs
                .FromSql("select  blogid,url from dbo.blogs")
                .Where(b=>b.BlogId>2)
                .OrderByDescending(b=>b.BlogId)
                 .Select(b=> new{ b.BlogId, b.Url})
                .ToList();
       --通过sql server profiler监听的sql,原始sql成了一个子查询,如下所示:
    SELECT [b].[BlogId], [b].[Url]
    FROM (
        select  blogid,url from dbo.blogs
    ) AS [b]
    WHERE [b].[BlogId] > 2
    ORDER BY [b].[BlogId] DESC

二. 异步查询

  当在数据库中执行查询时,异步查询可避免阻止线程。 这有助于避免冻结富客户端应用程序的 UI。 异步操作还可以增加 Web 应用程序的吞吐量,可以在等数据库操作完成时(I/O),释放当前线程到线程池,该线程可去处理其他请求。

  注意: EF Core 不支持在同一上下文实例上运行多个并行操作。 应始终等待操作完成,然后再开始下一个操作。 这通常是通过在每个异步操作上使用 await 关键字完成的。

  Entity Framework Core 提供了一组异步扩展方法,可用作执行查询并返回结果的 LINQ 方法的替代方法。示例包括 ToListAsync()ToArrayAsync()SingleAsync() 等。对于部分 LINQ 运算符(如 Where(...)OrderBy(...) 等),没有对应的异步版本,因为这些方法仅用于构建 LINQ 表达式树,而未将查询发送到数据库中执行。

  下面是一个示例,注意async必须搭配await,只有await完成后才会释放当前EF上下文, async后面必须是Task(无返回值)或Task<T>(有返回值):

    public async Task<List<Blog>> GetBlogsAsync()
    {return await context.Blogs.ToListAsync();
    }

  

三. 全局查询筛选器

  全局查询筛选器是应用于元数据模型(通常为 OnModelCreating)中的实体类型的 LINQ 查询谓词(通常传递给 LINQ Where 查询运算符的布尔表达式)。

  下面使用 HasQueryFilter API 在 OnModelCreating 中配置查询筛选器。

      protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //IsDeleted==1 没有被逻辑删除的Blog数据
            modelBuilder.Entity<Blog>().HasQueryFilter(b=>b.IsDeleted==1);
        }
         var blgos=  BloggingContext.Blogs
                .FromSql("select * from dbo.blogs")
                .Where(b=>b.BlogId>2)
                .OrderByDescending(b=>b.BlogId)
                 .Select(b=> new{ b.BlogId, b.Url})
                .ToList();
    --通过sql server profiler监听的sql,where后面加了IsDeleted==1,如下所示:
    SELECT [b].[BlogId], [b].[Url]
    FROM (
        select * from dbo.blogs
    ) AS [b]
    WHERE ([b].[IsDeleted] = 1) AND ([b].[BlogId] > 2)
    ORDER BY [b].[BlogId] DESC

  

        //可以在Linq查询语句中禁用全局查询筛选器
        BloggingContext.Blogs.IgnoreQueryFilters().ToList();

参考文献:

  原生 SQL 查询

  异步查询

  全局查询筛选器

02-27 21:11