概要:
1,说明如何使用Entity Framework中的function import功能。
2,说明如何使用ODP.NET的隐式REF CURSOR绑定(implicit REF CURSOR binding)。
环境以及工具:
Windows 10 企业版
Microsoft Visual Studio Enterprise 2015
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0
.NET Framework 4.0
ODP.NET(Nuget Package Id: Oracle.ManagedDataAccess; Nuget Package Version: 12.1.2400)
Entity Framework(Nuget Package Id: EntityFramework; Nuget Package Version: 5.0.0)
其他要求:Oracle数据库中的HR schema访问权限。
1,创建一个Winform项目名称为FunctionImportTest,.NET版本是4.0。
2, 打开Nuget Package Manager控制台。
依次执行如下两个命令分别安装EF和ODP.NET的包:
install-package -id EntityFramework -version 5.0.0 -projectname FunctionImportTest
install-package -id Oracle.ManagedDataAccess -projectname FunctionImportTest
3,在Oracle数据库中的HR方案里面创建一个存储过程。
此存储过程有一个类型为隐式游标变量的出参。
CREATE OR REPLACE PROCEDURE "HR"."PROC_GET_EMP_BY_DEPT_NAME"
(
DEPT_NAME IN VARCHAR2 DEFAULT NULL,
CUR_EMPS OUT SYS_REFCURSOR
) AS
SQL_STMT VARCHAR2(256) := 'SELECT t1.first_name, t1.last_name, t2.department_name FROM employees t1 ' ||
'JOIN departments t2 ON t1.department_id = t2.department_id ';
BEGIN
IF DEPT_NAME IS NOT NULL THEN
SQL_STMT := SQL_STMT || 'WHERE t2.department_name = ' || '''' || DEPT_NAME || '''';
END IF; OPEN CUR_EMPS FOR SQL_STMT;
END "PROC_GET_EMP_BY_DEPT_NAME";
4,从数据库生成概念模型
(1),在项目名称上点击右键,选择Add-->Add Item。选择ADO.NET Entity Data Model,并把Name设置为HRModel,然后点击Add。
(2),选择从数据库生成模型,点击NEXT。
(3),选择数据库连接。本文使用Oracle数据库中的HR方案。
(4),选择Entity Framework版本。
(5),选择数据库对象及设置。选择上文已经创建的HR.PROC_GET_EMP_BY_DEPT_NAME存储过程,并点击FINISH。
5,此时已经导入了Oracle中的存储过程。然后把存储过程的出参CUR_EMPS映射为具体的类。
导入之后的样子如下图所示:
6,配置app.config文件,设置存储过程的隐式游标参数的元数据信息。
以下是app.config文件中oracle.manageddataaccess.client部分的配置。在设置属性值时,VS基本都有提示。
在implicitRefCursor元素中配置隐式游标变量的绑定信息和元数据。
<oracle.manageddataaccess.client>
<version number="*">
<dataSources>
<dataSource alias="SampleDataSource" descriptor="(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=ORCL))) " />
</dataSources>
<implicitRefCursor>
<!--
注意:方案名称和存储过程的名称是大小写敏感的。
如果要保留方案名称和存储过程名称中的小写字母,则添加"。
例如<storedProcedure schema=""SchemaName"" name=""StoredProcedureName"">
否则,EF框架会默认把这些名称转换为大写形式。
-->
<storedProcedure schema="HR" name="PROC_GET_EMP_BY_DEPT_NAME">
<!-- 游标参数的名称是大小写敏感的。 -->
<refCursor name="CUR_EMPS">
<bindInfo mode="Output"/>
<!-- 参数的序数从0开始 -->
<metadata columnName="FIRST_NAME" columnOrdinal="0" columnSize="20" nativeDataType="Varchar2" providerType="Varchar2" providerDBType="String" dataType="System.String" />
<metadata columnName="LAST_NAME" columnOrdinal="1" columnSize="25" nativeDataType="Varchar2" providerType="Varchar2" providerDBType="String" dataType="System.String" />
<metadata columnName="DEPARTMENT_NAME" columnOrdinal="2" columnSize="30" nativeDataType="Varchar2" providerType="Varchar2" providerDBType="String" dataType="System.String" />
</refCursor>
</storedProcedure>
</implicitRefCursor>
</version>
</oracle.manageddataaccess.client>
7,把函数导入映射为类型。
(1),修改function import。切换到Model Browser窗口。在Function Imports下面刚才导入的存储过程名称上点击右键,然后点击Edit。
(2),修改function import,并生成Complext Type。
① 修改Function Import Name。
② 选择Returns a Collection Of下面的Complex。
③ 点击Get Column Information。
④ 点击Create New Complex Type。
⑤ 把Complex Type的名称修改为EmployeeBasicInfo。
⑥ 点击OK。
此时,Complex Types下面多了一个类型。
8,剩下的部分就是简单的Winform开发了。调用GetEmpByDeptName方法获取数据,然后和DataGridView控件绑定,把数据显示出来。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms; namespace FunctionImportTest
{
public partial class FunctionImportForm : Form
{
// 先创建DbContext
private HREntities context = new HREntities(); public FunctionImportForm()
{
InitializeComponent();
} private void OnLoad(object sender, EventArgs e)
{
try
{
this.Query(null);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
} private void OnClickQueryButton(object sender, EventArgs e)
{
try
{
string deptName = this.textBoxDeptNameCondition.Text;
this.Query("".Equals(deptName) ? null : deptName);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
} private void Query(string deptName)
{
this.dataGridViewEmpBasicInfo.DataSource = context.GetEmpByDeptName(deptName);
}
}
}