自從我得了一種"不用LINQ就不會寫資料庫程式"的病,為了滿足工作上要搭配Oracle(雖然我也是百般不願意)的需求,在官方遲遲不支援Entity Framework的情況下,只好求助於3rd Party產品

去年(2011)三月,Oracle總算良心發現,推出了ODAC Entity Framework Beta版,但這一杯塔就杯了近一年,遲遲等不到正式版問市。Beta版意味著"玩玩還可以,出包沒人理",無法真的推到實務專案中,還是沒解決問題。

終於,Oracle釋出ODAC 11.2 Release 4 Production Released with Entity Framework and LINQ Support!! 二話不說,立刻下載安裝。

安裝過程跟Oracle Client類似,在此不多著墨,值得一提的是發現了新東西,Oracle Database Extensions for .NET,其概念很像SQLCLR,意味著可用.NET寫Store Procedure、User Defined Function,並可在PL/SQL中直接引用呼叫。想到一堆用PL/SQL笨拙語法寫成的複雜Procedure可以用C#改寫,乍想之下令人頗為興奮,但很快就回歸現實: 第一,Database Extensions for .NET只適用於Oracle Database for Microsoft Windows,若規劃時大量採用,而實務環境資料庫卻都安裝在Unix平台就糗大了;第二,依架構來看,.NET寫的Procedure與Function被放在另一個獨立Process中執行,若涉及大量資料運算時,難免會有跨Process的資料傳輸,有效能上的疑慮。(這部分跟SQLCLR的特性限制類似) 因此在應用上有其限制,必須嚴選情境使用。

本想順便看看Database Extensions for .NET,但踢到了小鐵板! 由於在Oracle Server主機也要安裝ODAC(上圖紅框選項,Oracle Server主機選下方,其餘主機要選擇上方的for Oracle Client選項),安裝在手邊的Oracle Database Express Edition 11g Release 2 (簡稱Oracle XE)時,爆出了版本必須為11.2.0.3的訊息,目前Oracle尚未提供11.2.0.3版XE,且即便是Oracle企業版,也得透過客服支援才能取得Patch升級到11.2.0.3版。再看了文件,可行的做法應是在Oracle XE上安裝11.1.0.6.20以後的ODAC,不必裝到11.2.0.3... 不過你知道的,我對Oracle的愛並沒有旺盛到想克服一切難關嚐鮮,所以... 以後再試囉~~ (關於Database Extensions for .NET,Oracle有篇不錯的逐步教學,值得一看)

回到主題,該來看看在Oracle Entity Framework推出後,Oracle資料庫相關程式的開發方式將有什麼改變?

傳統上.NET開發者在寫Oracle應用程式時,習慣會用Toad、PL/SQL Developer等工具查看資料表、Procedure等資訊,並在其中執行語法測試確認結果後,在Visual Studio中透過ODP.NET或System.Data.OracleClient建立OracleConnection、執行OracleCommand完成資料庫存取作業。

11.2.0.3版除了支援EF,還內附Oracle Developer Tools for Visual Studio(ODT),它也將改變傳統開發方式,透過與Visual Studio緊密結合,讓你可以直接在Visual Studio內完成許多資料庫相關工作,例如: 修改Schema、分析程式效能、測試及偵錯PL/SQL指令、管理User/Role/Privilege、將.NET類別轉成User-Defined Type、由Excel/SQL匯入資料表到Oracle... 等等。MSDN上有段簡介影片,看過之後,我想蟾蜍(Toad)的臉會綠掉,牠的飯碗被搶了! 原本需要第三方工具才能完成的事,現在可在Visual Studio直接搞定,用得還是熟悉的Visual Studio UI,帥呀~~

Oracle有篇詳盡的Entity Framework逐步教學,但對於未接觸過EF、LINQ的人稍稍複雜了點,故我用個更簡單的範例來展示"開發.NET Oracle應用程式的新時代做法"。在展示中,我們要在Oracle上新增一個資料表(Player),並寫一小段程式在其中新增一筆資料,再把它查詢出來。

1.請先安裝好ODAC 11.0.2.3 with Oracle Developer Tools for Visual Studio

2.建立一個.NET 4.0 Console Application專案

3.開啟Server Explorer

4.建立新連線,Data source選"Oracle Database (Oracle ODP.NET)",輸入識別名稱、帳號、密碼

5.連線建立後,可檢視Table/View/Procedure/Function (蟾蜍: 鳴~ 這原來是我的工作耶!),
   新增PLAYER Table,新增欄位、選取資料型別...

