9.14 函数操作
温馨提示
推荐使用 《9.17 Sql 高级代理》代替本章节功能。Sql 高级代理
能够提供更容易且更易维护的方式。
9.14.1 数据库函数
引用百度百科:
数据库函数是指当需要分析数据清单中的数值是否符合特定条件时,使用数据库工作表函数。
简单来说,数据库函数就是用于子计算的函数。其计算的结果可以用于构建 sql
语句。
9.14.1.1 支持标量函数的数据库
SqlServer | Sqlite | Cosmos | InMemoryDatabase | MySql | PostgreSQL | Oracle | Firebird | Dm |
---|---|---|---|---|---|---|---|---|
✔ | ✔ | ✔ | ✔ | ✔ |
9.14.1.2 支持表值函数的数据库
SqlServer | Sqlite | Cosmos | InMemoryDatabase | MySql | PostgreSQL | Oracle | Firebird | Dm |
---|---|---|---|---|---|---|---|---|
✔ | ✔ | ✔ | ✔ |
9.14.2 数据库函数类型
在关系型数据库中,数据库函数有这两种类型:
标量函数
:只能返回单个值表值函数
:只能返回一个结果集
9.14.3 函数的使用
9.14.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.14.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.14.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.14.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.14.4 在 Linq
中使用 标量函数
Furion
框架提供非常灵活的在 Linq
中使用标量函数的方法。如果像使用这样的方式,需要满足以下两个条件:
- 标量函数必须定义在公开静态类中,且自己也是公开静态方法
- 该公开静态方法必须贴有
[QueryableFunction]
特性
示例如下:
9.14.4.1 创建标量函数
CREATE FUNCTION FN_GetId
(
@id INT
)
RETURNS INT
AS
BEGIN
RETURN @id + 1;
END;
9.14.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.14.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.14.5 在 Linq
中使用 表值函数
EF Core 5.0
版本支持在 Linq
中操作 表值函数
,操作有点类似 视图操作
示例如下:
9.14.5.1 创建表值函数
CREATE FUNCTION dbo.GetPersons
(
@id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT Id,
Name,
Age,
Address
FROM dbo.Person
WHERE Id > @id
);
9.14.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.14.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.14.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.14.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.14.7 反馈与建议
与我们交流
给 Furion 提 Issue。