我在两个SQL Server表之间有父子关系,其中有一个多列外键。使用直接的T-SQL,我能够在子表中插入行,其中一些外键列是null
,正如预期的那样。
但是,如果我尝试使用DataSet
和DataTable
来执行此操作,则会得到一个InvalidConstraintException
(请参见下面的示例)。我只在多部件钥匙上遇到这个问题;可以在没有CCD_ 5的情况下插入具有单个空外键列的行。
我在ADO.NET上下文中找不到任何有关这种情况的信息。
问题:
- 我做错什么了吗
- 有人能提出解决办法吗?我自己最好的想法:
- 禁用强制执行约束
- 在数据表中插入一个神奇的值,例如
-1
,而不是null
,然后将这些值作为null
写入数据库 - 把神奇的值写进数据库;但在我的情况下,这将使父表中的行数增加一倍
T-SQL代码示例(运行时没有出现错误,正如预期的那样):
CREATE TABLE [P](
[P1] [int] NOT NULL,
[P2] [int] NOT NULL,
PRIMARY KEY ([P1], [P2])
)
CREATE TABLE [C](
[C1] [int] NOT NULL,
[P1] [int] NOT NULL,
[P2] [int] NULL,
PRIMARY KEY ([C1] ASC)
)
ALTER TABLE [C] ADD CONSTRAINT [FK_C_P]
FOREIGN KEY([P1], [P2]) REFERENCES [dbo].[P] ([P1], [P2])
INSERT C (C1, P1, P2) VALUES (1, 1, NULL)
示例C#代码(从最后一行引发的异常):
static void Main(string[] args)
{
var p = new DataTable("P");
var pp1 = new DataColumn("P1", typeof(int));
var pp2 = new DataColumn("P2", typeof(int));
p.Columns.AddRange(new[] { pp1, pp2 });
p.PrimaryKey = new[] { pp1, pp2 };
var c = new DataTable("C");
var cc1 = new DataColumn("C1", typeof(int));
var cp1 = new DataColumn("P1", typeof(int));
var cp2 = new DataColumn("P2", typeof(int));
c.Columns.AddRange(new[] { cc1, cp1, cp2 });
c.PrimaryKey = new[] { cc1 };
c.Constraints.Add(new ForeignKeyConstraint(new[] { pp1, pp2 }, new[] { cp1, cp2 }));
var s = new DataSet() { EnforceConstraints = true };
s.Tables.AddRange(new[] { p, c });
// the following throws InvalidConstraintException
c.Rows.Add(1, 1, null);
}
你能尝试使用吗
//nullable int?
而不是
int
你钥匙的类型?我不熟悉ADO.NET,但我在实体框架方面也遇到过类似的问题,这解决了我的问题。
- 在任何数据库中,如果您有一个以表为条件而非null的架构,则无法进行更改
- 但是可以将NULL值用于外键
- 尝试使用sql命令而不是数据表,这会让您更容易
-
Microsoft sql命令
-
例如
基本sql命令用法
正如运行程序时的错误消息所说,"ForeignKeyConstraint Constraint2要求子键值(1,)存在于父表中。"然后您需要在p
中有一行包含这些值。为此,必须指定pp2
允许DBNull
值,并将该行添加到p
。那是在之后
p.PrimaryKey = new[] { pp1, pp2 };
添加
pp2.AllowDBNull = true;
和之前
c.Rows.Add(1, 1, null);
添加
p.Rows.Add(1, null);
这带来了完整的例子,正如你发布的,如下所示:
static void Main(string[] args)
{
var p = new DataTable("P");
var pp1 = new DataColumn("P1", typeof(int));
var pp2 = new DataColumn("P2", typeof(int));
pp2.DefaultValue = DBNull.Value;
p.Columns.AddRange(new[] { pp1, pp2 });
p.PrimaryKey = new[] { pp1, pp2 };
pp2.AllowDBNull = true;
var c = new DataTable("C");
var cc1 = new DataColumn("C1", typeof(int));
var cp1 = new DataColumn("P1", typeof(int));
var cp2 = new DataColumn("P2", typeof(int));
c.Columns.AddRange(new[] { cc1, cp1, cp2 });
c.PrimaryKey = new[] { cc1 };
c.Constraints.Add(new ForeignKeyConstraint(new[] { pp1, pp2 }, new[] { cp1, cp2 }));
var s = new DataSet() { EnforceConstraints = true };
s.Tables.AddRange(new[] { p, c });
// the following no longer throws InvalidConstraintException
p.Rows.Add(1, null);
c.Rows.Add(1, 1, null);
}
编辑:正如问题中所阐明的,希望避免出现父行。ADO.NET不允许这样做。如果将c.Rows.Add(1, 1, null)
更改为c.Rows.Add(1, null, null)
,它将不再抛出InvalidConstraintException
。也就是说,它要求所有值要么为null(而不是一个或多个),要么该值存在于父表中。
我可能会考虑临时添加父行以保持约束检查,然后在准备插入数据库时,禁用约束检查并从父表中删除不必要的行。