9.13 存储过程操作

温馨提示

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

9.13.1 关于存储过程#

引用百度百科:

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的 SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

存储过程是数据库中的一个重要对象。在数据量特别庞大的情况下利用存储过程能达到倍速的效率提升。

简单来说,存储过程就是关系型数据库中(Sqlite 除外)中编写逻辑的函数/方法,通过这种方式,可以将 sql 编译缓存起来,大大提高存储过程的执行效率。

这里不讨论存储过程的优缺点。

9.13.1.1 支持存储过程的数据库#

SqlServerSqliteCosmosInMemoryDatabaseMySqlPostgreSQLOracleFirebirdDm

9.13.2 存储过程使用#

Furion 框架中提供了多种存储过程操作方式:

  • ISqlRepositorySql 操作仓储,可通过 依赖注入
  • ISqlDispatchProxySql 代理方式(高级用法,推荐)
  • 通过任意实体仓储操作:personRepository.SqlProcedureQuery(procName)
  • 通过字符串拓展方法:procName.SqlProcedureQuery()
  • 通过 repository.Sql().SqlProcedureQuery() 方式

9.13.2.1 初始化方式#

using Furion.DatabaseAccessor;
using Furion.DynamicApiController;
namespace Furion.Application
{
[DynamicApiController]
public class SqlService
{
private ISqlRepository _sqlRepository;
public SqlService(ISqlRepository sqlRepository)
{
_sqlRepository = sqlRepository;
}
}
}

9.13.2.2 返回 DataTable#

// ISqlRepository 方法
var dataTable = _sqlRepository.SqlProcedureQuery("proc_GetPersons");
// ISqlDispatchProxy 方式
var dataTable = _sqlExecuteProxy.GetPersons(); // 推荐方式
// 实体仓储方式
var dataTable = _personRepository.SqlProcedureQuery("proc_GetPersons");
// IRepository 非泛型方式
var dataTable = _repository.Sql().SqlProcedureQuery("proc_GetPersons");
// 变态懒人方式,直接通过存储过程名执行
var dataTable = "proc_GetPersons".SqlProcedureQuery();
关于异步

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

9.13.2.3 返回 DataSet#

// ISqlRepository 方法
var dataSet = _sqlRepository.SqlProcedureQueries("proc_GetPersons");
// ISqlDispatchProxy 方式
var dataSet = _sqlExecuteProxy.GetPersons(); // 推荐方式
// 实体仓储方式
var dataSet = _personRepository.SqlProcedureQueries("proc_GetPersons");
// IRepository 非泛型方式
var dataSet = _repository.Sql().SqlProcedureQueries("proc_GetPersons");
// 变态懒人方式,直接通过存储过程名执行
var dataSet = "proc_GetPersons".SqlProcedureQueries();
关于异步

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

9.13.2.4 返回 List<T>#

// ISqlRepository 方法
var list = _sqlRepository.SqlProcedureQuery<Person>("proc_GetPersons");
// ISqlDispatchProxy 方式
var list = _sqlExecuteProxy.GetPersons(); // 推荐方式
// 实体仓储方式
var list = _personRepository.SqlProcedureQuery<Person>("proc_GetPersons");
// IRepository 非泛型方式
var list = _repository.Sql().SqlProcedureQuery<Person>("proc_GetPersons");
// 变态懒人方式,直接通过存储过程名执行
var list = "proc_GetPersons".SqlProcedureQuery<Person>();
关于异步

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

9.13.2.5 返回 Tuple<T1,...T8>#

Furion 框架大大利用了 Tuple 的特性,将返回多个结果集转成 Tuple<T1,...T8> 类型

