ADO.NET DataTable不允许多列外键

时间:2015-12-16 09:34:46

标签: c# datatable ado.net dataset

我在两个SQL Server表之间有一个父子关系,带有一个多列外键。使用直接T-SQL,我可以按照预期在子表中插入一些外键列为null的行。

但是,如果我尝试使用DataSetDataTable执行此操作,则会获得InvalidConstraintException(请参阅下面的示例)。我只遇到多部分键的这个问题;可以在没有InvalidConstraintException的情况下插入具有单个空外键列的行。

我无法在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);
}

3 个答案:

答案 0 :(得分:0)

您可以尝试使用

//nullable  int? 

而不是

int 

你的钥匙类型?我不熟悉ADO.NET,但我对实体框架有类似的问题,这解决了我的问题。

答案 1 :(得分:0)

  1. 在任何数据库中,如果您的模式将表作为条件而不是null,则无法进行更改。
  2. 但您可以使用外键的NULL值。
  3. 尝试使用sql命令而不是数据表,这会让您更轻松。
  4. Microsoft sql command

  5. 例如

  6. basic sql command usage

答案 2 :(得分:0)

正如错误消息在运行程序时所说,“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(而不是一个或多个),或者要存在于父表中的值。

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