使用LINQ将数据插入到使用序列作为主键生成器的表中

时间:2019-01-31 14:54:22

标签: c# .net tsql linq-to-sql

我有一个表,该表从一个序列(仅从0开始计数)生成其主键:

CREATE TABLE [dbo].[testTable](
    [id] [int] NOT NULL,
    [a] [int] NOT NULL,
 CONSTRAINT [PK_testTable] PRIMARY KEY CLUSTERED ([id] ASC))

ALTER TABLE [dbo].[tblTestTable] ADD  CONSTRAINT [DF_tblTestTable_id]  DEFAULT (NEXT VALUE FOR [seq_PK_tblTestTable]) FOR [id]

我已经使用Visual Studio的O / R设计器来创建表的映射文件。 id字段定义为:

[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_id", DbType="Int NOT NULL", IsPrimaryKey=true)]
public int id {…}

现在我正在尝试通过LINQ插入数据。

var testTableRecord = new testTable()
{
    a = 1,
};
db.Connection.Open();
db.testTables.InsertOnSubmit(testTableRecord);
db.SubmitChanges();
Console.WriteLine($"id: {testTableRecord.id}");

我遇到的问题是,LINQ似乎无法通过序列处理id生成,因为它在插入时将id隐式设置为0。

  • 当我将id设置为CanBeNull时,插入失败,因为它试图将NULL插入到不可为空的字段中。
  • 当我将id设置为IsDbGenerated时,该插入有效,但它期望一个IDENTITY字段并尝试使用SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'@p0 int',@p0=1加载生成的ID,然后设置{对象中的{1}}为空,因为id返回SCOPE_IDENTITY()
  • 我一直在考虑只使用null,销毁LINQ对象并向数据库查询IsDbGenerated,但是我没有什么可以搜索的。

不幸的是,无法将ID创建机制更改为IDENTITY。

我是否必须explicitly query the DB for the next sequence value并手动设置ID?

通过LINQ处理这些插入物的最佳方法是什么?

PS:我需要ID,因为我必须插入更多使用ID作为FK的数据。

2 个答案:

答案 0 :(得分:2)

从原始sql角度看解决方案:

1。

INSERT INTO [dbo].[testTable] VALUES (NEXT VALUE FOR [dbo].[seq_PK_tblTestTable], 1)

据我所知,在LINQ to SQL中根本无法完成

2。

INSERT INTO [dbo].[testTable] (a) VALUES (1)

这可以通过从testTable实体中排除id属性在LINQ to SQL中实现。

如果需要从表中检索ID,则可以创建用于插入和查询的单独实体:

public class testTableInsert {
    [ColumnAttribute(...)]
    public int a
}

public class testTableResult {
    [ColumnAttribute(...)]
    public int id

    [ColumnAttribute(...)]
    public int a
}

3。

DECLARE @nextId INT;
SELECT @nextId = NEXT VALUE FOR [dbo].[seq_PK_tblTestTable];  
INSERT INTO [dbo].[testTable] VALUES (@nextId, 1)

正如您提到的,这可以通过在每次插入之前手动请求下一个ID来实现。如果您采用这种方法,则可以在代码中采用多种方法来实现它,可以考虑使用存储过程或使用LINQ数据上下文手动执行sql来检索下一个序列值。

这是一个代码示例,演示如何使用部分方法扩展生成的DataContext。

public partial class MyDataContext : System.Data.Linq.DataContext
{
    partial void InsertTestTable(TestTable instance)
    {

        using (var cmd = Connection.CreateCommand()) 
        {
            cmd.CommandText = "SELECT NEXT VALUE FOR [dbo].[seq_PK_TestTable] as NextId";
            cmd.Transaction = Transaction;
            int nextId = (int) cmd.ExecuteScalar();
            instance.id = nextId;

            ExecuteDynamicInsert(instance);
        }         
    }
}

实施上述步骤后,您可以安全地插入这样的实体,它们会生成正确的序列ID。

TestTable entity = new TestTable { a = 2 };
dataContext.TestTables.InsertOnSubmit(entity);
dataContext.SubmitChanges();

答案 1 :(得分:2)

您唯一的希望是深刻的重构,并使用stored procedure to insert records。可以将存储过程映射到数据上下文设计器中类的Insert方法。

使用您的表定义,存储的内容仅此而已:

CREATE PROCEDURE InsertTestTable
(
    @id int OUTPUT,
    @a AS int
)
AS
BEGIN
    INSERT dbo.testTable (a) VALUES (@a);

    SET @id = (SELECT CONVERT(int, current_value) 
        FROM sys.sequences WHERE name = 'seq_PK_tblTestTable')
END

您可以将该存储过程从Sql Object Explorer中拖动到设计器图面上,然后将其导入到上下文中。

dbml designer

下一步是单击testTable类,然后单击Insert方法(通过将存储过程添加到上下文中而启用)的省略号按钮:

Set Insert method

并按如下所示对其进行自定义:

Set Insert method dialog

仅此而已。现在,LINQ-to-SQL将生成一个存储过程调用以插入一条记录,例如:

declare @p3 int
set @p3=8

declare @p5 int
set @p5=0
exec sp_executesql N'EXEC @RETURN_VALUE = [dbo].[InsertTestTable] @id = @p0 OUTPUT, 
    @a = @p1',N'@p0 int output,@p1 int,@RETURN_VALUE int output',
    @p0=@p3 output,@p1=123,@RETURN_VALUE=@p5 output
select @p3, @p5

当然,您可能不得不怀疑要挂多久才能使用LINQ-to-SQL。实体框架核心具有序列支持out of the box。还有更多。