存储过程支持(实体框架)

实体数据模型 (EDM) 支持使用存储过程检索和修改数据。存储过程可用于检索、插入、更新和删除数据。

许多数据库应用程序使用存储过程提供以下优点:

  • 安全性。可拒绝数据库用户直接访问表或其他数据库对象。数据库管理员只需授予对存储过程的执行权限即可创建数据访问的单入口点。这极大地减少了 SQL 注入式攻击的攻击面。配置为使用默认值的输入和输出参数可实现严格的参数验证。存储过程中的验证代码可限制可用操作。

  • 封装。只要将复杂数据逻辑、显式事务及其他数据库操作写入存储过程代码,即可从多个客户端应用程序执行这些操作。在服务器端代码中,可以处理错误、异常和并发冲突,从而降低了与客户端应用程序的往返次数。只要存储过程的签名保持不变,就可以在不影响客户端应用程序的情况下修改存储过程代码。

  • 可预测性。数据库管理员经常必须确保客户端应用程序不发出可能会对服务器性能产生负面影响的低效查询。存储过程中的查询可单独进行优化以生成合理的执行计划。除了优化之外,正确编写的存储过程还可简化解决服务器端问题(如阻塞和死锁)的过程。

  • 性能。在某些情况下,使用存储过程可能会使性能得到提升。如今的数据库引擎通常将动态 SQL 语句视为与存储过程中的语句一样有效;数据库管理员通过强制使用存储过程,可更好地对性能进行控制。

返回数据的存储过程是从 EDM 对象模型中的命名函数调用的。如果用于更新数据的存储过程未实现和映射,则在它们所定义的操作要使用系统生成的方法时,这些存储过程将映射到实体和关联并进行隐式调用。

本主题介绍用于通过 ModificationFunctionMapping 架构元素中所指定的映射更新数据的存储过程。用于检索数据的存储过程使用 FunctionImportMapping 元素。有关用于检索数据但不更改数据的存储过程的快速入门,请参见如何:使用存储过程定义模型(实体框架)

可由存储过程管理的另一个方案是插入或删除现有实体之间的关联的实例。将关联集映射到存储过程(实体框架)中介绍如何将存储过程映射到这些操作。

默认情况下,实体框架直接对基于概念架构和存储架构之间的映射的数据库表执行操作。若要将实体框架配置为使用存储过程来更新数据,需要对存储和映射架构进行修改。

下表列出了一些主题链接,这些主题说明如何在 EDM 所支持的几种方案中使用存储过程。

操作 SSDL 要求 MSL 要求 CSDL 要求

执行返回实体对象的查询。有关更多信息,请参见如何:使用存储过程定义模型(实体框架)

在存储模型文件的 Schema 元素中添加 Function 元素。此元素引用用于返回实体类型的存储过程。

在概念模型文件的 EntityContainerMapping 元素中添加一个 FunctionImportMapping 元素。

在概念模型文件的 EntityContainer 元素中添加一个 FunctionImport 元素。

存储过程可用于插入、更新和删除实体数据。有关更多信息,请参见如何:使用修改存储过程定义模型(实体框架)

在存储模型文件的 Schema 元素中添加 Function 元素。对每个插入、更新和删除存储过程执行一次此操作。

在实体类型的 EntityTypeMapping 元素中添加一个 ModificationFunctionMapping 元素。此元素必须分别为插入、更新和删除存储过程定义 InsertFunctionUpdateFunctionDeleteFunction 元素。当实体具有关系时,AssociationEnd 元素指定关联。

无。

存储过程用于创建或删除在数据源中使用链接表实现的实体类型之间的多对多关系。有关更多信息,请参见将关联集映射到存储过程(实体框架)

在存储模型文件的 Schema 元素中添加 Function 元素。对在数据源中创建或删除关系的每个存储过程执行一次此操作。

在关联的 AssociationSetMapping Element 元素中添加一个 ModificationFunctionMapping 元素。此元素必须分别为创建和删除此关联的关系的存储过程定义 InsertFunctionDeleteFunction 元素。

无。

数据修改和存储过程

