預存程序支援 (Entity Framework)
Entity Data Model (EDM) 支援使用預存程序來進行資料擷取和修改。預存程序可以用來擷取、插入、更新和刪除資料。
許多資料庫應用程式仰賴預存程序提供下列優點:
安全性:資料表或其他資料庫物件有可能會拒絕資料庫使用者的直接存取。而資料庫管理員只需要將執行權限授與給預存程序,即可建立資料存取的單一進入點。這樣可以大幅降低 SQL 插入式攻擊的攻擊面。設定為使用預設值的輸入和輸出參數,可以促成嚴格的參數驗證。而預存程序中的驗證程式碼則能夠限制可用的動作。
封裝:在預存程序程式碼中寫入一次複雜資料邏輯、明確交易和其他資料庫作業,就可以從多種用戶端應用程式執行。錯誤、例外狀況和並行違規可以從伺服器端程式碼處理,進而降低與用戶端應用程式間的往返通訊次數。只要預存程序的簽章保持不變,就可以在不影響用戶端應用程式的情況下修改預存程序程式碼。
可預測性:資料庫管理員必須常常確認用戶端應用程式所發出的查詢是否沒效率,會對伺服器效能造成負面影響。而預存程序中的查詢可以個別進行微調,以產生可接受的執行計劃。除了微調之外,經過良好撰寫的預存程式可以簡化伺服器端問題 (例如封鎖和死結) 的疑難排解。
效能:在某些情況下,使用預存程序可以獲得效能上的好處。現代資料庫引擎通常都能像處理預存程序中的陳述式一樣,有效地處理動態 SQL 陳述式,資料庫管理員則可藉由強制使用預存程序進而維護效能的控制。
在 EDM 物件模型中,能夠傳回資料的預存程序是從具名函式呼叫的。負責更新資料的預存程序則會對應到實體和關聯,並且會在預存程序所定義的作業應該要使用系統產生的方法,但卻沒有實作和定義預存程序的情況下隱含呼叫。
本主題描述的預存程序,用於透過 ModificationFunctionMapping 結構描述項目中指定的對應來更新資料。用於擷取資料的預存程序則使用 FunctionImportMapping 項目。如需用於擷取資料但不會進行變更的預存程序的快速入門,請參閱 HOW TO:定義具有預存程序的模型 (Entity Framework)。
另一個預存程序可以管理的情況,是在現有實體間插入或刪除關聯的執行個體。在將關聯集對應至預存程序 (Entity Framework) 中有描述預存程序與這些作業的對應。
根據預設,Entity Framework 會依據概念和儲存結構描述間的對應,對資料庫資料表直接執行作業。設定 Entity Framework 使用預存程序更新資料,需要對儲存和對應結構描述進行修改。
下表包含的主題連結,說明幾種 EDM 所支援案例中的預存程序使用方式。
作業 | SSDL 需求 | MSL 需求 | CSDL 需求 |
---|---|---|---|
執行可傳回實體物件的查詢。如需詳細資訊,請參閱 HOW TO:定義具有預存程序的模型 (Entity Framework)。 |
在概念模型檔案的 EntityContainerMapping 項目中加入 FunctionImportMapping 項目。 |
在概念模型檔案的 EntityContainer 項目中加入 FunctionImport 項目。 |
|
預存程序用於插入、更新和刪除實體資料。如需詳細資訊,請參閱 HOW TO:使用修改預存程序定義模型 (Entity Framework)。 |
在儲存模型檔案的 Schema 項目內加入 Function 項目。對於每個插入、更新和刪除預存程序,執行一次這個動作。 |
在實體類型的 EntityTypeMapping 項目中加入 ModificationFunctionMapping 項目。這個項目必須定義插入、更新和刪除預存程序的 InsertFunction、UpdateFunction 和 DeleteFunction 項目。在實體具有關聯性時,AssociationEnd 項目要指定關聯。 |
無。 |
預存程序用於建立或刪除實體類型間的多對多關聯性 (這是使用連結資料表在資料來源中實作的)。如需詳細資訊,請參閱將關聯集對應至預存程序 (Entity Framework)。 |
在儲存模型檔案的 Schema 項目內加入 Function 項目。對於建立或刪除資料來源關聯性的每個預存程序,執行一次這個動作。 |
在關聯的 AssociationSetMapping 項目中加入 ModificationFunctionMapping 項目。這個項目必須為建立和刪除這個關聯之關聯性的預存程序,定義 InsertFunction 和 DeleteFunction 項目。 |
無。 |
資料修改和預存程序
用於資料修改的預存程序會取代 Entity Framework 所產生的方法。預存程序是以隱含方式呼叫的,因此不必對概念結構描述或現有應用程式程式碼中定義的 EDM 進行變更。
CSDL
下列概念結構定義語言 (CSDL) 區段會定義 SalesOrderHeader
實體和 SalesOrderDetail
實體。這兩種型別都是以隨附於 SQL Server 2005 和 SQL Server 2008 的 AdventureWorks 範例資料庫為基礎。使用預存程序修改 EDM 資料,不需要對 CSDL 結構描述進行任何 CSDL 修改,但這裡基於完整性而顯示 CSDL 結構描述。為了簡潔起見,這個結構描述省略某些屬性定義。完整的結構描述可以在 AdventureWorks Sales Model (EDM) 的範例中找到。
<Schema Namespace="AdventureWorksModel"
Alias="Self" xmlns="https://schemas.microsoft.com/ado/2006/04/edm">
<EntityContainer Name="AdventureWorksEntities">
<EntitySet Name="AddressType" EntityType="AdventureWorksModel.AddressType" />
<EntitySet Name="Contact" EntityType="AdventureWorksModel.Contact" />
<EntitySet Name="Product" EntityType="AdventureWorksModel.Product" />
<EntitySet Name="SalesOrderDetail"
EntityType="AdventureWorksModel.SalesOrderDetail" />
<EntitySet Name="SalesOrderHeader"
EntityType="AdventureWorksModel.SalesOrderHeader" />
<AssociationSet Name="FK_SalesOrderHeader_Contact_ContactID"
Association="AdventureWorksModel.FK_SalesOrderHeader_Contact_ContactID">
<End Role="Contact" EntitySet="Contact" />
<End Role="SalesOrderHeader" EntitySet="SalesOrderHeader" />
</AssociationSet>
<AssociationSet Name="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
Association="AdventureWorksModel.FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID">
<End Role="SalesOrderHeader" EntitySet="SalesOrderHeader" />
<End Role="SalesOrderDetail" EntitySet="SalesOrderDetail" />
</AssociationSet>
</EntityContainer>
<EntityType Name="SalesOrderDetail">
<Key>
<PropertyRef Name="SalesOrderID" />
<PropertyRef Name="SalesOrderDetailID" />
</Key>
<Property Name="SalesOrderID" Type="Int32" Nullable="false" />
<Property Name="SalesOrderDetailID"
Type="Int32" Nullable="false" />
<Property Name="CarrierTrackingNumber" Type="String" />
<Property Name="OrderQty" Type="Int16" Nullable="false" />
<Property Name="ProductID" Type="Int32" Nullable="false" />
<Property Name="SpecialOfferID" Type="Int32" Nullable="false" />
<Property Name="UnitPrice" Type="Decimal" Nullable="false" />
<Property Name="UnitPriceDiscount"
Type="Decimal" Nullable="false" />
<Property Name="LineTotal" Type="Decimal" Nullable="false" />
<Property Name="rowguid" Type="Guid" Nullable="false" />
<Property Name="ModifiedDate"
Type="DateTime" Nullable="false" />
<NavigationProperty Name="SalesOrderHeader"
Relationship="AdventureWorksModel.FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
FromRole="SalesOrderDetail" ToRole="SalesOrderHeader" />
</EntityType>
<EntityType Name="SalesOrderHeader">
<Key>
<PropertyRef Name="SalesOrderID" />
</Key>
<Property Name="SalesOrderID" Type="Int32" Nullable="false" />
<Property Name="RevisionNumber" Type="Byte" Nullable="false" />
<Property Name="OrderDate" Type="DateTime" Nullable="false" />
<Property Name="DueDate" Type="DateTime" Nullable="false" />
<Property Name="ShipDate" Type="DateTime" />
<Property Name="Status" Type="Byte" Nullable="false" />
<Property Name="OnlineOrderFlag"
Type="Boolean" Nullable="false" />
<Property Name="SalesOrderNumber"
Type="String" Nullable="false" />
<Property Name="PurchaseOrderNumber" Type="String" />
<Property Name="AccountNumber" Type="String" />
<Property Name="CustomerID" Type="Int32" Nullable="false" />
<Property Name="SalesPersonID" Type="Int32" />
<Property Name="TerritoryID" Type="Int32" />
<Property Name="BillToAddressID"
Type="Int32" Nullable="false" />
<Property Name="ShipToAddressID"
Type="Int32" Nullable="false" />
<Property Name="ShipMethodID" Type="Int32" Nullable="false" />
<Property Name="CreditCardID" Type="Int32" />
<Property Name="CreditCardApprovalCode" Type="String" />
<Property Name="CurrencyRateID" Type="Int32" />
<Property Name="SubTotal" Type="Decimal" Nullable="false" />
<Property Name="TaxAmt" Type="Decimal" Nullable="false" />
<Property Name="Freight" Type="Decimal" Nullable="false" />
<Property Name="TotalDue" Type="Decimal" Nullable="false" />
<Property Name="Comment" Type="String" />
<Property Name="rowguid" Type="Guid" Nullable="false" />
<Property Name="ModifiedDate" Type="DateTime" Nullable="false" />
<NavigationProperty Name="Contact"
Relationship="AdventureWorksModel.FK_SalesOrderHeader_Contact_ContactID"
FromRole="SalesOrderHeader" ToRole="Contact" />
<NavigationProperty Name="SalesOrderDetail"
Relationship="AdventureWorksModel.FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
FromRole="SalesOrderHeader" ToRole="SalesOrderDetail" />
</EntityType>
<EntityType Name="AddressType">
<Key>
<PropertyRef Name="AddressTypeID" />
</Key>
<!-- Other properties -->
</EntityType>
<EntityType Name="Contact">
<Key>
<PropertyRef Name="ContactID" />
</Key>
<!-- Other properties -->
<NavigationProperty Name="SalesOrderHeader"
Relationship="AdventureWorksModel.FK_SalesOrderHeader_Contact_ContactID"
FromRole="Contact" ToRole="SalesOrderHeader" />
</EntityType>
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductID" />
</Key>
<!-- Other properties -->
</EntityType>
<Association Name="FK_SalesOrderHeader_Contact_ContactID">
<End Role="Contact"
Type="AdventureWorksModel.Contact" Multiplicity="1" />
<End Role="SalesOrderHeader"
Type="AdventureWorksModel.SalesOrderHeader" Multiplicity="*" />
</Association>
<Association Name="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID">
<End Role="SalesOrderHeader"
Type="AdventureWorksModel.SalesOrderHeader" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="SalesOrderDetail"
Type="AdventureWorksModel.SalesOrderDetail" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="SalesOrderHeader">
<PropertyRef Name="SalesOrderID" />
</Principal>
<Dependent Role="SalesOrderDetail">
<PropertyRef Name="SalesOrderID" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>
如需 CSDL 的詳細資訊,請參閱概念結構描述 (CSDL)。
範例中使用的預存程序
為了示範預存程序的使用,在此提供下列資料庫指令碼以修改 AdventureWorks 資料庫。這些指令碼建立的預存程序會建立、更新和刪除儲存區中 SalesOrderDetail
的執行個體。
![]() |
---|
我們並不建議您在 Entity Framework 資料所使用的預存程序內進行交易管理,因為這可能會跟 Entity Framework 處理作業衝突。 |
CreateSalesOrderDetail 程序
下列指令碼建立的預存程序會將 SalesOrderDetail
項目加入到儲存區中。指令碼包含可用於刪除預存程序的程式碼 (如果在測試這個範例後不再需要該預存程序的話)。若要刪除預存程序,請省略 drop procedure
後的程式碼行,並執行指令碼。
USE [AdventureWorks]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.CreateSalesOrderDetail', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.CreateSalesOrderDetail;
GO
CREATE PROCEDURE [dbo].[CreateSalesOrderDetail]
@SalesOrderID int,
@CarrierTrackingNumber nvarchar(25),
@OrderQty smallint,
@ProductID int,
@SpecialOfferID int,
@UnitPrice money,
@UnitPriceDiscount money,
@rowguid uniqueidentifier,
@ModifiedDate datetime
AS
INSERT INTO [AdventureWorks].[Sales].[SalesOrderDetail]
([SalesOrderID]
,[CarrierTrackingNumber]
,[OrderQty]
,[ProductID]
,[SpecialOfferID]
,[UnitPrice]
,[UnitPriceDiscount]
,[rowguid]
,[ModifiedDate])
VALUES
(@SalesOrderID,
@CarrierTrackingNumber,
@OrderQty,
@ProductID,
@SpecialOfferID,
@UnitPrice,
@UnitPriceDiscount,
@rowguid,
@ModifiedDate)
select SalesOrderDetailID, LineTotal
from [AdventureWorks].[Sales].[SalesOrderDetail]
where SalesOrderID = @SalesOrderID and SalesOrderDetailID = scope_identity()
UpdateSalesOrderDetail 程序
下列指令碼建立的預存程序,用於更新儲存區中的 SalesOrderDetail
項目。
USE [AdventureWorks]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.UpdateSalesOrderDetail', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.UpdateSalesOrderDetail;
GO
CREATE PROCEDURE [dbo].[UpdateSalesOrderDetail]
@OrderQty smallint,
@SalesOrderDetailID int,
@SalesOrderID int
AS
UPDATE [AdventureWorks].[Sales].[SalesOrderDetail]
SET [OrderQty] = @OrderQty
WHERE SalesOrderDetailID = @SalesOrderDetailID
DeleteSalesOrderDetail 程序
下列指令碼建立的預存程序,用於刪除儲存區中的 SalesOrderDetail
項目。
USE [AdventureWorks]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
IF OBJECT_ID ( 'dbo.DeleteSalesOrderDetail', 'P' ) IS NOT NULL
DROP PROCEDURE dbo.DeleteSalesOrderDetail;
GO
CREATE PROCEDURE [dbo].[DeleteSalesOrderDetail]
@SalesOrderDetailID int,
@SalesOrderID int
AS
DELETE FROM [AdventureWorks].[Sales].[SalesOrderDetail]
WHERE SalesOrderDetailID = @SalesOrderDetailID
存放結構定義語言 (SSDL)
在儲存結構描述中,Function 項目會定義資料庫可用的預存程序。巢狀 Parameter 項目會指定可對應之預存程序的參數名稱。這些宣告會通知 Entity Framework 資料庫中存在預存程序,但不會指定對應。對應是在對應結構描述中實作的,本主題稍後將會說明。
代表預存程序之函式宣告的 IsComposable 屬性必須設定為 false
。這表示程序所傳回的結果不能在其他 Entity SQL 陳述式的 FROM
子句中使用。下列儲存結構描述宣告會指定三個預存程序:CreateSalesOrderDetail
、UpdateSalesOrderDetail
和 DeleteSalesOrderDetail
。
<Function Name="CreateSalesOrderDetail" Aggregate="false"
BuiltIn="false" NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="SalesOrderID" Type="int" Mode="In" />
<Parameter Name="CarrierTrackingNumber" Type="nvarchar" Mode="In" />
<Parameter Name="OrderQty" Type="smallint" Mode="In" />
<Parameter Name="ProductID" Type="int" Mode="In" />
<Parameter Name="SpecialOfferID" Type="int" Mode="In" />
<Parameter Name="UnitPrice" Type="money" Mode="In" />
<Parameter Name="UnitPriceDiscount" Type="money" Mode="In" />
<Parameter Name="rowguid" Type="uniqueidentifier" Mode="In" />
<Parameter Name="ModifiedDate" Type="datetime" Mode="In" />
</Function>
<Function Name="UpdateSalesOrderDetail" Aggregate="false"
BuiltIn="false" NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="OrderQty" Type="smallint" Mode="In"/>
<Parameter Name="SalesOrderDetailID" Type="int" Mode="In"/>
<Parameter Name="SalesOrderID" Type="int" Mode="In"/>
</Function>
<Function Name="DeleteSalesOrderDetail" Aggregate="false"
BuiltIn="false" NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="SalesOrderDetailID" Type="int" Mode="In"/>
<Parameter Name="SalesOrderID" Type="int" Mode="In"/>
</Function>
如需 AdventureWorks Sales Model 在加入預存程序前的完整儲存結構描述,請參閱AdventureWorks Sales 儲存結構描述 (EDM)。
對應規格語言 (MSL)
對應規格會定義儲存結構描述所宣告函式和資料庫中預存程序之間的對應。
EntitySetMapping 的 EntityTypeMapping 項目下的 ModificationFunctionMapping 項目,會描述 SSDL 檔案中指定的哪些函式會處理變更過程。子項目包括 DeleteFunction、InsertFunction 和 UpdateFunction。每個函式對應會指定要對應之預存程序的 FunctionName。
EntityTypeMapping 內的 AssociationEnd 項目可以讓您將關聯性視為參考或外部索引鍵 (這是建立實體與其他實體的相關性的關聯基礎)。如需藉由預存程序建立或刪除現有實體間關聯的詳細資訊,請參閱將關聯集對應至預存程序 (Entity Framework)。
<ModificationFunctionMapping >
<InsertFunction
FunctionName="AdventureWorksModel.Store.CreateSalesOrderDetail">
<ScalarProperty Name="CarrierTrackingNumber"
ParameterName="CarrierTrackingNumber" Version="Current"/>
<ScalarProperty Name="OrderQty" ParameterName="OrderQty"
Version="Current"/>
<ScalarProperty Name="ProductID" ParameterName="ProductID" Version="Current"/>
<ScalarProperty Name="SpecialOfferID"
ParameterName="SpecialOfferID" Version="Current"/>
<ScalarProperty Name="UnitPrice"
ParameterName="UnitPrice" Version="Current"/>
<ScalarProperty Name="UnitPriceDiscount"
ParameterName="UnitPriceDiscount" Version="Current"/>
<ScalarProperty Name="rowguid" ParameterName="rowguid"
Version="Current"/>
<ScalarProperty Name="ModifiedDate"
ParameterName="ModifiedDate" Version="Current"/>
<AssociationEnd
AssociationSet="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
From="SalesOrderDetail" To="SalesOrderHeader">
<ScalarProperty Name="SalesOrderID"
ParameterName="SalesOrderID" />
</AssociationEnd>
<ResultBinding ColumnName="SalesOrderDetailID"
Name="SalesOrderDetailID" />
<ResultBinding ColumnName="LineTotal" Name="LineTotal" />
</InsertFunction>
<UpdateFunction
FunctionName="AdventureWorksModel.Store.UpdateSalesOrderDetail" >
<ScalarProperty Name="OrderQty" ParameterName="OrderQty"
Version="Current"/>
<ScalarProperty Name="SalesOrderDetailID"
ParameterName="SalesOrderDetailID" Version="Current"/>
<AssociationEnd
AssociationSet="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
From="SalesOrderDetail" To="SalesOrderHeader">
<ScalarProperty Name="SalesOrderID"
ParameterName="SalesOrderID" Version="Current" />
</AssociationEnd>
</UpdateFunction>
<DeleteFunction
FunctionName="AdventureWorksModel.Store.DeleteSalesOrderDetail" >
<ScalarProperty Name="SalesOrderDetailID"
ParameterName="SalesOrderDetailID" Version="Original"/>
<AssociationEnd
AssociationSet="FK_SalesOrderDetail_SalesOrderHeader_SalesOrderID"
From="SalesOrderDetail" To="SalesOrderHeader">
<ScalarProperty Name="SalesOrderID"
ParameterName="SalesOrderID" />
</AssociationEnd>
</DeleteFunction>
</ModificationFunctionMapping>
若要檢視 AdventureWorks 模型在加入預存程序前的對應結構描述,請參閱AdventureWorks Sales 對應結構描述 (EDM)。
開放式並行存取控制
ScalarProperty 項目的 Version 屬性支援用於更新和刪除的開放式並行存取控制。您指定的 Version 屬性值可以是 original (如同原來讀取自資料庫的資料) 也可以是 current (有可能已由用戶端程式碼變更過)。在更新函式對應中必須要指定版本。對於刪除函式對應而言,指定版本則是選擇性的。至於插入作業則不需要開放式並行存取控制,因為並沒有原始值可以測試資料來源。
設定 Version 屬性可以讓預存程序在執行開放式並行存取控制時,同時採用新舊值做為參數。這樣可以讓您確定,只有在資料來源仍然與上次應用程式擷取資料來源時擁有相同的值,才會發生更新或刪除作業。
預存程序中 UPDATE 或 DELETE 陳述式的 WHERE 子句,會使用繫結至實體屬性原始版本的輸入參數。對於更新作業而言,預存程序中 UPDATE 陳述式的 SET 子句,還會使用繫結至實體屬性目前版本的其他參數。這會驗證只有在原始值仍然符合資料來源中的值時,才將新 (目前) 值指派到資料來源。如果在擷取原始值後有其他使用者或其他應用程式已經變更過資料來源中的值,更新或刪除作業將會失敗。
擷取伺服器值
針對插入和更新作業,有個額外的子項目 ResultBinding 能夠支援透過結果集傳回伺服器產生值。ResultBinding 項目會指定傳回值要如何對應實體屬性,並能夠讓更新管線依據概念模型設定物件值。
ResultBinding 項目的 Name 屬性 (Attribute) 是參考實體定義中屬性 (Property) 的名稱,而其另一個 ColumnName 屬性 (Attribute) 則是預存程序所傳回結果集中資料行的名稱。ResultBinding 項目顯示於下列結構描述區段中。
<ResultBinding Name="SalesOrderDetailID"
ColumnName="SalesOrderDetailID" />
<ResultBinding Name="LineTotal" ColumnName="LineTotal" />
</InsertFunction>
在預存程序程式碼中執行 INSERT 或 UPDATE 陳述式後,請使用 SELECT 陳述式擷取傳遞給 ResultBinding 的值。
SELECT SalesOrderDetailID, LineTotal
FROM [AdventureWorks].[Sales].[SalesOrderDetail]
WHERE SalesOrderID = @SalesOrderID and SalesOrderDetailID = scope_identity()
另請參閱
工作
HOW TO:定義具有預存程序的模型 (Entity Framework)
概念
ModificationFunctionMapping (EntityTypeMapping)
ModificationFunctionMapping (AssociationSetMapping)
Entity Framework 資源
Entity Framework 詞彙
將關聯集對應至預存程序 (Entity Framework)