我一直在寻找一段时间试图弄清楚这一点。我正在尝试使用复合主键创建一个表。键的第一部分也是父表的外键。第二部分是在 SQL Server 上自动生成的。所以,我有一个应该是这样的表:
ParentId ChildId
-------- -------
1 1
1 2
1 3
2 1
2 2
2 3
2 4
ChildId 列仅在 ParentId 的上下文中是唯一的。这些值是使用 INSTEAD OF INSERT 触发器在服务器上自动生成的,因此每个 ChildId 都有自己的序列。
我的问题是,虽然这在 SQL Server 和经典 ADO.NETSqlCommand
语句中很有效,但 Entity Framework 不想使用它。
如果我将 ChildId 列的 StoreGeneratedPattern 设置为 Identity,则 EF 会生成如下所示的 SQL:
insert [dbo].[ChildTable]([ParentId], [Name])
values (@0, @1)
select [ChildId]
from [dbo].[ChildTable]
where @@ROWCOUNT > 0 and [ParentId] = @0 and [Id] = scope_identity()
这只会产生一个错误:
System.Data.Entity.Infrastructure.DbUpdateConcurrencyException:存储更新、插入或删除语句影响了意外的行数 (0)。自加载实体后,实体可能已被修改或删除。刷新 ObjectStateManager 条目。
----> System.Data.OptimisticConcurrencyException:存储更新、插入或删除语句影响了意外的行数 (0)。自加载实体后,实体可能已被修改或删除。刷新 ObjectStateManager 条目。
但是,如果我使用基于 GUID 的键创建测试表并将 StoreGeneratedPattern 设置为 Identity,则生成的 SQL 如下所示:
declare @generated_keys table([Id] uniqueidentifier)
insert [dbo].[GuidTable]([Name])
output inserted.[Id] into @generated_keys
values (@0)
select t.[Id]
from @generated_keys as g join [dbo].[GuidTable] as t on g.[Id] = t.[Id]
where @@ROWCOUNT > 0
并且我的应用程序中的实体使用 SQL Server 生成的 GUID 的值进行更新。
因此,这表明该列不必是 IDENTITY 列,以便实体框架返回值,但是,由于它使用逻辑表inserted
,因此 ChildId 的值不会是它被更改为的值由触发器。此外,该inserted
表不能应用 UPDATE 操作以将值推回触发器内(试过了,它说“无法更新逻辑表 INSERTED 和 DELETED。”)
我觉得我已经把自己逼到了一个角落,但在我重新考虑设计之前,有什么办法可以通过实体框架将 ChildId 值返回到应用程序中?