在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?

简介:

在《实现存储过程的自动映射》中,我通过基于T4的代码生成实现了CUD存储过程的自动映射。由于映射的都是基于数据表结构的标准的存储过程,所以它们适合概念模型和存储模型结构相同的场景。如果两种模型存在差异,在进行数据更新操作的时候就会出错。本篇文章主要介绍当概念模型中具有继承关系的两个实体映射到数据库关联的两个表,如何使用存储过程。

目录
一、创建具有继承关系的实体
二、基于继承关系实体的查询与更新
三、映射标准的CUD存储过程
四、修正存储过程

一、创建具有继承关系的实体

假设数据库中有如下两个关联的表:T_EMP和T_SALES。T_EMP用于存储员工信息,主键为代表员工ID号的EMP_ID。为了简单起见,我仅仅定义两个额外的字段:FIRST_NAME和LAST_NAME。另一个表T_EMP用于存储销售人员的信息,它具有一样的主键EMP_ID,额外的两个字段代表负责的区域(Territory)和提成的比率(Commission Rate)。两者通过EMP_ID进行关联。

image

然后我们通过选择这两个表创建.edmx模型。由于这两个表之间具有关联,.edmx模型得两个实体之间会默认创建联系,你首先需要删除此联系。由于销售人员也是公司的员工,它属于是员工类型的子类。所以你需要建立它们之间的继承关系。由于具有继承关系的两个实体不能有重复的属性,属于你需要删除掉T_SALES的EMP_ID属性。最后你需要修正实体和属性的名称使之更具可读性。最后的.edmx模型如下图所示。

image

二、基于继承关系实体的查询与更新

在引入存储过程之前,我们先来谈谈针对于如上一个具有继承关系实体的.edmx模型,如果进行查询和更新。使用过EF的读者应该很清楚,客户端代码进行数据的查询和更新都是通过自动生成的一个继承自ObjectContext的类来完成的。我们不妨来看看针对上面创建的.edmx模型,这个类具有怎样的定义。由于我为该模型的Entity Container起名为HrEntities,随后最终生成的是如下一个同名的类。

   1: public partial class HrEntities : ObjectContext
   2: {    
   3:     public ObjectSet<Employee> Employees{get}    
   4:     public void AddToEmployees(Employee employee)
   5:     {
   6:         //...
   7:     }
   8: }
   9:  
  10: public partial class Employee : EntityObject
  11: {
  12:      //...
  13: }
  14: public partial class Sales : Employee
  15: {
  16:     //...
  17: }

我们可以看到,虽然在模型中有两个实体,但是对于HrEntities来说,它仅仅具有一个类型为ObjectSet<Employee>的Employees属性(没有ObjectSet<Sales>类型的属性)和对应的AddToEmployee方法。但是针对这个两个实体对应的类都是存在的,并且存在继承关系。理解起来也容易,Sales也是Employee,所以Employees属性表述的ObjectSet可以同时包括普通的Employee和Sales。

最后我们在一个控制台应用中编写如下一段代码。这段代码中,先删除掉现有的Employee(包括Sales)记录,然后分别添加一个Employee对象和Sales对象。最后通过查询确保它被成功提交到数据库中。

   1: static void Main(string[] args)
   2: {
   3:     using (HrEntities context = new HrEntities())
   4:     {
   5:         foreach (Employee emp in context.Employees)
   6:         {
   7:             context.Employees.DeleteObject(emp);
   8:         }
   9:         context.SaveChanges();
  10:  
  11:         Employee employee = Employee.CreateEmployee("001", "Jin Nan", "Jiang");
  12:         Employee sales = Sales.CreateSales("002", "Yan Yan", "Xu", "West", 10);
  13:         context.AddToEmployees(employee);
  14:         context.AddToEmployees(sales);
  15:         context.SaveChanges();
  16:  
  17:         foreach (Employee emp in context.Employees)
  18:         {
  19:             Console.WriteLine("Employee ID: {0}; First Name: {1}; Last Name: {2}; Is Sales: {3}",
  20:                 emp.ID, emp.FirstName, emp.LastName, (emp is Sales) ? "Yes" : "No");
  21:         }
  22:     }
  23: }

下面是我们希望的输出结果:

   1: Employee ID: 001; First Name: Jin Nan; Last Name: Jiang; Is Sales: No
   2: Employee ID: 002; First Name: Yan Yan; Last Name: Xu; Is Sales: Yes