// ISqlRepository 方法
// 返回一个结果集
var list1 = _sqlRepository.SqlProcedureQueries<T1>("proc_GetPersons");
// 返回两个结果集
var (list1, list2) = _sqlRepository.SqlProcedureQueries<T1,T2>("proc_GetPersons");
// 返回三个结果集
var (list1, list2, list3) = _sqlRepository.SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");
// 返回四个结果集
var (list1, list2, list3, list4) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");
// 返回五个结果集
var (list1, list2, list3, list4, list5) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");
// 返回六个结果集
var (list1, list2, list3, list4, list5, list6) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");
// 返回七个结果集
var (list1, list2, list3, list4, list5, list6, list7) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");
// 返回八个结果集
var (list1, list2, list3, list4, list5, list6, list7, list8) = _sqlRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");
// ==================================
// ISqlDispatchProxy 方式,推荐方式,自动处理多个结果集
// 返回一个结果集
var list1 = _sqlRepository.GetDatas();
// 返回两个结果集
var (list1, list2) = _sqlRepository.GetDatas();
// 返回三个结果集
var (list1, list2, list3) = _sqlRepository.GetDatas();
// 返回四个结果集
var (list1, list2, list3, list4) = _sqlRepository.GetDatas();
// 返回五个结果集
var (list1, list2, list3, list4, list5) = _sqlRepository.GetDatas();
// 返回六个结果集
var (list1, list2, list3, list4, list5, list6) = _sqlRepository.GetDatas();
// 返回七个结果集
var (list1, list2, list3, list4, list5, list6, list7) = _sqlRepository.GetDatas();
// 返回八个结果集
var (list1, list2, list3, list4, list5, list6, list7, list8) = _sqlRepository.GetDatas();
// ==================================
// 实体仓储方式
// 返回一个结果集
var list1 = _personRepository.SqlProcedureQueries<T1>("proc_GetPersons");
// 返回两个结果集
var (list1, list2) = _personRepository.SqlProcedureQueries<T1,T2>("proc_GetPersons");
// 返回三个结果集
var (list1, list2, list3) = _personRepository.SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");
// 返回四个结果集
var (list1, list2, list3, list4) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");
// 返回五个结果集
var (list1, list2, list3, list4, list5) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");
// 返回六个结果集
var (list1, list2, list3, list4, list5, list6) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");
// 返回七个结果集
var (list1, list2, list3, list4, list5, list6, list7) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");
// 返回八个结果集
var (list1, list2, list3, list4, list5, list6, list7, list8) = _personRepository.SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");
// ==================================
// IRepository 非泛型方式
// 返回一个结果集
var list1 = _repository.Sql().SqlProcedureQueries<T1>("proc_GetPersons");
// 返回两个结果集
var (list1, list2) = _repository.Sql().SqlProcedureQueries<T1,T2>("proc_GetPersons");
// 返回三个结果集
var (list1, list2, list3) = _repository.Sql().SqlProcedureQueries<T1,T2,T3>("proc_GetPersons");
// 返回四个结果集
var (list1, list2, list3, list4) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4>("proc_GetPersons");
// 返回五个结果集
var (list1, list2, list3, list4, list5) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5>("proc_GetPersons");
// 返回六个结果集
var (list1, list2, list3, list4, list5, list6) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6>("proc_GetPersons");
// 返回七个结果集
var (list1, list2, list3, list4, list5, list6, list7) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>("proc_GetPersons");
// 返回八个结果集
var (list1, list2, list3, list4, list5, list6, list7, list8) = _repository.Sql().SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>("proc_GetPersons");
// ==================================
// 变态懒人方式,直接通过存储过程名执行
// 返回一个结果集
var list1 = "proc_GetPersons".SqlProcedureQueries<T1>();
// 返回两个结果集
var (list1, list2) = "proc_GetPersons".SqlProcedureQueries<T1,T2>();
// 返回三个结果集
var (list1, list2, list3) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3>();
// 返回四个结果集
var (list1, list2, list3, list4) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4>();
// 返回五个结果集
var (list1, list2, list3, list4, list5) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5>();
// 返回六个结果集
var (list1, list2, list3, list4, list5, list6) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6>();
// 返回七个结果集
var (list1, list2, list3, list4, list5, list6, list7) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7>();
// 返回八个结果集
var (list1, list2, list3, list4, list5, list6, list7, list8) = "proc_GetPersons".SqlProcedureQueries<T1,T2,T3,T4,T5,T6,T7,T8>();
关于异步

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

9.13.2.6 返回 单行单列 object#

// ISqlRepository 方法
var value = _sqlRepository.SqlProcedureScalar("proc_GetName");
// ISqlDispatchProxy 方式
var value = _sqlExecuteProxy.GetName(); // 推荐方式
// 实体仓储方式
var value = _personRepository.SqlProcedureScalar("proc_GetName");
// IRepository 非泛型方式
var value = _repository.Sql().SqlProcedureScalar("proc_GetName");
// 变态懒人方式,直接通过存储过程名执行
var value = "proc_GetName".SqlProcedureScalar();
关于异步

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

9.13.2.7 返回 单行单列 <T>#

// ISqlRepository 方法
var value = _sqlRepository.SqlProcedureScalar<string>("proc_GetName");
// ISqlDispatchProxy 方式
var value = _sqlExecuteProxy.GetName(); // 推荐方式
// 实体仓储方式
var value = _personRepository.SqlProcedureScalar<string>("proc_GetName");
// IRepository 非泛型方式
var value = _repository.Sql().SqlProcedureScalar<string>("proc_GetName");
// 变态懒人方式,直接通过存储过程名执行
var value = "proc_GetName".SqlProcedureScalar<string>();
关于异步

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

9.13.2.8 执行但无返回#

// ISqlRepository 方法
_sqlRepository.SqlProcedureNonQuery("proc_UpdateData");
// ISqlDispatchProxy 方式
_sqlExecuteProxy.UpdateData(); // 推荐方式
// 实体仓储方式
_personRepository.SqlProcedureNonQuery("proc_UpdateData");
// IRepository 非泛型方式
_repository.Sql().SqlProcedureNonQuery("proc_UpdateData");
// 变态懒人方式,直接通过存储过程名执行
"proc_UpdateData".SqlProcedureNonQuery();
关于异步

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

