使用带有输出参数的实体框架调用 Oracle 存储过程?
Posted
技术标签:
【中文标题】使用带有输出参数的实体框架调用 Oracle 存储过程?【英文标题】:Calling Oracle stored procedure using Entity Framework with output parameter? 【发布时间】:2016-12-26 12:53:00 【问题描述】:我有一个简单的 Oracle 存储过程,它传入三个参数,并有一个输出参数:
CREATE OR REPLACE PROCEDURE RA.RA_REGISTERASSET
(
INPROJECTNAME IN VARCHAR2
,INCOUNTRYCODE IN VARCHAR2
,INLOCATION IN VARCHAR2
,OUTASSETREGISTERED OUT VARCHAR2
)
AS
BEGIN
SELECT
INPROJECTNAME || ', ' || INLOCATION || ', ' || INCOUNTRYCODE
INTO
OUTASSETREGISTERED
FROM
DUAL;
END RA_REGISTERASSET;
我正在尝试使用 Entity Framework 6.1 来取回 OutAssetRegistered
值,但是,在调用 SqlQuery
后我得到一个空值,无一例外:
public class CmdRegisterAssetDto
public string inProjectName get; set;
public string inCountryCode get; set;
public string inLocation get; set;
public string OutAssetRegistered get; set;
//--------------------------------------------- ---------------
string projectName = "EXCO";
string location = "ANYWHERE";
string countryCode = "XX";
using (var ctx = new RAContext())
var projectNameParam = new OracleParameter("inProjectName", OracleDbType.Varchar2, projectName, ParameterDirection.Input);
var countryCodeParam = new OracleParameter("inCountryCode", OracleDbType.Varchar2, countryCode, ParameterDirection.Input);
var locationParam = new OracleParameter("inLocation", OracleDbType.Varchar2, location, ParameterDirection.Input);
var assetRegisteredParam = new OracleParameter("OutAssetRegistered", OracleDbType.Varchar2, ParameterDirection.Output);
var sql = "BEGIN RA.RA_RegisterAsset(:inProjectName, :inCountryCode, :inLocation, :OutAssetRegistered); END;";
var query = ctx.Database.SqlQuery<CmdRegisterAssetDto>(sql, projectNameParam, countryCodeParam, locationParam, assetRegisteredParam
);
assetRegistered = (string)assetRegisteredParam.Value;
我一直在努力让这个工作无济于事,检查了不同的博客,所有其他的 crud 操作都有效,谁能帮助并指导我哪里出错了?
【问题讨论】:
我有类似的要求,你能解决吗? 我遇到了同样的问题,如果您找到了解决方案,请告诉我。 【参考方案1】:在这种情况下,你不应该打电话:
var query = ctx.Database.SqlQuery<CmdRegisterAssetDto>(sql, projectNameParam, countryCodeParam, locationParam, assetRegisteredParam);
而是调用:
var result = ctx.Database.ExecuteSqlCommand(sql, projectNameParam, countryCodeParam, locationParam, assetRegisteredParam);
请注意,唯一有效的区别是 SqlQuery<CmdRegisterAssetDto>
被替换为 ExecuteSqlCommand
。这也意味着 DTO 是不必要的。否则,您的代码看起来应该可以工作。这是您的完整原始代码以及我提到的更改:
string projectName = "EXCO";
string location = "ANYWHERE";
string countryCode = "XX";
using (var ctx = new RAContext())
var projectNameParam = new OracleParameter("inProjectName", OracleDbType.Varchar2, projectName, ParameterDirection.Input);
var countryCodeParam = new OracleParameter("inCountryCode", OracleDbType.Varchar2, countryCode, ParameterDirection.Input);
var locationParam = new OracleParameter("inLocation", OracleDbType.Varchar2, location, ParameterDirection.Input);
var assetRegisteredParam = new OracleParameter("OutAssetRegistered", OracleDbType.Varchar2, ParameterDirection.Output);
var sql = "BEGIN RA.RA_RegisterAsset(:inProjectName, :inCountryCode, :inLocation, :OutAssetRegistered); END;";
var result = ctx.Database.ExecuteSqlCommand(sql, projectNameParam, countryCodeParam, locationParam, assetRegisteredParam);
assetRegistered = (string)assetRegisteredParam.Value;
为了证明我的理论,我复制了您遇到的空行为,然后进行了一次更改。它挂了一会儿(可能是为了让 EF 启动),但此后每次都快速执行。在每种情况下,我都在 out 参数中找到了一个等待值。
如果有人遇到了麻烦,有一个简单的变体可以为您处理脚本细节:
string projectName = "EXCO";
string location = "ANYWHERE";
string countryCode = "XX";
using (var ctx = new RAContext())
using (var cmd = ctx.Database.Connection.CreateCommand())
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "RA.RA_REGISTERASSET";
var projectNameParam = new OracleParameter("inProjectName", OracleDbType.Varchar2, projectName, ParameterDirection.Input);
var countryCodeParam = new OracleParameter("inCountryCode", OracleDbType.Varchar2, countryCode, ParameterDirection.Input);
var locationParam = new OracleParameter("inLocation", OracleDbType.Varchar2, location, ParameterDirection.Input);
var assetRegisteredParam = new OracleParameter("OutAssetRegistered", OracleDbType.Varchar2, ParameterDirection.Output);
cmd.Parameters.AddRange(new[] projectNameParam, countryCodeParam, locationParam, assetRegisteredParam );
cmd.Connection.Open();
var result = cmd.ExecuteNonQuery();
cmd.Connection.Close();
assetRegistered = (string)assetRegisteredParam.Value;
作为事后的想法,如果您在之后立即调用查询(即query.FirstOrDefault()
),从技术上讲,您可以使用原始解决方案。查询的返回值将始终为 null,但您的 out 参数至少会被填充。这是因为 EF 查询使用延迟执行。
【讨论】:
【参考方案2】:如果您需要传入和/或传出日期:
public DateTime GetDate(string dateIn)
Regex regex = new Regex(@"(\d4-\d2-\d2T\d2:\d2:\d2Z)");
if (!regex.IsMatch(dateIn))
throw new ArgumentException($"dateIn must be given in the YYYY-MM-DD\"T\"hh24:mi:ss\"Z\" format. " +
$"Value given: dateIn");
OracleParameter dateInParam = new OracleParameter("DATE_IN_", dateIn);
createdParam.OracleDbType = OracleDbType.TimeStampTZ;
OracleParameter dateOutParam= new OracleParameter("DATE_OUT_", OracleDbType.Date, System.Data.ParameterDirection.Output);
var sql = "BEGIN " +
$"Get_Date(:DATE_IN_, TO_TIMESTAMP_TZ('dateIn','YYYY-MM-DD\"T\"hh24:mi:ss\"Z\"'), :DATE_OUT_ ); " +
"END;";
#pragma warning disable EF1000 // Possible SQL injection vulnerability.
context.Database.ExecuteSqlCommand(sql, dateInParam, dateOutParam);
#pragma warning restore EF1000 // Possible SQL injection vulnerability.
return (DateTime)(OracleDate)dateOutParam.Value;
【讨论】:
以上是关于使用带有输出参数的实体框架调用 Oracle 存储过程?的主要内容,如果未能解决你的问题,请参考以下文章
oracle中怎么执行带有输出参数的存储过程,在程序中我知道怎么调用,
从 Java 调用带有数组输出参数的 Oracle 存储过程