我有一个表,该表从一个序列(仅从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的数据。
答案 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中拖动到设计器图面上,然后将其导入到上下文中。
下一步是单击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。实体框架核心具有序列支持out of the box。还有更多。