Oracle и C # - массив UDT в качестве возвращаемого значения - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь, чтобы C # читал массив определенного пользователем типа PL / SQL.

Это определение каждого объекта массива:

CREATE OR REPLACE TYPE "REPORTDATUM" AS OBJECT
(
            FIELD1                                                   VARCHAR2(20),
            FIELD2                                                   VARCHAR2(3000),
            FIELD3                                                   NUMBER(15),
            FIELD4                                                   DATE,
            FIELD5                                                   DATE,
            FIELD6                                                   VARCHAR2(4000),
            FIELD7                                                   TIMESTAMP,
            FIELD8                                                   VARCHAR2(10),
            FIELD9                                                   NUMBER(35),
            FIELD10                                                  VARCHAR2(80),
            FIELD11                                                  VARCHAR2(50)
);

CREATE OR REPLACE TYPE "REPORTARRAY" IS TABLE OF REPORTDATUM;

Это подпись функции, которую я пытаюсь вызвать:

FUNCTION getReport(startTime IN DATE, endTime IN DATE) RETURN REPORTARRAY;

Теперь, основываясь на руководстве, которое я нашел здесь: http://appsjack.blogspot.com/2010/09/pass-custom-udt-types-to-oracle-stored.html

Я создал класс ReportDatum.cs следующим образом:

public class ReportDatum : INullable,  IOracleCustomType {

    private bool objectIsNull;

    [OracleObjectMappingAttribute(Constants.ReportDatum.Aliases.Field1)]
    public string Field1 { get; set; }

    [OracleObjectMappingAttribute(Constants.ReportDatum.Aliases.Field2)]
    public string Field2 { get; set; }

                            [...]

    [OracleObjectMappingAttribute(Constants.ReportDatum.Aliases.Field11)]
    public string Field11 { get; set; }


    public static ReportDatum Null {
        get {
            ReportDatum reportDatum = new ReportDatum();
            reportDatum.objectIsNull = true;
            return reportDatum;
        }
    }

    public bool IsNull {
        get { return objectIsNull; }
    }

    public void FromCustomObject(OracleConnection databaseConnection, IntPtr userDefinedTypePointer) {
        if (string.IsNullOrEmpty(this.Field1) == false ) {
            OracleUdt.SetValue(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field1, this.Field1);
        }
        if (string.IsNullOrEmpty(this.Field2) == false) {
            OracleUdt.SetValue(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field2, this.Field2);
        }
        [...]
        if (string.IsNullOrEmpty(this.Field11) == false) {
            OracleUdt.SetValue(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field11, this.Field11);
        }
    }

    public void ToCustomObject(OracleConnection databaseConnection, IntPtr userDefinedTypePointer) {
        this.Field1 = parseString(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field1);
        this.Field2 = parseString(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field2);
                                            [...]
        this.Field11 = parseString(databaseConnection, userDefinedTypePointer, Constants.ReportDatum.Aliases.Field11);
    }

    private string parseString(OracleConnection databaseConnection, IntPtr userDefinedTypePointer, string columnName) {
        string valueToReturn = String.Empty;
        try {
            valueToReturn = ( String ) OracleUdt.GetValue(databaseConnection, userDefinedTypePointer, columnName);
        }
        catch (Exception e) {
            error("Exception while attempting to parse string value of column [" + columnName + "]. Message [" + (e == null || e.Message == null ? "NULL" : e.Message) + "]", e);
        }
        return valueToReturn;
    }

 }

Я также приступил к созданию ReportDatumFactory.cs следующим образом:

public class ReportDatumFactory : IOracleCustomTypeFactory {

    public IOracleCustomType CreateObject() {
        return new ReportDatum();
    }

}

Этого, согласно приведенному выше руководству, должно быть достаточно для обработки каждого элемента, составляющего массив.

Чтобы обработать сам массив, я создал следующие классы.

ReportArray.cs

public class ReportArray : INullable, IOracleCustomType {

    [OracleArrayMapping()]
    public ReportDatum[] reportArray;

    private bool objectIsNull;

    public bool IsNull {
        get { return objectIsNull; }
    }

    public void FromCustomObject(OracleConnection databaseConnection, IntPtr userDefinedTypePointer) {
        try {
            OracleUdt.SetValue(databaseConnection, userDefinedTypePointer, 0, reportArray);
        }
        catch (Exception e) {
            error("Exception while populating OracleUdt from ReportArray.", e);
        }
    }

    public void ToCustomObject(OracleConnection databaseConnection, IntPtr userDefinedTypePointer) {
        try {
            reportArray = ( ReportDatum[] ) OracleUdt.GetValue(databaseConnection, userDefinedTypePointer, 0);
        }
        catch (Exception e) {
            error("Exception while reading values from OracleUdt related to ReportArray.", e);
        }
    }

}         