用于数据修改的存储过程取代了实体框架所生成的方法。存储过程是隐式调用的,因此不需要更改概念架构或现有应用程序代码中定义的 EDM。

CSDL

下面的概念架构定义语言 (CSDL) 段定义 SalesOrderHeaderSalesOrderDetail 实体。这两种类型都基于 SQL Server 2005 和 SQL Server 2008 附带的 AdventureWorks 示例数据库。无需对 CSDL 架构进行 CSDL 修改,即可使用存储过程修改 EDM 数据,但为了完整起见,此处列出了 CSDL 架构。为简洁起见,此架构省略了部分属性定义。在 AdventureWorks 销售模型 (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 实例。

Note注意

不建议使用实体框架数据所使用的存储过程中的事务管理,因为它可能会与实体框架处理发生冲突。

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 元素指定可映射的存储过程的参数名称。这些声明向实体框架指示存储过程存在于数据库中但未指定映射。映射将在映射架构中实现,如本主题后面的内容所示。

表示存储过程的函数声明的 IsComposable 属性必须设置为 false。此设置指示过程返回的结果不能用于其他 Entity SQL 语句的 FROM 子句。存储架构中的下列声明指定三个存储过程:CreateSalesOrderDetailUpdateSalesOrderDetailDeleteSalesOrderDetail

        <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 销售模型的完整存储架构,请参见AdventureWorks 销售存储架构 (EDM)

映射规范语言 (MSL)

映射规范定义存储架构中定义的函数与数据库中的存储过程之间的映射。

EntitySetMappingEntityTypeMapping 元素下,ModificationFunctionMapping 元素描述 SSDL 文件中指定的哪些函数负责处理更改。子元素包括 DeleteFunctionInsertFunctionUpdateFunction。每个函数映射都指定映射的存储过程的 FunctionName

使用 EntityTypeMapping 中的 AssociationEnd 元素可以将关系视为引用或外键,引用或外键是使两个实体相关的关联的基础。有关存储过程创建或删除现有实体之间关联的信息,请参见将关联集映射到存储过程(实体框架)

  <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 销售映射架构 (EDM)

开放式并发控制

ScalarProperty 元素的 Version 属性支持使用开放式并发控制执行更新和删除操作。您可以指定 Version 属性,该属性具有最初从数据库读取的 original 值或可能由客户端代码更改的 current 值。在更新函数映射中,指定版本的操作是必需的。在删除函数映射中,指定版本的操作是可选的。在插入操作中,无需使用开放式并发控制,因为数据源没有可测试的原始值。

在执行开放式并发控制时,通过设置 Version 属性,存储过程可以同时采用新值和旧值作为参数。这样,可以确保仅当数据源仍保留应用程序上次从数据源检索的值时才执行更新或删除。

绑定到实体属性的原始版本的输入参数用于存储过程中 UPDATE 或 DELETE 语句的 WHERE 子句。在更新操作中,绑定到实体属性的当前版本的其他参数用于存储过程中 UPDATE 语句的 SET 子句。这样,可确保仅当原始值仍与数据源中的值匹配时才将新的当前值分配给数据源。如果其他用户或其他应用程序自检索原始值后更改了数据源中的值,则更新或删除操作将会失败。

检索服务器值

在插入和更新操作中,另一个子元素 ResultBinding 支持通过结果集返回服务器生成的值。ResultBinding 元素指定返回值与实体属性的对应方式,并使更新管线可以根据概念模型设置对象中的值。

ResultBinding 元素具有 Name 属性 (Attribute) 和 ColumnName 属性 (Attribute),前者是被引用实体定义中的属性 (Property) 的名称,而后者是存储过程所返回的结果集中的列的名称。下面的架构段演示 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()

另请参见

任务

如何:使用存储过程定义模型(实体框架)

概念

ModificationFunctionMapping (EntityTypeMapping)
ModificationFunctionMapping (AssociationSetMapping)
实体框架资源
实体框架术语
将关联集映射到存储过程(实体框架)

其他资源

EDM 规范
架构和映射规范(实体框架)
演练:将实体映射到存储过程