ADO.NET DataTable不允许多列外键中的null列



我在两个SQL Server表之间有父子关系,其中有一个多列外键。使用直接的T-SQL,我能够在子表中插入行,其中一些外键列是null,正如预期的那样。

但是,如果我尝试使用DataSetDataTable来执行此操作,则会得到一个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,但我在实体框架方面也遇到过类似的问题,这解决了我的问题。

  1. 在任何数据库中,如果您有一个以表为条件而非null的架构,则无法进行更改
  2. 但是可以将NULL值用于外键
  3. 尝试使用sql命令而不是数据表,这会让您更容易
  4. Microsoft sql命令

  5. 例如

基本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(而不是一个或多个),要么该值存在于父表中。

我可能会考虑临时添加父行以保持约束检查,然后在准备插入数据库时,禁用约束检查并从父表中删除不必要的行。

最新更新