EF 存儲過程(上)

目前,EF對存儲過程的支持並不完善。存在如下問題:
> EF不支持存儲過程返回多表聯合查詢的結果集。
> EF僅支持返回某個表的所有字段,以便轉換成對應的實體。沒法支持返回部分字段的狀況。
> 雖然能夠正常導入返回標量值的存儲過程,可是卻沒有爲咱們自動生成相應的實體.cs代碼,咱們仍是沒法在代碼中直接調用或使用標量存儲過程
> EF不能直接支持存儲過程當中Output類型的參數。
> 其餘一些問題。
下面,主要針對如何使用存儲過程,以及存儲返回實體、表的部分字段這個幾個問題,作具體介紹。數據庫

> 導入存儲過程及返回實體函數

在VS可視化設計器中,打開實體模型(emdx文件)。而後,鼠標右鍵點擊「Customers」à 「添加」à「函數導入」,而後選擇「存儲過程名稱」,並輸入函數導入名稱,選擇返回類型爲實體並選擇Customers。以下圖所示:this

以後,點擊「肯定」。以後,存儲過程導入。在代碼咱們就可使用改存儲過程了。以下代碼所示:設計

複製代碼
public void GetEntityBySP()
{
    using (var db = new NorthwindEntities())
    {
      var cst = db.GetCustomerById("ALFKI").FirstOrDefault();
      Assert.IsNotNull(cst);
      Console.WriteLine("CustomerId:{0},ContactName:{1}", cst.CustomerID, cst.ContactName);
    }
}
複製代碼

> 聯合查詢結果集的問題對象

在此版本的EF中,是不支持存儲過程的多張表聯合查詢的,也就是說查詢的結果集中,一部分字段來自表A,另外一部分字段來自表B,像這種狀況,目前EF沒法直接進行處理。爲此,能夠經過寫兩個或多個存儲過程來實現。好比:第一個存儲過程返回表A的全部字段,第二存儲過程返回表B的全部字段;而後,咱們在代碼中來實現聯合的查詢。
按照前面的思路,增長一個返回全部的Orders表字段的存儲過程GetOrdersByCustomerId,再增長一個返回Order Details表所有字段的存儲過程GetDetailsByCustomerId,並將它們導入到實體模型中。
其中,GetOrdersByCustomerId存儲過程以下:ip

複製代碼
CREATE PROCEDURE GetOrdersByCustomerId
         @CustomerId varchar(5)
AS
BEGIN
         SET NOCOUNT ON;
         SELECT * FROM orders WHERE orders.CustomerID=@CustomerId;
END
複製代碼
GetDetailsByCustomerId存儲過程以下:
複製代碼
CREATE PROCEDURE GetDetailsByCustomerId
@CustomerId varchar(5)       
AS
BEGIN
         SET NOCOUNT ON;
         SELECT d.*
      FROM  Orders o,[Order Details] d
         WHERE o.OrderId=d.OrderId  AND o.CustomerId=@CustomerId;
END
複製代碼


以後,在咱們的代碼來實現聯合查詢。代碼以下:內存

複製代碼
public void GetOrderBySp()
{
    using (var db = new NorthwindEntities())
    {
    var orders = db.GetOrdersByCustomerId("VINET").ToList();
    var details = db.GetDetailsByCustomerId("VINET").ToList();
    orders.ForEach(o => o.Order_Details.Attach(details.Where(d => d.OrderID == o.OrderID)));
    foreach (var order in orders)
    {
        Console.WriteLine(order.OrderID);
        foreach (var d in order.Order_Details)
        Console.WriteLine(d.ProductID);
    }
    }
}
複製代碼

其中,須要注意的,因爲是分別執行了兩個存儲,在內存中是以兩個對立的對象存在,它們以前是沒有創建聯繫的。爲此,咱們須要使用Attach方法來把他們聯繫起來(紅色代碼段),這樣咱們就能夠經過導航來訪問對象的實體了,也就是foreach (var d in order.Order_Details)中的order.Order_Details。cmd

