如何让ODP.NET从Oracle存储过程中的out参数返回User Defined Type的空值?

时间:2015-06-25 11:13:09

标签: c# oracle stored-procedures odp.net

关于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;
}

2 个答案:

答案 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; }