6.也可指定Primary Key、Constraint、Index,按下Preview SQL可檢視CREATE TABLE Script,按下Save即建立資料表

7.在專案中新增ADO.NET Entity Data Model

8.選擇由現有資料表產生Model

9.選擇剛才建立的連線(JEFF.XE),並指定將連線字串存入Config中(在實務上連線字串應使用加密方式保存,正式上線的系統務必要留意這一點)

10.選取我們要為哪一個Table、View、Procedure建立Model類別,在這裡只有一個Table PLAYER
    (Pluralize or singularize generated object names將決定PLAYER集合物件是否要寫成PLAYERs)

11.在建立好的Model(.edmx)中可以看到PLAYER Table

12.接下來是愉快的動手寫程式時間

程式很簡單,建立一個EF的Context(JeffEntities),宣告一個全新的PLAYER物件,指定各屬性(資料庫的每一個欄位都被映對到.NET類別中的一個屬性,奶油桂花手們再也不用擔心敲錯名稱抓蟲抓半天囉),將物件加入集合(ctx.PLAYER.AddObject),再ctx.SubmitChages()就會新增至資料庫!

至於查詢,此處則用Single(o => o.ID == 1)查詢剛才塞入的資料,ODP.NET會幫忙將其轉換成SELECT * FROM PLAYER WHERE ID = 1送到Oracle執行,查詢結果則會被轉成PLAYER類別,接著將它的NAME顯示出來證明查詢成功。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace OracleEF
{
    class Program
    {
        static void Main(string[] args)
        {
            using (JeffEntities ctx = new JeffEntities())
            {
                //新增一筆資料
                PLAYER p = new PLAYER()
                {
                    ID = 1,
                    NAME = "Jeffrey",
                    REGDATE = new DateTime(2012, 4, 1),
                    SCORE = 32767
                };
                ctx.PLAYER.AddObject(p);
                ctx.SaveChanges();
                //查詢資料
                var res = ctx.PLAYER.Single(o => o.ID == 1);
                Console.WriteLine(res.NAME);
            }
            Console.Read();
        }
    }
}

執行結果如下,順便用PL/SQL Developer驗證資料真的在資料庫中。

還在用Toad、OracleConnection、OracleCommand寫.NET程式嗎? 從現在起,試試Oracle Developer Tools + Entity Framework的新選擇吧!


Comments

# by Alex Lee

從噗浪看到MSDN 機器人看到的 心想 這對黑大應該是個好消習... 殊不知......

# by sophieQ

黑暗大大, 您有試過ODAC 11.0.2.3 + EF4 做「iteratively adding objects 」的動作嗎? 也就是msdn論壇中這篇文章的問題,我覺得提問的人提得很好,但回文的版主整個狀況外Orz。 提問的人特別說到 「My suspicion is that it has something to do with the temporary entitykey that gets set prior to the actual insert (which is equal to 0), but I don't know how to confirm that, nor do I know how to resolve it.」 版主貼的link,我也看過了,我相信貼文的人也看過了,才會問「My suspicion is that」 而且我覺得這個問題很重要。 http://social.msdn.microsoft.com/Forums/eu/adodotnetentityframework/thread/02b2b7eb-d91c-474a-94a6-a945defcbc76?prof=required

# by SophieQ

SORRY, 我很少用留言版,不太會用,我一直以為我留失敗了,對不起貼這麼多次

# by SophieQ

hey, 我解了 ,但不知這樣是不是正解。 我先detech,然後用StoreWins方式用datasource中的值取代object context中的值。 context.SaveChanges(SaveOptions.DetectChangesBeforeSave); context.Refresh(RefreshMode.StoreWins, context.entity);

# by Jeffrey

to SophieQ, 你的情境是否與討論區文章類似? 有兩個Table(A & B),TableB的Primary Key為自動跳號,有Foreign Key關聯至TableA,程式針對TableA的查詢結果跑迴圈,在連續新增資料到TableB時出問題?

# by SophieQ

是的,好像跟oracle PK,是用trigger + sequence 有關. 我剛剛post方式是ok的, 如果是用 context.SaveChanges(); 可以寫入db,但是會無法update objectDataSource. 然後掛掉。 我不清楚為何無法update objectDataSource.我不了解原理是什麼,僅管我已經看了多篇的msdn。

# by SophieQ

所以也不禁想問 My suspicion is that it has something to do with the temporary entitykey that gets set prior to the actual insert (which is equal to 0), but I don't know how to confirm that, nor do I know how to resolve it

# by SophieQ

喔喔 對了,Table a 連續新增也會有問題

Post a comment