2011年3月3日星期四

當EF LINQ Expression遇上Methods

若你跟我一樣之前有用LINQ to SQL的話,總會有機會把Extension Methods與LINQ Query結合,就算沒有,toString()這個Object Method或者Convert.ToString(),你都沒可能未用過。

在LINQ to SQL或者LINQ to Object的時候都是相安無事的。

但在Entity Framework上,情況就有不同。

先看Extension methods :
    public static class ExtensionMethods
    {
        public static int ToInt(this String str)
        {
            return Convert.ToInt32(str);
        }
    }

    protected void Page_Load(object sender, EventArgs e)
    {
        using (AdventureWorksEntities awe = new AdventureWorksEntities())
        {
            string s = "1";
            var select = from p in awe.ProductModel
                         where p.ProductModelID == s.ToInt()
                         select p;
            Response.Write(select.Count());
        }
    }
很無奈,你會得到錯誤訊息:
中文:
LINQ to Entities 無法辨識方法 'Int32 ToInt(System.String)' 方法,而且這個方法無法轉譯成存放區運算式。

英文:
System.NotSupportedException: LINQ to Entities does not recognize the method 'Int32 ToInt(System.String)' method, and this method cannot be translated into a store expression.

可能你會說,不用Extension Methods就可以了,但原來ToString()和Convert.ToString()都會出現類似錯誤。
再看看:
    protected void Page_Load(object sender, EventArgs e)
    {
        using (AdventureWorksEntities awe = new AdventureWorksEntities())
        {
            object obj = "abcd";
            var select = from p in awe.ProductModel
                         where p.CatalogDescription.Contains(obj.ToString())
                         select p;
            Response.Write(select.Count());
            //錯誤訊息:LINQ to Entities 無法辨識方法 'System.String ToString()' 方法,
            //而且這個方法無法轉譯成存放區運算式。
        }

    }

    protected void Page_Load(object sender, EventArgs e)
    {
        using (AdventureWorksEntities awe = new AdventureWorksEntities())
        {
            object obj = "abcd";
            var select = from p in awe.ProductModel
                         where p.CatalogDescription.Contains(Convert.ToString(obj))
                         select p;
            Response.Write(select.Count());
            //錯誤訊息:LINQ to Entities 無法辨識方法 'System.String ToString(System.Object)' 方法,
            //而且這個方法無法轉譯成存放區運算式。
        }
    }

那原因是什麼呢?
根據這裡這裡這裡所講。

L2S和EF其中一個最大分別就是,EF把LINQ expressions區分為Client的CLR Method和Server的Canonical Function
因為EF轉換expressions成SQL時,就必須參照兩者進行轉換。
可以參考MSDN上的CLR Method to Canonical Function Mapping

而大家所見列表中並沒有ToString()和Convert.ToString(),更莫說自定義的Extension Methods,所以才會出現錯誤 : System.NotSupportedException: LINQ to Entities does not recognize the method.

1 則留言:

  1. Linq2Sql 一樣做 Expression compile & conversion, 係個 EF support 嘅 function list 唔同唓

    回覆刪除