我有一个表,该表根据序列生成其主键:
CREATE SEQUENCE [dbo].[seq_PK_testTable] AS [int] START WITH 0 INCREMENT BY 1;
CREATE TABLE [dbo].[testTable](
[id] [int] NOT NULL,
CONSTRAINT [PK_testTable] PRIMARY KEY CLUSTERED ([id] ASC)) ON [PRIMARY];
ALTER TABLE [dbo].[testTable] ADD CONSTRAINT [DF_testTable_id] DEFAULT (NEXT VALUE FOR [seq_PK_testTable]) FOR [id];
为了能够使用这种密钥生成机制,我选择使用this answer中提出的数据上下文的Insert
部分方法。
对于第一个插入,它的工作就像一个魅力:在对象中执行插入操作并更新ID,但是一旦插入第二个对象,我就会得到一个System.Data.Linq.DuplicateKeyException: 'Cannot add an entity with a key that is already in use.'
。
MCVE:
using System.Data.Linq.Mapping;
namespace test
{
static class Program
{
static void Main(string[] args)
{
var db = new DataClasses1DataContext(@"Data Source=");
var testTableRecord1 = new testTable();
var testTableRecord2 = new testTable();
db.GetTable<testTable>().InsertOnSubmit(testTableRecord1);
db.SubmitChanges();
db.GetTable<testTable>().InsertOnSubmit(testTableRecord2);
db.SubmitChanges();
}
}
[Database(Name = "TestDB")]
public class DataClasses1DataContext : System.Data.Linq.DataContext
{
public DataClasses1DataContext(string fileOrServerOrConnection) : base(fileOrServerOrConnection) { }
void InserttestTable(testTable instance)
{
using(var cmd = Connection.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR [dbo].[seq_PK_testTable] as NextId";
cmd.Transaction = Transaction;
instance.id = (int)cmd.ExecuteScalar();
ExecuteDynamicInsert(instance);
}
}
}
[Table(Name = "dbo.testTable")]
public class testTable
{
[Column(DbType = "Int NOT NULL", IsPrimaryKey = true)]
public int id;
}
}
我认为它在内部仍希望id
为0
。如何强制LINQ反映真实ID?
PS:因为已将问题标记为LINQ to SQL insert primary key index的重复项:IsPrimaryKey = true
和IsDbGenerated = true
不起作用,因为它们使LINQ to SQL生成查询{ {1}} ID,即IDENTITY
,如果ID是使用默认值SELECT CONVERT(Int,SCOPE_IDENTITY())
创建的,则返回NULL
。
答案 0 :(得分:0)
您可以通过为每个插入操作实例化DataContext的新实例来解决此问题。
var db = new DataClasses1DataContext(@"Data Source=");
var testTableRecord1 = new testTable();
db.GetTable<testTable>().InsertOnSubmit(testTableRecord1);
db.SubmitChanges();
db = new DataClasses1DataContext(@"Data Source=");
var testTableRecord2 = new testTable();
db.GetTable<testTable>().InsertOnSubmit(testTableRecord2);
db.SubmitChanges();
如果您想了解发生了什么,请尝试在断点插入表TestTest方法内放置一个断点。您将看到在第二次调用SubmitChanges()之后没有调用它。数据上下文的每个实例都维护一个缓存,其中包含插入,更新或检索的每个实体。缓存的行为就像字典一样,使用实体的主键作为字典键。由于某种原因,LINQ对每个缓存的实体仅运行一次自定义插入逻辑。 IMO,这是LINQ中的错误,我找不到任何证明这种行为的文档。
要了解我的意思,请尝试将每个实体的ID设置为单独的值,然后您会看到自定义插入行为实际上可以正常工作:
var db = new DataClasses1DataContext(@"Data Source=");
var testTableRecord1 = new testTable(){ id = -1 };
var testTableRecord2 = new testTable(){ id = -2 };
db.GetTable<testTable>().InsertOnSubmit(testTableRecord1);
db.SubmitChanges();
db.GetTable<testTable>().InsertOnSubmit(testTableRecord2);
db.SubmitChanges();
我的建议是在每次调用SubmitChanges()之前创建数据上下文的新实例,或者将插入的内容批量处理为单个SubmitChanges(如果可能,最好)。使用LINQ时,通常应将数据上下文视为短期的,可抛弃的对象。