--- 返回部分字段的問題
默認狀況,目前此版本的EF在使用存儲過程返回實體的時候,必須返回全部的字段,以即是EF可以自動將返回的結果轉換成對應的實體。不然會報「數據讀取器與指定的XXX類型不兼容的異常,….」。
接下來,咱們經過創建一個存儲過程,並創建新創建一個實體來解決此問題。首先,咱們在數據庫中創建一個名爲GetCustomerAndOrders的存儲過程,其定義以下:string

複製代碼
CREATE PROCEDURE GetCustomerAndOrders
    AS
BEGIN
    SET NOCOUNT ON;
    SELECT c.CustomerID,c.CompanyName,o.OrderID,o.OrderDate,d.Quantity
    FROM Customers c,Orders o,[Order Details] d
    WHERE c.CustomerID=o.CustomerID AND o.OrderID=d.OrderID;
END
GO
複製代碼

而後,添加一個實體CustomerOders,並設置屬性以下圖所示:it

而後,在VS可視化設計器中,打開實體模型(emdx文件),經過添加à函數導入,導入存儲過程GetCustomerAndOrders,並取名爲GetCustomerAndOrders,返回類型設置爲實體CustomerOrders。
最後,咱們就能夠代碼實體此實體和存儲過程了。以下代碼:

複製代碼
public void GetOrderCountByCustomerId()
{
    using (var db = new NorthwindEntities())
    {
    var co = db.GetCustomerAndOrders().Take(10).Skip(0);
    foreach(var c in co)
          Console.WriteLine(c.CustomerID);
    }
}
複製代碼

> 返回標量值問題

目前,EF對存儲過程返回標量值的支持並無徹底。雖然,咱們能夠按照前面的步驟導入函數,並設置返回標量值的類型,同時EF會在實體模型文件爲咱們自動生成此存儲過程相關的映射配置等。可是,EF卻沒有爲咱們生成在實體模型cs文件中,自動生成相應的.cs代碼,因此咱們根本沒法直接調用此存儲過程。爲解決此問題,咱們須要手動添加代碼到實體模型的.cs代碼文件中。
首先,在數據庫中創建存儲存儲過程GetOrderCntByCustomerId,代碼以下:

複製代碼
CREATE PROCEDURE GetOrderCntByCustomerId
         @CustomerId varchar(5)
AS
BEGIN
         SET NOCOUNT ON;
         SELECT count(*) FROM Orders WHERE Orders.CustomerId=@CustomerId;
END
複製代碼

接着,按照正常的步驟,導入此存儲過程並設置返回類型爲標量類型的Int32。
而後,咱們須要添加一個泛型的方法和一個執行存儲過程的方法,代碼以下:

複製代碼
public partial class NorthwindEntities
{
   //泛型方法用於執行標量存儲過程
        private T ExecuteFunction<T>(string functionName, System.Data.EntityClient.EntityParameter[] parameters) where T : struct
        {
            System.Data.EntityClient.EntityCommand cmd = ((System.Data.EntityClient.EntityConnection)this.Connection).CreateCommand();
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            cmd.Parameters.AddRange(parameters);
            cmd.CommandText = this.DefaultContainerName + "." + functionName;
            try
            {
                if (cmd.Connection.State != System.Data.ConnectionState.Open)
                    cmd.Connection.Open();
                var obj = cmd.ExecuteScalar();
                return (T)obj;
            }
            catch (System.Exception)
            {
                throw;
            }
            finally
            {
                cmd.Connection.Close();
            }
        }
        //執行數據庫中GetOrderCntByCustomerId存儲過程的方法
        public int GetOrderCountByCustomerId(string CustomerId)
        {
            var param = new System.Data.EntityClient.EntityParameter("CustomerId", System.Data.DbType.String);
            param.Value = CustomerId;
            return ExecuteFunction<int>("GetOrderCountByCustomerId", new[] { param });
        }
}
複製代碼

最後,經過以上修改,咱們就能夠直接使用返回標量值的存儲過程,代碼以下:

複製代碼
public void GetOrderCountByCustomerId()
{
    using (var db = new NorthwindEntities())
    {
    var result = db.GetOrderCountByCustomerId("VINET");
    Assert.Greater(result, 0);
    Console.WriteLine(result.ToString());
    }
}
複製代碼

至此,咱們就解決了EF存儲過程返回標量的問題。

相關文章
相關標籤/搜索