Skip to content

9.15 函数操作

温馨提示推荐使用 《9.18 Sql 高级代理》代替本章节功能。Sql 高级代理 能够提供更容易且更易维护的方式。

9.15.1 数据库函数

引用百度百科:

数据库函数是指当需要分析数据清单中的数值是否符合特定条件时,使用数据库工作表函数。

简单来说,数据库函数就是用于子计算的函数。其计算的结果可以用于构建 sql 语句。

9.15.1.1 支持标量函数的数据库

SqlServer Sqlite Cosmos InMemoryDatabase MySql PostgreSQL Oracle Firebird Dm

9.15.1.2 支持表值函数的数据库

SqlServer Sqlite Cosmos InMemoryDatabase MySql PostgreSQL Oracle Firebird Dm

9.15.2 数据库函数类型

在关系型数据库中,数据库函数有这两种类型:

  • 标量函数:只能返回单个值
  • 表值函数:只能返回一个结果集

9.15.3 函数的使用

9.15.3.1 标量函数返回 object

// ISqlRepository 方法  
var value = _sqlRepository.SqlFunctionScalar("func_GetValue");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetValue();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlFunctionScalar("func_GetValue");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlFunctionScalar("func_GetValue");  

// 变态懒人方式,直接通过函数名执行  
var value = "func_GetValue".SqlFunctionScalar();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.15.3.2 标量函数返回 T

// ISqlRepository 方法  
var value = _sqlRepository.SqlFunctionScalar<string>("func_GetValue");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetValue();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlFunctionScalar<string>("func_GetValue");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlFunctionScalar<string>("func_GetValue");  

// 变态懒人方式,直接通过函数名执行  
var value = "func_GetValue".SqlFunctionScalar<string>();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.15.3.3 表值函数返回 DataTable

// ISqlRepository 方法  
var value = _sqlRepository.SqlFunctionQuery("func_GetTable");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetTable();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlFunctionQuery("func_GetTable");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlFunctionQuery("func_GetTable");  

// 变态懒人方式,直接通过函数名执行  
var value = "func_GetTable".SqlFunctionQuery();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.15.3.4 表值函数返回 List<T>

// ISqlRepository 方法  
var value = _sqlRepository.SqlFunctionQuery<Person>("func_GetTable");  

// ISqlDispatchProxy 方式  
var value = _sqlExecuteProxy.GetTable();  // 推荐方式  

// 实体仓储方式  
var value = _personRepository.SqlFunctionQuery<Person>("func_GetTable");  

// IRepository 非泛型方式  
var value = _repository.Sql().SqlFunctionQuery<Person>("func_GetTable");  

// 变态懒人方式,直接通过函数名执行  
var value = "func_GetTable".SqlFunctionQuery<Person>();  

关于异步Furion 框架每一个数据库操作都支持异步方式,由于篇幅有限,就不列举异步方式了。

9.15.4 在 Linq 中使用 标量函数

Furion 框架提供非常灵活的在 Linq 中使用标量函数的方法。如果像使用这样的方式,需要满足以下两个条件:

  • 标量函数必须定义在公开静态类中,且自己也是公开静态方法
  • 公开静态方法必须贴有 [QueryableFunction] 特性

示例如下:

9.15.4.1 创建标量函数

CREATE FUNCTION FN_GetId  
(  
    @id INT  
)  
RETURNS INT  
AS  
BEGIN  
    RETURN @id + 1;  
END;  

9.15.4.2 创建静态类和静态方法

创建静态类,如 QueryFunctions,将该 标量函数 放在静态类中:

using Furion.DatabaseAccessor;  
using System;  

namespace Furion.Application  
{  
    // 必须是公开静态的  
    public static class QueryFunctions  
    {  
        // 必须是静态方法  
        [QueryableFunction("FN_GetId", "dbo")]  // 配置标量函数  
        public static int GetId(int id) => throw new NotSupportedException();  
    }  
}  

9.15.4.3 在 Linq 中使用

_personRepository.Where(u => u.Id > QueryFunctions.GetId(1)).ToList();  

SELECT [p].[Id], [p].[Address], [p].[Age], [p].[CreatedTime], [p].[IsDeleted], [p].[Name], [p].[UpdatedTime]  
FROM [Person] AS [p]  
WHERE [p].[Id] > [dbo].[FN_GetId](1)    // 💥 注意这里  

9.15.5 在 Linq 中使用 表值函数

EF Core 5.0 版本支持在 Linq 中操作 表值函数,操作有点类似 视图操作

示例如下:

9.15.5.1 创建表值函数

CREATE FUNCTION dbo.GetPersons  
(  
    @id INT  
)  
RETURNS TABLE  
AS  
RETURN  
(  
    SELECT Id,  
           Name,  
           Age,  
           Address  
    FROM dbo.Person  
    WHERE Id > @id  
);  

9.15.5.2 创建表值函数模型

namespace Furion.Core  
{  
    public class F_Person  
    {  
        /// <summary>  
        /// 主键Id  
        /// </summary>  
        public int Id { get; set; }  

        /// <summary>  
        /// 姓名  
        /// </summary>  
        public string Name { get; set; }  

        /// <summary>  
        /// 年龄  
        /// </summary>  
        public int Age { get; set; }  

        /// <summary>  
        /// 住址  
        /// </summary>  
        public string Address { get; set; }  
    }  
}  

9.15.5.3 表值函数配置

DbContext 类中定义方法:

using Furion.DatabaseAccessor;  
using Microsoft.EntityFrameworkCore;  
using System.Linq;  

namespace Furion.EntityFramework.Core  
{  
    [AppDbContext("Sqlite3ConnectionString")]  
    public class FurionDbContext : AppDbContext<FurionDbContext>  
    {  
        public IQueryable<F_Person> GetPersons(int id) => FromExpression(() => GetPersons(id));  

        public FurionDbContext(DbContextOptions<FurionDbContext> options) : base(options)  
        {  
        }  

        protected override void OnModelCreating(ModelBuilder modelBuilder)  
        {  
            base.OnModelCreating(modelBuilder);  

            modelBuilder.Entity(typeof(F_Person)).HasNoKey();  
            modelBuilder.HasDbFunction(() => GetPersons(default));  
        }  
    }  
}  

9.15.5.4 在 Linq 中使用

IQueryable<F_Person> query = _repository.DynamicDbContext.GetPersons(1);  
var result = query.Where(u => u.Name.Equals("Furion")).ToList();  

最终生成 Sql

SELECT [g].Id, [g].Name, [g].Age, [g].Address  
FROM dbo.GetPersons(1) AS [g]  
WHERE [g].Name == N'Furion';  

9.15.6 在 EF Core 内置函数

EF Core 为我们提供了很多常用的内置函数,可以在 Lambda 条件中使用,主要是通过 EF.Functions 调用,如:

_repository.Where(u => EF.Functions.DateDiffHour(u.CreatedDt, DateTime.Now) > 8).FirstOrDefault();  

这个语句使用了 EF.Functions.DateDiffHour 最终生成的 Sql 如下:

SELECT TOP(1) [a].*  
FROM [dbo].[TEST] AS [a]  
WHERE DATEDIFF(HOUR, [a].[CREATED_DT], GETDATE()) > 8  

EF Core 内置函数就不一一列出了,可以通过 EF.Functions 查看更多,如果不能满足自己的需求,那么可以自定义 Linq 标量函数

9.15.7 反馈与建议

与我们交流给 Furion 提 Issue