【问题标题】:ORM style mapping of Oracle Stored Procedures with .NetOracle 存储过程与 .Net 的 ORM 样式映射
【发布时间】:2011-11-03 05:13:54
【问题描述】:

我正在寻找某种工具来帮助从 .Net 代码调用 Oracle 存储过程。我们有一个大型遗留数据库,坦率地说有点混乱(没有 id 字段、大型复合键和重复数据)。目前,我们必须通过存储过程通过一个旧且有缺陷的自定义库进行所有数据访问,我想替换它。

我对像 nHibernate 这样的 ORM 工具有一些经验,但是在我们的环境中使用它之后,它似乎并不是使用这样的遗留数据库的最佳选择。

有没有人知道一个好工具,可以轻松调用存储过程并将结果映射到对象的集合/集合中?一个不错的好处是能够处理连接事务。

谢谢

【问题讨论】:

    标签: .net database oracle orm


    【解决方案1】:

    如果商业库是一个选项,我们对 Devart 非常满意(请参阅 http://www.devart.com/dotconnect/oracle/features.html)...它们支持 LINQ 和 PLINQ 以及 EF 和存储过程、REF 游标等 - 从 Oracle 7.3 到 11g / .NET 2及以上 / 32 + 64 位...

    没有附属关系,只是一个快乐的客户......

    【讨论】:

    • 不幸的是,现在商业图书馆不是一个选择,尽管它看起来很棒。
    【解决方案2】:

    新的 Oracle beta 实体框架驱动程序让您可以做到这一点。您可以将 SP 映射到模型中,或者映射到实体(如果它们返回表的等价物)或创建一个“复杂类型”,这是一个围绕 SP 返回的类。

    我不知道你调用了多少个 SP,但对于我尝试过的那些,它已经解决了。

    另一种选择是编写您自己的库,该库只调用过程并将结果作为 .net 类返回,但这需要您在重复代码方面进行大量设置工作(将参数映射到 Oracle 中的过程)乏味的真快)。

    edit - 这是一个使用存储过程的配置文件条目,其中结果来自作为 OUT 参数的游标。

      <oracle.dataaccess.client>
        <settings>
          <add name="ENVMSTR.P_ORG_UNIT_R_BY_STAFF.RefCursor.RESULT_CURSOR_P" value="implicitRefCursor bindinfo='mode=Output'" />
        </settings>
      </oracle.dataaccess.client>
    

    编辑 2 - 以及有问题的存储过程:

    create or replace
    PROCEDURE                 P_ORG_UNIT_R_BY_STAFF 
    (
      STAFF_ID_P IN NUMBER
    , RESULT_CURSOR_P OUT SYS_REFCURSOR  
    ) AS 
    BEGIN
      OPEN RESULT_CURSOR_P FOR
        select *
          from dept_organizational_unit
          start with deptorgunit_cd = (select deptorgunit_cd from staff where staff_id = STAFF_ID_P)
          connect by prior deptorgunit_parent_cd = deptorgunit_cd;
    END P_ORG_UNIT_R_BY_STAFF;
    

    【讨论】:

    • 我实际上已经玩过这个并且在映射工作时遇到了一些问题。当我尝试“添加函数导入”并单击“获取列信息”时,我收到错误“所选存储过程不返回任何列”但它返回一个游标,我可以通过常规 ado.net 调用它。
    • @zaq 您需要在 web.config/app.config 中添加一些内容以使存储过程正常工作。您应该阅读自述文件以获取信息。对于我正在使用的其中一个,我将使用必要的配置更新答案。
    • 它仍然不适合我。我在 Oracle 论坛上找到了一些文档,指出游标返回的每一列都需要在 web.config 中定义。如果是这样的话,那么它就像在代码中映射参数一样乏味。我希望他们在生产版本中有所改进。
    • @zaq 它这么说,但我不必为我的。不太清楚为什么。但是,是的,我真的希望他们能够为这些东西需要额外的配置条目做点什么。
    • 嗯,很高兴您不必这样做。你有机会发布你的存储过程是什么样的吗?
    【解决方案3】:

    如果您只想通过参数化和物化(数据到对象)处理 SPROC,dapper-dot-net 很简单,极简,并且应该可以在 Oracle 上正常工作;例如:

    var user = cnn.Query<User>("spGetUser", new {Id = 1}, 
        commandType: CommandType.StoredProcedure).First();
    

    积分:

    • spGetUser 是存储过程的名称
    • 它通过commandType作为存储过程调用
    • 参数是从传入的对象推导出来的;在这种情况下,假设有一个名为Id的参数接受一个整数,并传入1的值
    • 应用直接的列到属性映射,为返回的每一行构造一个 User 对象
    • 在这种情况下,我们还使用 LINQ-to-Objects 来简单地说明读取 1 行

    请注意,还支持映射多个数据网格和水平分区(到相关图中的不同对象)。

    【讨论】:

    • 我认为我们需要为 oracle 以及 : param thingy 进行特殊配置
    • 看起来很有趣,我可能会研究一下这个包。
    • Dapper 不支持 Oracle Ref Cursors,你已经被警告过了。有关详细信息,请参阅 Sams 答案stackoverflow.com/questions/7390015/…
    【解决方案4】:

    对于那些试图让实体框架函数导入与 Oracle 一起工作的人,您可以使用以下演练: http://www.oracle.com/webfolder/technetwork/tutorials/obe/db/dotnet/EntityFrameworkOBE/EntityFrameworkOBE.htm

    我还写了一篇杂志文章,其中包括一个演练: http://www.oracle.com/technetwork/issue-archive/2011/11-sep/o51odt-453447.html

    注意:在发布这篇文章时,由于 beta3 中 app.config 关键字的一些变化,这些演练不起作用。请参阅 beta3 的 ODP.NET 目录中的自述文件。

    已更改的影响遍历的关键字可以修改如下:

    NATIVE_DATA_TYPE 到 NATIVEDATATYPE

    PROVIDER_DB_TYPE 到 PROVIDERDBTYPE

    请参阅 ODT 目录中的自述文件以了解有关此的其他警告。在(未来的)生产版本(在发布本文时不可用)中,Oracle Developer Tools for Visual Studio 在线帮助有一个名为“使用实体框架”的部分。本节包含有关映射到存储过程和函数的注意事项。请阅读此文档。

    有关此 app.config 元数据格式的详细信息,请参阅“隐式 REF CURSOR 绑定支持”部分中的 ODP.NET 联机帮助。

    请注意,仅当您使用复杂类型作为结果进行映射时,才需要此详细的 app.config 元数据。如果你返回一个实体,你就不需要它了。

    不幸的是,修改 app.config 的过程充满了出错的可能性。任何错误都将导致导入函数向导上的“获取列信息”按钮不执行任何操作。我们已经意识到这一点,并计划在未来的版本中使用配置工具。

    克里斯蒂安·谢伊

    甲骨文

    【讨论】:

      【解决方案5】:

      您最好为数据访问层使用代码生成器。写它很容易。它甚至可以生成 CRUD PL/SQL。

      这里有一个小例子:

      Private Sub GeneraCapaAccesoDato(ByVal tipo As Integer, ByVal modo As String)
          Dim sb As New StringBuilder()
          Dim Tabla As String = lbTabla.SelectedValue
          Dim dt As DataTable = RecuperarDatosTabla(Tabla)
          sb.Append(String.Format("public int {1}(OracleConnection con, BE{0} oBE{0})", Tabla, modo))
          sb.AppendLine("{")
          sb.AppendLine("int Resultado;")
          sb.AppendLine(String.Format("OracleCommand cmd = new OracleCommand(""Pa_{0}_{1}"", con);", Tabla, modo))
          sb.AppendLine("cmd.CommandType = CommandType.StoredProcedure;")
          sb.AppendLine("")
          Dim i As Integer
          Row = dt.Select()
          Dim NomTabla As String
          Dim Tamaño As Integer
          Dim scala As Integer
          Dim TipoDato As String = "Ninguno"
          Dim precision As Integer
          Dim aux As Object
          Dim aux1 As Object
          Dim llave As Integer
          For i = tipo To Row.Count() - 1
              llave = Int32.Parse(Row(i).Item(7))
              NomTabla = Row(i).Item(0).ToString()
              Tamaño = Integer.Parse(Row(i).Item(2).ToString())
              aux = Row(i).Item(4).ToString()
              scala = Integer.Parse(If(aux = "", 0, aux))
      
              aux1 = Row(i).Item(3).ToString()
              precision = Integer.Parse(If(aux1 = "", 0, aux1))
      
              If scala > 0 Then
                  If scala >= 0 And scala <= 15 Then
                      TipoDato = "OracleDbType.Double"
                  End If
              ElseIf Row(i).Item(1).ToString() = "NUMBER" Then
                  If precision < 2 Then
                      TipoDato = "OracleDbType.Int16"
                  ElseIf precision >= 2 And precision <= 9 Then
                      TipoDato = "OracleDbType.Int32"
                  ElseIf precision >= 10 And precision <= 18 Then
                      TipoDato = "OracleDbType.Int64"
                  End If
              Else
                  If Row(i).Item(1).ToString() = "DATE" Then
                      TipoDato = "OracleDbType.Date "
                  End If
                  If Row(i).Item(1).ToString() = "VARCHAR2" Then
                      TipoDato = "OracleDbType.Varchar2  "
                  End If
                  If Row(i).Item(1).ToString() = "CHAR" Then
                      TipoDato = "OracleDbType.Char "
                  End If
      
              End If
              sb.AppendLine(String.Format("OracleParameter Par{1} = cmd.Parameters.Add(""P_{0}"",{2});", _
                                          NomTabla, If(tipo = 0, i + 1, i), TipoDato))
      
              If Row(i).Item(1).ToString() = "VARCHAR2" Or Row(i).Item(1).ToString() = "CHAR" Then
                  sb.AppendLine(String.Format("Par{1}.Size = {0};", Tamaño, If(tipo = 0, i + 1, i)))
              End If
      
              sb.AppendLine(String.Format("Par{0}.Direction = ParameterDirection.Input;", If(tipo = 0, i + 1, i)))
              sb.AppendLine(String.Format("Par{0}.Value = oBE{2}.{1};", If(tipo = 0, i + 1, i), NomTabla, Tabla))
              sb.AppendLine("")
              TipoDato = ""
      
          Next
          sb.AppendLine("Resultado = cmd.ExecuteNonQuery();")
          sb.AppendLine("return Resultado;")
          sb.AppendLine("}")
          rtbVisor.Text = sb.ToString()
      End Sub
      Private Function RecuperarDatosTabla(ByVal NombreTabla As String) As DataTable
          sb = New StringBuilder
          sb.Append(" select COLUMN_NAME, DATA_TYPE, DATA_LENGTH, DATA_PRECISION, DATA_SCALE, NULLABLE, DATA_DEFAULT ,column_id")
          sb.Append(" from(USER_TAB_COLUMNS)")
          sb.Append(String.Format(" where TABLE_NAME = '{0}' order by table_name,column_id ", NombreTabla))
          Using con As New OracleConnection(strConexion)
              con.Open()
              dt = New DataTable
              Dim da As OracleDataAdapter = New OracleDataAdapter(sb.ToString(), con)
              da.Fill(dt)
          End Using
          Return dt
      End Function
      

      【讨论】:

        猜你喜欢
        • 2017-04-23
        • 2017-08-26
        • 1970-01-01
        • 2021-12-05
        • 1970-01-01
        • 2010-10-19
        • 1970-01-01
        • 1970-01-01
        • 2010-09-10
        相关资源
        最近更新 更多