简体   繁体   English

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

[英]Using LINQ to insert data into a table that uses a sequence as primary key generator

I have a table which generates its primary key from a sequence (that just counts up from 0): 我有一个表,该表从一个序列(仅从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]

I've used Visual Studio's O/R Designer to create the mapping files for the table; 我已经使用Visual Studio的O / R设计器来创建表的映射文件。 the id field is defined as: id字段定义为:

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

and now I'm trying to insert data via LINQ. 现在我正在尝试通过LINQ插入数据。

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

The problem I'm encountering is, that LINQ seems unable to handle the id generation via sequence as it sets the id implicitly to 0 when inserting. 我遇到的问题是,LINQ似乎无法通过序列处理ID生成,因为插入时将id隐式设置为0。

  • When I set the id to CanBeNull , the insert fails because it tries to insert NULL into a non-nullable field. 当我将id设置为CanBeNull ,插入将失败,因为它尝试将NULL插入非空字段。
  • When I set the id to IsDbGenerated , the insert works but it expects an IDENTITY field and tries to load the generated id with SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'@p0 int',@p0=1 and than sets the id in the object to null because SCOPE_IDENTITY() returns null 当我将id设置为IsDbGenerated ,插入可以工作,但是它需要一个IDENTITY字段,并尝试使用SELECT CONVERT(Int,SCOPE_IDENTITY()) AS [value]',N'@p0 int',@p0=1然后将对象中的id设置为null,因为SCOPE_IDENTITY()返回null
  • I've been thinking about just using IsDbGenerated , destroying the LINQ object and querying the DB for the id , but I don't have anything unique to search for. 我一直在考虑只使用IsDbGenerated ,销毁LINQ对象并向数据库查询id ,但是我没有什么可以搜索的。

Unfortunately changing the id creation mechanism to IDENTITY is not an option. 不幸的是,不能将ID创建机制更改为IDENTITY。

Do I have to explicitly query the DB for the next sequence value and set the id manually? 我是否必须明确查询数据库以获取下一个序列值并手动设置ID?

Whats the best way to handle these inserts via LINQ? 通过LINQ处理这些插入物的最佳方法是什么?

PS: I need the id because I have to insert more data that uses the id as FK. PS:我需要ID,因为我必须插入更多使用ID作为FK的数据。

Looking at solutions from the raw sql perspective: 从原始sql角度看解决方案:

1. 1。

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

Simply can't be done in LINQ to SQL as far as I can tell 据我所知,根本无法在LINQ to SQL中完成

2. 2。

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

This can be achieved in LINQ to SQL by excluding the id property from the testTable entity. 通过从testTable实体中排除id属性,可以在LINQ to SQL中实现这一点。

If you need to retrieve ids from the table, you could create separate entities for inserting and querying: 如果需要从表中检索ID,则可以创建单独的实体以进行插入和查询:

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

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

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

3. 3。

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

As you mentioned, this can be essentially achieved by manually requesting the next id before each insert. 如您所述,这可以通过在每次插入之前手动请求下一个ID来实现。 If you go this route there are multiple ways to achieve it in your code, you can consider stored procedures or use the LINQ data context to manually execute the sql to retrieve the next sequence value. 如果您采用这种方法,则可以在代码中采用多种方法来实现它,可以考虑使用存储过程或使用LINQ数据上下文手动执行sql来检索下一个序列值。

Here's a code sample demonstrating how to extend the generated DataContext using partial methods. 这是一个代码示例,演示如何使用部分方法扩展生成的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);
        }         
    }
}

Once the above is implemented, you can safely insert entities like this, and they will generate the correct sequence id. 一旦实现了上述内容,您就可以安全地插入这样的实体,它们将生成正确的序列ID。

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

Your only hope is a pretty profound refactoring and use a stored procedure to insert records . 您唯一的希望是进行深刻的重构,并使用存储过程插入记录 The stored procedure can be mapped to the class's Insert method in the data context designer. 可以将存储过程映射到数据上下文设计器中类的Insert方法。

Using your table definition, the stored is nothing but this: 使用表定义,存储的内容只不过是这样:

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

You can import this stored procedure into the context by dragging it from the Sql Object Explorer onto the designer surface, which will then look like this: 通过将其从Sql Object Explorer拖到设计器图面上,可以将其存储到上下文中,然后将其如下所示:

dbml设计器

The next step is to click the testTable class and click the ellipses button for the Insert method (which got enabled by adding the stored procedure to the context): 下一步是单击testTable类,然后单击Insert方法的省略号按钮(通过将存储过程添加到上下文中来启用该按钮):

设置插入方法

And customize it as follows: 并如下对其进行自定义:

设置插入方法对话框

That's all. 就这样。 Now LINQ-to-SQL will generate a stored procedure call to insert a record, for example: 现在,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

Of course you may have to wonder how long you're going to hang on to LINQ-to-SQL. 当然,您可能不得不怀疑要挂多久才能使用LINQ-to-SQL。 Entity Framework Core has sequence support out of the box . 实体框架核心具有开箱即用的序列支持。 And much more. 以及更多。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM