Reputation: 173
I am having some trouble with Odp.net as regards the UDT (User Defined Type) behavior.
My problem arises when I have a procedure containing an OUT parameter that returns a specific UDT.
When I return an instantiated UDT for the OUT parameter there is no problem.
When I return a NULL value for the OUT parameter I get a NullReference error:
A first chance exception of type 'System.NullReferenceException' occurred in Oracle.DataAccess.dll
I have tried setting IsNullable = True on the relevant OracleCommand parameter without any success.
I have no problem sending and receiving rather complex UDT's, e.g. UDT's with nested UDT's and collections of objects and UDT's witgh nested collections of objects.
Any ideas if this can be solved using ODP.NET other than making the Oracle procedure return an instance of the object type?
UPDATE - SOLVED:
The issue was that the UDT type with its nested UDT type was not correctly initialized to null. Using the auto generated code solved the problem.Using Oracle User-Defined Types with .NET and Visual Studio
Christian Shay, thank you for resolving the issue - the auto generated code is perhaps a better choice than implementing base classes to handle most of the behavior, although this is possible.
The Oracle stored procedure signature is:
PROCEDURE CREATE_DEFINITIONS_FOR_GROUP(
P_GRP_NO IN NUMBER
,P_DATE IN DATE
,P_ERROR_CODE OUT MYSCHEMA.ERROR_CODE);
I call this code in C# using ODP.NET after opening a connection:
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);
}
}
The UDT type is defined as a valid UDT type in .NET like this:
public class DbErrorCode : TypeTemplate
{
[OracleObjectMapping("ERROR_CODE")]
public decimal Code { get; set; }
[OracleObjectMapping("DESCRIPTION")]
public string Description { get; set; }
}
The base TypeTemplate class is defined like this:
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
}
The method in OracleParameterFactory for the UDT parameter is as follows (exception handling removed from the code to present as clean code as possible - the error produced does not come from the exception handling):
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;
}
Upvotes: 0
Views: 2098
Reputation: 173
Problem solved: The issue was that the UDT type with its nested UDT type was not correctly initialized to null. Using the auto generated code solved the problem.Using Oracle User-Defined Types with .NET and Visual Studio
Christian Shay, thank you for resolving the issue - the auto generated code is perhaps a better choice than implementing base classes to handle most of the behavior, although this is possible.
Upvotes: 1
Reputation: 1
I am not certain on this, but my experience is that "oCmd.ExecuteNonQuery()" is likely returning null and needs to be returned to an object and you can then check if it is null and return and empty UDT or the on returned. Try
TypeTemplate udtOnbj = new TypeTemplate();
object testObj = oCmd.ExecuteNonQuery();
if ( testObj == null ) { return udtObj; } else { return testObj; }
Upvotes: 0