и соответствующий фабричный класс

ReportArrayFactory.cs

public class ReportArrayFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory {

    public IOracleCustomType CreateObject() {
        return new ReportArray();
    }


    public Array CreateArray(int numberOfElements) {
        return new ReportArray[numberOfElements];
    }

    public Array CreateStatusArray(int numberOfElements) {
        return null;
    }

}

Теперь, что касается метода, который вызывает хранимую процедуру, это то, что у меня есть:

Report.cs

    private void executeStoredProcedure2(OracleConnection databaseConnection, string schema, string storedProcedureName, DateTime startTime, DateTime endTime, int numberOfElements) {
        OracleCommand databaseCommand = new OracleCommand();
        ReportArray reportArray = new ReportArray();

        debug("Attempting to populate the report array");
        if (databaseConnection != null && databaseConnection.State == ConnectionState.Open) {
            try {
                databaseCommand.Connection = databaseConnection;
                databaseCommand.CommandType = CommandType.StoredProcedure;
                databaseCommand.CommandText = schema + "." + storedProcedureName;
                databaseCommand.Parameters.Add(createUserDefinedParameter(numberOfElements, reportArray));
                databaseCommand.Parameters.Add(Constants.ReportDatum.ParameterNames.startTime, OracleDbType.Date, startTime, ParameterDirection.Input);
                databaseCommand.Parameters.Add(Constants.ReportDatum.ParameterNames.endTime, OracleDbType.Date, endTime, ParameterDirection.Input);
                databaseCommand.ExecuteNonQuery();
                //How do I populate the reportArray object?
            }
            catch (Exception exception) {
                error("Exception while executing stored procedure [" + storedProcedure2 + "].", exception);
            }
            finally {
                databaseCommand.Dispose();
            }
        }
    }



    private OracleParameter createUserDefinedParameter(int size, ReportArray reportArray) {
        OracleParameter valueToReturn = null;
        try {
            valueToReturn = new OracleParameter();
            valueToReturn.ParameterName = "reportData"; //this is the name of the REPORTARRAY object in the Oracle function that is being returned.
            valueToReturn.OracleDbType = OracleDbType.Array;
            valueToReturn.Direction = ParameterDirection.ReturnValue;
            valueToReturn.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
            valueToReturn.UdtTypeName = Constants.ReportDatum.arrayDataTypeDefinition;
            valueToReturn.ArrayBindSize = new int[size];
            valueToReturn.ArrayBindStatus = new OracleParameterStatus[size];
            valueToReturn.Size = size;
            valueToReturn.Value = reportArray.reportArray;
        }
        catch (Exception e) {
            error("Exception while attempting to create user defined parameter.", e);
        }
        return valueToReturn;
    }

Проблема, с которой я столкнулся, заключается в том, что я не могу получить массив обратно в C #. Кто-нибудь сталкивался с этой проблемой? Чего мне не хватает?

У меня есть основания полагать, что код SQL работает. На данный момент проблему создает только C #.

1 Ответ

0 голосов
/ 15 ноября 2018

Возможно, что-то не так.

Во-первых, фабрикам нужны атрибуты OracleCustomTypeMapping. Без них Oracle не поймет, что эти классы фабрики отображений должны использоваться вообще:

    [OracleCustomTypeMapping("SCHEMA.REPORTDATUM")]
    public class ReportDatum ...

и

    [OracleCustomTypeMapping("SCHEMA.REPORTARRAY")]
    public class ReportArray ...

Во-вторых, в фабрике отображения массивов есть ошибка: при создании массива вы хотите создать массив из ReportDatum объектов. В данный момент вы создаете массив из ReportArray объектов, то есть массив массивов, которые вам не нужны. Другими словами, заменить

    public Array CreateArray(int numberOfElements) {
        return new ReportArray[numberOfElements];
    }

с

    public Array CreateArray(int numberOfElements) {
        return new ReportDatum[numberOfElements];
    }

В-третьих, удалить строку

        CollectionType = OracleCollectionType.PLSQLAssociativeArray,

из вашего createUserDefinedParameter метода. Возвращаемое значение не является ассоциативным массивом PL / SQL (это другой тип коллекции), поэтому это свойство не должно использоваться. Если вы используете это свойство, вы, вероятно, обнаружите, что возвращаемое значение, возвращаемое из сохраненной функции, является пустым object массивом.

Наконец, чтобы вернуть массив после вызова сохраненной функции, добавьте следующую строку после вызова к databaseCommand.ExecuteNonQuery():

    reportArray = (ReportArray)databaseCommand.Parameters["reportData"].Value;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...