9.13.3 执行复杂存储过程#

在存储过程中,有一种例子非常复杂,那就是既有 INPUT 参数,又有 OUTPUT 参数,还有 RETURN 参数,同时还输出 结果集 💥,如:

CREATE PROC PROC_Output
@Id INT, // 输入参数
@Name NVARCHAR(32) OUTPUT, // 输出参数,还带长度
@Age INT OUTPUT // 输出参数
AS
BEGIN
SET @Name = 'Furion Output';
// 输出结果集
SELECT *
FROM dbo.Test
WHERE Id > @Id;
// 输出结果集
SELECT TOP 10
*
FROM dbo.Test;
SET @Age = 27;
// 带 RETURN 返回
RETURN 10;
END;

9.13.3.1 创建参数模型#

using Furion.DatabaseAccessor;
using System.Data;
namespace Furion.Application
{
public class ProcOutputModel
{
public int Id { get; set; } // 输入参数
[DbParameter(ParameterDirection.Output, Size = 32)]
public string Name { get; set; } // 输出参数
[DbParameter(ParameterDirection.Output)]
public int Age { get; set; } // 输出参数
[DbParameter(ParameterDirection.ReturnValue)]
public int ReturnValue { get; set; } // 返回值
}
}

9.13.3.2 执行复杂存储过程#

  • DataSet 方式
// ISqlRepository 方法
ProcedureOutputResult result = _sqlRepository.SqlProcedureOutput("proc_Complex", new ProcOutputModel{});
// ISqlDispatchProxy 方式
ProcedureOutputResult result = _sqlExecuteProxy.Complex(new ProcOutputModel{}); // 推荐方式
// 实体仓储方式
ProcedureOutputResult result = _personRepository.SqlProcedureOutput("proc_Complex", new ProcOutputModel{});
// IRepository 非泛型方式
ProcedureOutputResult result = _repository.Sql().SqlProcedureOutput("proc_Complex", new ProcOutputModel{});
// 变态懒人方式,直接通过存储过程名执行
ProcedureOutputResult result = "proc_Complex".SqlProcedureOutput(new ProcOutputModel{});
// 获取 OUTPUT 参数值
var outputs = result.OutputValues;
// 获取 RETURN 返回值
var reval = result.ReturnValue;
// 获取返回结果集
var dataSet = result.Result;
  • Tuple<T1,...T8> 方式
// ISqlRepository 方法
ProcedureOutputResult<(List<Test>, List<Test>)> result = _sqlRepository.SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});
// ISqlDispatchProxy 方式
ProcedureOutputResult<(List<Test>, List<Test>)> result = _sqlExecuteProxy.Complex(new ProcOutputModel{}); // 推荐方式
// 实体仓储方式
ProcedureOutputResult<(List<Test>, List<Test>)> result = _personRepository.SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});
// IRepository 非泛型方式
ProcedureOutputResult<(List<Test>, List<Test>)> result = _repository.Sql().SqlProcedureOutput<(List<Test>, List<Test>)>("proc_Complex", new ProcOutputModel{});
// 变态懒人方式,直接通过存储过程名执行
ProcedureOutputResult<(List<Test>, List<Test>)> result = "proc_Complex".SqlProcedureOutput<(List<Test>, List<Test>)>(new ProcOutputModel{});
// 获取 OUTPUT 参数值
var outputs = result.OutputValues;
// 获取 RETURN 返回值
var reval = result.ReturnValue;
// 获取返回结果集
var (list1,list2) = result.Result;
关于异步

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

9.13.3 关于 [DbParameter]#

[DbParameter] 特性是用来标注 Sql函数存储过程 参数的,可配置属性:

  • Direction:设置参数方向,ParameterDirection 枚举类型,默认 ParameterDirection.Input
  • DbType:设置参数类型,DbType 枚举类型,无默认
  • Size:设置参数长度的,int 类型

其中 Direction 属性是默认构造函数参数。

9.13.4 关于 ProcedureOutputResult#

ProcedureOutputResultProcedureOutputResult<TResult> 是复杂存储过程执行返回模型类,有以下属性:

  • OutputValues:多个输出值,ProcedureOutputValue 类型
  • ReturnValue:返回值,object 类型
  • Result:结果集,非泛型版本是 DataSet类型,否则是 泛型类型

9.13.5 存储过程参数#

所有 sql 参数都支持四种方式:

  • DbParameter[]:数组类型
  • new {}:匿名类型
  • new Class{}:强类型类型(支持复杂存储过程参数)
  • Dictionary<string,object> 类型

9.13.6 反馈与建议#

与我们交流

给 Furion 提 Issue