大家都明白,在设计数据库的时候,外键的存在无可避免。在带来好处的同时(确保数据的完整性和一致性等,这些都不多说了),也有它的很多缺陷,那就是使诸如查询等相关操作的效率降低(但有的时候这也是没办法的事情,现在硬件发展都这么快了),但最主要的是,某些时候,在用户不知道各个实体关系的情况下,他们想去删某些记录,下面我们举个例子。
假设有一张产品类别表:Categary,一张产品表Product,其中产品表引用类表表中的类别编号作为外键。
如果出现这样一种情况,一个用户拥有这些表的删除权限,假设他拥有最高权限,也许此时考虑到数据一致性,你不会开放给用户Categary表记录的删除权限,但假设确实有这么种情况,这种产品类别的产品我们以后确实不会在这个系统中使用,也就是某种产品类别的存在没有意义。因此从可维护性的角度来讲,我们需要将这条记录删除是符合业务逻辑需要的。
但如果开放给用户权限吧,用户删除Categary中被Product表中记录引为外键的记录,会出现很多情况:代码不严谨,直接报错抛给用户,很悲剧;严谨点,捕获到异常,但只是告诉用户出错,这是通常的做法,但用户觉得很莫名其妙。如果Categary类别表中确实有垃圾记录,或者用户想删某条记录。我们需要在有外键约束冲突的情况下,给用户更友好的提示信息,或者提醒他,应该先删什么表中的什么数据,然后才能删这条记录等等。
下面是实现:
在项目数据库中增加一张表,该表有数据库管理员维护,此表是项目中其他表之间主外键关系的描述,我们不能直接告诉用户真实的主、外键表(出于安全),但我们可以告诉他们关于这些表功能的描述,让他们知道哪里有冲突,为什么不能删这条记录等等
表名:Sys_PrimaryForeignTables(系统主外键关系表)
字段名 |
类型 |
描述 |
id |
Int PK not null |
自增编号 |
primaryTables |
Nvarchar(50) not null |
主表(如部门表) |
foreignTables |
Nvarchar(50) not null |
引入外键表(如学生表) |
foreginId |
Nvarchar(20) not null |
外键表中的外键字段名 |
primaryRemark |
Nvarchar(255) null |
对主表的描述 |
foreignRemark |
Nvarchar(255) null |
对引入外键的表的描述 |
|
|
|
代码的实现:
namespace DTMS.DAL.Components.Common { /// <summary> /// 数据访问层——在删除之前检查是否存在外键约束(辅助类) /// </summary> public static class CheckFKReferences { /// <summary> /// 检查是否存在外键约束 /// </summary> /// <param name="id">要删除记录的主键</param> /// <param name="primaryTable">记录所在的表</param> public static string CheckFKBeforeDelete(string id, string primaryTable) { try { string errText = ""; //连接字符串 string connString = ConfigurationManager.ConnectionStrings["DTMS_DBConnectionString"].ConnectionString; DTMS.DTMS_LINQDataContext db = new DTMS_LINQDataContext(); //取得当前表的所有外键信息 var fts = db.Sys_PrimaryForeignTables.Where(pt => pt.primaryTables.Trim() == primaryTable).ToList(); string fId, fTable, strSQL; string[] sql, sqlArray; int count = 0; for (int i = 0; i < fts.Count; i++) { fId = fts[i].foreginId.Trim(); fTable = fts[i].foreignTables.Trim(); sql = new string[]{ "SELECT COUNT(*) FROM ", fTable, " WHERE ", fId, "='", id, "'" }; strSQL = string.Concat(sql); count = Convert.ToInt32(DTMS.DAL.DBUtility.SqlHelper.ExecuteScalar(connString, System.Data.CommandType.Text, strSQL_1)); if (count > 0) { sqlArray = new string[]{ "在删除[",fts[i].primaryRemark.Trim(),"]中的记录时,由于外键约束,所以该记录暂时无法删除。", "请确保将先[",fts[i].foreignRemark.Trim(),"]中关联的相关记录删除后,再执行此删除操作!"}; errText = string.Concat(sqlArray); LogTools.Instance.Log(errText); return errText; } } return ""; } catch (Exception ex) { throw ex; } } } }
实现很简单,相信你能看得懂,一般情况下,在DAL中,我们在对那些有键作为其他表的外键的表作删除的时候,会先去检查。当然,你也可以为此抽象出一个IDeleteable接口,让每个DAL中有删除操作的业务类去实现这个接口。
原文发布时间为:2010-06-15
本文作者:vinoYang
本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。