Entity Framework 存儲過程--返回多表聯合查詢的結果集、入返回標量值


目前,EF對存儲過程的支持並不完善。存在以下問題:

·         EF不支持存儲過程返回多表聯合查詢的結果集。
·         EF僅支持返回返回某個表的全部字段,以便轉換成對應的實體。無法支持返回部分字段的情況。
·         雖然可以正常導入返回標量值的存儲過程,但是卻沒有為我們自動生成相應的實體.cs代碼,我們還是無法在代碼中直接調用或使用標量存儲過程
·         EF不能直接支持存儲過程中Output類型的參數。
·         其他一些問題。
下面,主要針對如何使用存儲過程,以及存儲返回實體、表的部分字段這個幾個問題,做具體介紹。
·         導入存儲過程及返回實體

VS可視化設計器中,打開實體模型(emdx文件)。然后,鼠標右鍵點擊“Customersà “添加”à“函數導入”,然后選擇“存儲過程名稱”,並輸入函數導入名稱,選擇返回類型為實體並選擇Customers。如下圖所示:

 

之后,點擊“確定”。之后,存儲過程導入。在代碼我們就可以使用改存儲過程了。如下代碼所示:

        [Test]

        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存儲過程如下:

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

之后,在我們的代碼來實現聯合查詢。代碼如下:

        [Test]

        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。

·         返回部分字段的問題

默認情況,目前此版本的EF在使用存儲過程返回實體的時候,必須返回所有的字段,以便是EF能夠自動將返回的結果轉換成對應的實體。否則會報“數據讀取器與指定的XXX類型不兼容的異常,.”。

接下來,我們通過建立一個存儲過程,並建立新建立一個實體來解決此問題。首先,我們在數據庫中建立一個名為GetCustomerAndOrders的存儲過程,其定義如下:

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,並設置屬性如下圖所示:

 

然后,在VS可視化設計器中,打開實體模型(emdx文件),通過添加à函數導入,導入存儲過程GetCustomerAndOrders,並取名為GetCustomerAndOrders,返回類型設置為實體CustomerOrders

最后,我們就可以代碼實體此實體和存儲過程了。如下代碼:

        [Test]

        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 });

        }

}

最后,通過以上修改,我們就可以直接使用返回標量值的存儲過程,代碼如下:

        [Test]

        public void GetOrderCountByCustomerId()

        {

            using (var db = new NorthwindEntities())

            {

                var result = db.GetOrderCountByCustomerId("VINET");

                Assert.Greater(result, 0);

                Console.WriteLine(result.ToString());

            }

        }        

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

 
      本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM