Entity Framework - Model-Defined Functions

Entity Framework - Model-Defined Functions

上篇在Linq中使用SQL內建Function介紹了如何在LINQ下達特定的SQL Function之方式,而有時候我們需要的是一個算式而不是單一的SQL Function.

在EF4提供了Model-Defined Functions來解決此問題.

方式如下:

這邊要設計一個自動計算將西元轉換為民國的功能.

建立Model-Defined Functions有二個步驟如下

1.因目前設計工具並不支援此功能,必須手動編輯edmx檔案,請在edmx檔案按滑鼠右鍵選擇"open with.."然後在左邊邊及工具選擇XML (Text) Editor

image
這時VS就會使用文字編輯模式開啟edmx檔案,實際上edmx也只是一個純文字的xml檔案.
在畫面中找到<edmx:ConceptualModels>區段
然後加入底下程式碼

        <Function Name="GetRepublicEraYear" ReturnType="Edm.Int32">
          <Parameter Name="time" Type="Edm.DateTime" />
          <DefiningExpression>
            YEAR(time) - 1911
          </DefiningExpression>
        </Function>
Function的第一個Name可以自行指定一個名稱,這名稱是給步驟二使用,故可以隨意,第二個ReturnType則是這個SQL Function的傳回型別,這邊要注意的這個型別並不是SQL型別,
而是EF對應的dotNet型別,而名稱都為Edm開頭如Edm.Int32,Edm.Int64,Edm.String(有大小寫)等.
而DefiningExpression區段中則是SQL的語法
 
2.新增一個class file,名稱可以自訂,譬如 MySQLFunction.cs,並鍵入底下程式碼
    public static class MySQLFunction
    {
        [EdmFunction("TestModel", "GetRepublicEraYear")]
        public static int GetRepublicEraYear(DateTime time)
        {
            throw new NotSupportedException("Direct calls are not supported.");
        }
    }

這裡說明一下,這個檔案的目的主要用來轉換dotNet程式碼與SQL語法之用,因為EF在查詢時是透過LINQ來查詢,而最終再透過SQL Provider轉譯成SQL語法,故在寫程式時因為使用LINQ並不能直接使用 SQL語法所以必須透過一個虛擬的dotNet函式來對應SQL語法.故程式並沒有實作而是產生一個NotSupportedException例外.
因此這個函式必須宣告為public static,且在函式上面加上EdmFunction的attribute,函式名稱可以隨便取不一定要與SQL Function Name相同,而EdmFunction有兩個參數,

第一個為EDM檔案的namespace,namespace可以在剛剛開啟的XML中找到
   <edmx:ConceptualModels>
      <Schema Namespace="XXX"

第二個則為Function名稱,這個名稱就式步驟一加上的XML文字片段中的Name="XXX"

至此為止大致上已經完成

最後在程式中就可以直接透過MySQLFunction.GetRepublicEraYear來使用

            using (TestEntities dc = new TestEntities())
            {
                var q = dc.tb_Person.Select(p => new { SQLDate = MySQLFunction.GetRepublicEraYear(p.CreateTime) });
                Console.WriteLine(((ObjectQuery)q).ToTraceString());
            }

最終產生的SQL語法如下

image