从上面的代码中我们可以看到,当我们通过ObjectContext添加一个Employee对象的时候,它会先根据对象的真实类型,判断仅仅需要添加Employee对应的数据表记录,还是需要同时在Employee和Sales对应的两张数据表中同时添加一条记录。修改和删除操作采用的机制也是如此。

三、映射标准的CUD存储过程

到目前为止,我们的程序运行的很好,现在我们分别Employee和Sales实体映射我们创建的标准的数据表,你可以手工是完成,也可以利用在《实现存储过程的自动映射》提到的代码生成的方式。在这之前我们不妨先来看看我们标准的存储过程长什么模样。下面是基于T_EMP数据表的CUD存储过程。

   1: CREATE PROCEDURE [dbo].[P_EMP_I]
   2:     @p_emp_id        VARCHAR(50),
   3:     @p_first_name    NVARCHAR(50),
   4:     @p_last_name    NVARCHAR(50)
   5: AS
   6: BEGIN
   7:     INSERT T_Emp(EMP_ID, FIRST_NAME, LAST_NAME)
   8:     VALUES(@p_emp_id,@p_first_name,@p_last_name)
   9: END
  10: GO
  11:  
  12: CREATE PROCEDURE [dbo].[P_EMP_U]
  13:     @o_emp_id        VARCHAR(50),
  14:     @p_first_name    NVARCHAR(50),
  15:     @p_last_name    NVARCHAR(50)
  16: AS
  17: BEGIN
  18:     UPDATE T_EMP
  19:     SET FIRST_NAME    = @p_first_name,    LAST_NAME = @p_last_name
  20:     WHERE emp_id    = @o_emp_id
  21: END
  22: GO
  23:  
  24: CREATE PROCEDURE [dbo].[P_EMP_D]
  25:     @o_emp_id VARCHAR(50)
  26: AS
  27: BEGIN
  28:     DELETE T_EMP    
  29:     WHERE EMP_ID = @o_emp_id
  30: END
  31: GO

下面三个是基于T_SALES数据表的三个存储过程。

   1: CREATE PROCEDURE [dbo].[P_SALES_I]
   2:     @p_emp_id            VARCHAR(50),
   3:     @p_territory        NVARCHAR(50),
   4:     @p_commission_rate    INT
   5: AS
   6: BEGIN
   7:     INSERT T_SALES(EMP_ID, TERRITORY,COMMISSION_RATE)
   8:     VALUES(@p_emp_id, @p_territory, @p_commission_rate)
   9: END
  10: GO
  11:  
  12: CREATE PROCEDURE [dbo].[P_SALES_U]
  13:     @o_emp_id            VARCHAR(50),
  14:     @p_territory        NVARCHAR(50),
  15:     @p_commission_rate    INT
  16: AS
  17: BEGIN
  18:     UPDATE T_SALES
  19:     SET TERRITORY        = @p_territory,
  20:         COMMISSION_RATE = @p_commission_rate    
  21:     WHERE emp_id        = @o_emp_id
  22: END
  23: GO
  24:  
  25: CREATE PROCEDURE [dbo].[P_SALES_D]
  26:     @o_emp_id    VARCHAR(50)
  27: AS
  28: BEGIN
  29:     DELETE T_SALES    
  30:     where EMP_ID = @o_emp_id
  31: END

完成存储过程的映射后,再次运行上面的代码,会有如下一个UpdateException异常抛出来。追踪InnerException,你会发现一条有用的异常消息:The INSERT statement conflicted with the FOREIGN KEY constraint "FK_T_SALES_T_EMP". The conflict occurred in database "EFExtensions", table "dbo.T_EMP", column 'EMP_ID'.The statement has been terminated.

image

之所以出现上述的异常在于:当我们添加一个Sale对象的时候,只有Sales实体对象的Insert存储过程被执行。而该存储过程仅仅是为T_SALES数据表中插入数据,但是此时主表T_EMP没有相应的记录,违反外键约束。在进行数据的修改和删除时,也有相同的问题。

四、修正存储过程

为了解决这个问题,我们只需要修改子类对应表的存储过程,让它们同时去添加、修改和删除主记录。下面列出了修正后的存储过程定义。

P_SALES_I

   1: CREATE PROCEDURE [dbo].[P_SALES_I]
   2:     @p_emp_id           VARCHAR(50),
   3:     @p_first_name       NVARCHAR(50),
   4:     @p_last_name        NVARCHAR(50),
   5:     @p_territory        NVARCHAR(50),
   6:     @p_commission_rate  INT
   7: AS
   8: BEGIN
   9:     INSERT  T_Emp(EMP_ID, FIRST_NAME, LAST_NAME)
  10:     VALUES(@p_emp_id,@p_first_name,@p_last_name)
  11:     
  12:     INSERT T_SALES(EMP_ID, TERRITORY,COMMISSION_RATE)
  13:     VALUES(@p_emp_id, @p_territory, @p_commission_rate)
  14: END

