关于UDT(用户定义类型)行为,我在Odp.net上遇到了一些麻烦。
当我有包含返回特定UDT的OUT参数的过程时,我的问题就出现了。
当我为OUT参数返回实例化的UDT 时,没有问题。
当我为OUT参数返回 a NULL值时,我收到NullReference错误:
类型'System.NullReferenceException'的第一次机会异常 发生在Oracle.DataAccess.dll
中
我尝试在相关的OracleCommand参数上设置IsNullable = True,但没有成功。
我发送和接收相当复杂的UDT没有问题,例如UDT包含嵌套的UDT和对象集合以及UDT嵌套的对象集合。
除了使Oracle程序返回对象类型的实例之外,是否可以使用ODP.NET解决这个问题?
更新 - 已解决:
问题是具有嵌套UDT类型的UDT类型未正确初始化为null。使用自动生成的代码解决了问题。Using Oracle User-Defined Types with .NET and Visual Studio
Christian Shay ,感谢您解决问题 - 自动生成的代码 或许比实现基类来处理更好的选择 大多数行为,虽然这是可能的。
Oracle存储过程签名是:
PROCEDURE CREATE_DEFINITIONS_FOR_GROUP(
P_GRP_NO IN NUMBER
,P_DATE IN DATE
,P_ERROR_CODE OUT MYSCHEMA.ERROR_CODE);
打开连接后,我使用ODP.NET在C#中调用此代码:
using (var oCmd = new OracleCommand
{
CommandText = "MYSCHEMA.MYPACKAGE.CREATE_DEFINITIONS_FOR_GROUP",
Connection = oConn,
CommandType = CommandType.StoredProcedure
})
{
try
{
oCmd.Parameters.Add(OracleParameterFactory.CreateInParam(
"P_GRP_NO", OracleDbType.Int64, value: groupNo));
oCmd.Parameters.Add(OracleParameterFactory.CreateInParam(
"P_DATE", OracleDbType.Date, value: dateOfGroup));
oCmd.Parameters.Add(OracleParameterFactory.CreateOutParamForUdtType(
"P_ERROR_CODE", "MYSCHEMA.ERROR_CODE"));
oCmd.ExecuteNonQuery();
var report = oCmd.Parameters["P_ERROR_CODE"].Value as DbErrorCode;
return report;
}
finally
{
CommandHelpers.DisposeParameters(oCmd);
}
}
UDT类型在.NET中定义为有效的UDT类型,如下所示:
public class DbErrorCode : TypeTemplate
{
[OracleObjectMapping("ERROR_CODE")]
public decimal Code { get; set; }
[OracleObjectMapping("DESCRIPTION")]
public string Description { get; set; }
}
基本的TypeTemplate类定义如下:
public class TypeTemplate : IOracleCustomType, INullable
{
public virtual void FromCustomObject(OracleConnection con, IntPtr pUdt)
{
foreach (var p in GetType().GetProperties())
{
// Must ignore these two properties
if (p.Name == "Null" || p.Name == "IsNull") continue;
var oracleObjectMappingAttribute = p.GetCustomAttributes(typeof(OracleObjectMappingAttribute), false)[0] as OracleObjectMappingAttribute;
if (oracleObjectMappingAttribute == null) continue;
var attributeName = oracleObjectMappingAttribute.AttributeName;
if (p.GetCustomAttributes(typeof(IgnoreAttribute), false).Length == 0)
{
if (p.GetCustomAttributes(typeof(NullableAttribute), false).Length == 0)
{
OracleUdt.SetValue(con, pUdt, attributeName, p.GetValue(this, null));
}
else
{
if (p.GetValue(this, null) != null)
{
OracleUdt.SetValue(con, pUdt, attributeName, p.GetValue(this, null));
}
}
}
}
}
public virtual void ToCustomObject(OracleConnection con, IntPtr pUdt)
{
foreach (var p in GetType().GetProperties())
{
// Must ignore these two properties
if (p.Name == "Null" || p.Name == "IsNull") continue;
var oracleObjectMappingAttribute = p.GetCustomAttributes(typeof(OracleObjectMappingAttribute), false)[0] as OracleObjectMappingAttribute;
if (oracleObjectMappingAttribute == null) continue;
var attributeName = oracleObjectMappingAttribute.AttributeName;
if (!OracleUdt.IsDBNull(con, pUdt, attributeName))
{
p.SetValue(this, OracleUdt.GetValue(con, pUdt, attributeName), null);
}
}
}
#region INullable Members
public bool IsNull { get; private set; }
public static TypeTemplate Null
{
get
{
var obj = new TypeTemplate { IsNull = true };
return obj;
}
}
#endregion
}
OracleParameterFactory中UDT参数的方法如下(从代码中删除异常处理以尽可能地呈现为干净代码 - 产生的错误不是来自异常处理):
public static OracleParameter CreateOutParamForUdtType(
string paramName, string udtName, object value, bool isNullable = false)
{
var param = new OracleParameter
{
ParameterName = paramName,
UdtTypeName = udtName.ToUpperInvariant(),
OracleDbType = OracleDbType.Object,
Direction = ParameterDirection.Output,
IsNullable = isNullable
};
if (value != null)
{
param.Value = value;
}
return param;
}
答案 0 :(得分:1)
问题已解决:问题是UDT类型及其嵌套的UDT类型未正确初始化为null。使用自动生成的代码解决了问题。使用.NET和Visual Studio的Oracle用户定义类型
Christian Shay ,感谢您解决问题 - 自动生成的代码可能是比实现基类来处理大多数行为更好的选择,尽管这是可能的。
答案 1 :(得分:0)
我不确定这一点,但我的经验是" oCmd.ExecuteNonQuery()"可能返回null并需要返回到一个对象,然后您可以检查它是否为null并返回并清空UDT或返回的on。尝试
TypeTemplate udtOnbj = new TypeTemplate();
object testObj = oCmd.ExecuteNonQuery();
if(testObj == null){return udtObj; } else {return testObj; }