【问题标题】:Using LINQ to insert data into a table that uses a sequence as primary key generator使用 LINQ 将数据插入使用序列作为主键生成器的表中
【发布时间】:2019-06-25 01:42:22
【问题描述】:

我有一个表,它从一个序列中生成它的主键(从 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,然后将对象中的id 设置为null,因为SCOPE_IDENTITY() 返回null...
  • 我一直在考虑只使用 IsDbGenerated,销毁 LINQ 对象并在数据库中查询 id,但我没有任何独特的东西可以搜索。

不幸的是,将 id 创建机制更改为 IDENTITY 不是一种选择。

我必须explicitly query the DB for the next sequence value 并手动设置 id 吗?

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

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

【问题讨论】:

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


    【解决方案1】:

    从原始 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)
    

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

    如果您需要从表中检索 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();
    

    【讨论】:

    • 方法3看起来很有趣;您知道在提交时自动调用密钥生成方法的方法吗?没有它感觉很容易出错......
    • @Beat 在答案中添加了示例
    • 转念一想:方法3似乎是通往地狱的道路!见stackoverflow.com/questions/54511913/…
    【解决方案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 对象资源管理器拖到设计器界面上,将其导入到上下文中,然后将如下所示:

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

    并自定义如下:

    就是这样。现在 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 多久。 Entity Framework Core 具有序列支持out of the box。还有更多。

    【讨论】:

    • 感谢您的回答。在许多情况下,这可能是最好的解决方案,但在我的具体情况下,我宁愿不对数据库进行任何更改。
    猜你喜欢
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 1970-01-01
    • 2011-02-15
    • 1970-01-01
    • 2015-09-13
    • 2016-01-15
    • 1970-01-01
    相关资源
    最近更新 更多