P_SALES_U

   1: CREATE PROCEDURE [dbo].[P_SALES_U]
   2:     @o_emp_id           VARCHAR(50),
   3:     @p_first_name       NVARCHAR(50),
   4:     @p_last_name        NVARCHAR(50),
   5:     @p_territory        NVARCHAR(50),
   6:     @p_commission_rate  INT
   7: AS
   8: BEGIN
   9:     UPDATE T_EMP
  10:     SET FIRST_NAME        = @p_first_name,
  11:         LAST_NAME         = @p_last_name
  12:     WHERE EMP_ID           = @o_emp_id
  13:     
  14:     UPDATE T_SALES
  15:     SET TERRITORY        = @p_territory,
  16:         COMMISSION_RATE  = @p_commission_rate    
  17:     WHERE emp_id         = @o_emp_id
  18: END

P_SALES_D

   1: CREATE PROCEDURE [dbo].[P_SALES_D]
   2:     @o_emp_id VARCHAR(50)
   3: AS
   4: BEGIN
   5:  
   6:     DELETE T_SALES    
   7:     WHERE EMP_ID = @o_emp_id
   8:     
   9:     DELETE T_EMP
  10:     WHERE EMP_ID = @o_emp_id
  11: END

然后在EF的模型设计器中对新的参数进行映射即可。

在Entity Framework中使用存储过程(一):实现存储过程的自动映射
在Entity Framework中使用存储过程(二):具有继承关系实体的存储过程如何定义?
在Entity Framework中使用存储过程(三):逻辑删除的实现与自增长列值返回
在Entity Framework中使用存储过程(四):如何为Delete存储过程参数赋上Current值?
在Entity Framework中使用存储过程(五):如何通过存储过程维护多对多关系?


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
2天前
|
存储 SQL 数据库
存储过程定义
存储过程是一组为了完成特定功能的SQL语句集合。 存储过程在使用过程中是将常用或者复杂的工作,预先使用SQL语句写好并用一个指定的名称存储起来,这个过程经编译和优化后存储在数据库服务器中。当需要使用该存储过程时,只需要调用它即可。存储过程在执行上比传统SQL速度更快、执行效率更高。
|
3月前
|
人工智能 开发者 前端开发
【创新·未来】当AI遇见代码:Vaadin Copilot引领Web开发新时代,你准备好了吗?
【8月更文挑战第31天】Vaadin 是一个成熟的 Java Web 应用框架,最新版本 24.4.0 带来了多项更新,包括引入 Vaadin Copilot——一个集成 AI 的开发工具,可实现拖放组件、实时更新源代码等功能。此外,Vaadin 24.4.0 还统一了 Hilla 框架,支持 Flow 和 Hilla 视图混合应用,实现 React 组件与 Java 应用的无缝集成。未来,Vaadin 将继续提升开发者体验和应用性能,整合更多现代 Web 技术,如 Web 组件和 PWA 支持,保持其在企业级应用开发领域的领先地位。
56 0
|
3月前
|
存储 SQL 测试技术
Entity Framework Core 中的存储过程超厉害!从定义到调用全攻略,提升性能与安全性!
【8月更文挑战第31天】在现代软件开发中,数据库操作效率至关重要。Entity Framework Core(EF Core)作为强大的对象关系映射(ORM)框架,支持存储过程,可提升数据库操作的性能、安全性和可维护性。本文详细介绍如何在 EF Core 中定义、配置及调用存储过程,并提供最佳实践建议,包括性能优化、安全性增强、代码可维护性提升以及参数化查询等。通过遵循这些指导原则,开发者能够充分利用存储过程的优势,显著提高应用程序质量和性能。附带完整示例代码,展示从定义实体类到调用存储过程的全过程。
176 0
|
5月前
|
存储 SQL 关系型数据库
MySQL数据库——存储过程-变量(系统变量、用户定义变量、局部变量)
MySQL数据库——存储过程-变量(系统变量、用户定义变量、局部变量)
336 0
|
6月前
|
存储 SQL 定位技术
数据库基础(六):T-SQL语言程序设计、游标定义与使用及存储过程创建与信息查找
数据库基础(六):T-SQL语言程序设计、游标定义与使用及存储过程创建与信息查找
105 1