我有一个数据结构:
SecurityPolicy 1< --- * SecurityPolicyRule
因此,SecurityPolicy可以有0个,一个或多个SecurityPolicyRules。
我正在使用Julie Lerman的实体框架书来实现某种程度的并发检查,TDD和POCO支持。
据我所知,每个表都应该有一个rowversion / timestamp字段,标记为ConcurrencyMode == Fixed。
我决定在存储过程中实现CUD。我的UPDATE Sproc如下:
create PROCEDURE dbo.sp_M2_Core_UpdateSecurityPolicy
@ID int,
@Name nvarchar(256),
@Comment nvarchar(max)=null,
@timestamp timestamp
AS
declare @nameExists nvarchar(256)
select @nameExists= [Name] from M2_Core_SecurityPolicy where [Name]=@Name and [ID]<>@id
if (not @nameExists is null)
begin
raiserror (N'Name is already in use: %s',
11,
1,
@Name)
end
else
begin
update M2_Core_SecurityPolicy
set [Name]=@Name,
[Comment]=@Comment
where id=@id and [timestamp]=@timestamp
IF @@ROWCOUNT>0
SELECT [Timestamp] AS newTimeStamp FROM M2_Core_SecurityPolicy WHERE id=@id
end
go
create PROCEDURE dbo.sp_M2_Core_UpdateSecurityPolicyRule
(
@id int,
@RoleName nvarchar(256),
@Rank int,
@CanReadExecute bit=null,
@CanWrite bit=null,
@CanDelete bit=null,
@CanExport bit=null,
@Timestamp timestamp
)
AS
declare @roleExists nvarchar(256)
declare @securityPolicyID int
select @roleExists= [RoleName] from vw_aspnet_Roles where [RoleName]=@RoleName
if (@roleExists is null)
begin
raiserror (N'Role is not defined: %s',
11,
1,
@roleName)
end
else
begin
select @securityPolicyID=[SecurityPolicyID] from M2_Core_SecurityPolicyRule where [id]=@id
-- move all other rules up in priority
IF (SELECT COUNT(*) FROM M2_Core_SecurityPolicyRule WHERE [ID]<>@ID AND [SecurityPolicyID]=@SecurityPolicyID AND [Rank]=@Rank) > 0
BEGIN
UPDATE M2_Core_SecurityPolicyRule
SET [Rank]=[Rank]+1
WHERE [Rank] >= @rank
AND [SecurityPolicyID]=@SecurityPolicyID
AND [ID]<>@ID
END
update M2_Core_SecurityPolicyRule
set [RoleName]=@RoleName,
[Rank]=@Rank,
[CanReadExecute]=@CanReadExecute,
[CanWrite]=@CanWrite,
[CanDelete]=@CanDelete,
[CanExport]=@CanExport
where id=@id and [timestamp]=@timestamp
IF @@ROWCOUNT>0
SELECT [Timestamp] AS newTimeStamp FROM M2_Core_SecurityPolicyRule WHERE id=@id
end
RETURN
go
我正在使用以下代码进行测试:
测试如下:
[TestMethod()]
public void AddWithSecurityPolicyRuleChangeRankTest()
{
ICoreContext coreContext = new CoreEntities(_coreDbConnectionString);
CoreUnitOfWork coreUnitOfWork = new CoreUnitOfWork(coreContext);
SecurityPolicyRepository target = new SecurityPolicyRepository(coreUnitOfWork);
int originalCount = coreContext.SecurityPolicies.Count();
string securityPolicyName = "addwithsecuritypolicyrulechangeruletest";
int originalRank = 1;
SecurityPolicy entity = new SecurityPolicy()
{
Comment = null,
Name = securityPolicyName,
SecurityPolicyRules = new FixUpCollection<SecurityPolicyRule>()
};
entity.SecurityPolicyRules.Add(
new SecurityPolicyRule()
{
CanDelete = null,
CanExport = null,
CanReadExecute = null,
CanWrite = null,
Rank = originalRank,
RoleName = "User"
});
target.Add(entity);
coreUnitOfWork.Save();
entity.SecurityPolicyRules[0].Rank=originalRank+1;
coreUnitOfWork.Save(); // <-- exception thrown here
SecurityPolicy savedSecurityPolicy = target.GetAll().Single(q => q.Name.Equals(securityPolicyName, StringComparison.CurrentCultureIgnoreCase));
Assert.AreEqual(originalRank+1,savedSecurityPolicy.SecurityPolicyRules[0].Rank);
}
然而,当我运行它时,它会在突出显示的行处抛出异常。例外是:
System.Data.OptimisticConcurrencyException 用户代码未处理 Message =存储更新,插入或 删除语句影响了 意外的行数(0)。 实体可能已被修改或 自实体加载后删除。 刷新ObjectStateManager条目 来源= System.Data.Entity的
堆栈跟踪: 在System.Data.Mapping.Update.Internal.UpdateTranslator.ValidateRowsAffected(Int64) rowsAffected,UpdateCommand source) 在System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager) stateManager,IEntityAdapter适配器) 在System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) 在System.Data.Objects.ObjectContext.SaveChanges(SaveOptions 选项) 在System.Data.Objects.ObjectContext.SaveChanges() 在MIGTurbo2.Core.Data.CoreEntities.Save() 在 d:\ dev的\ migturbo2.0 \ MIGTurbo2.Core \数据\ Core.Context.cs:线 92 在MIGTurbo2.Repositories.CoreUnitOfWork.Save() 在 d:\ dev的\ migturbo2.0 \ MIGTurbo2.Repositories \ CoreUnitOfWork.cs:行 26 在MIGTurbo2.Core.Tests.IntegrationTests.SecurityPolicyRepositoryTest.AddWithSecurityPolicyRuleChangeRankTest() 在 d:\ dev的\ migturbo2.0 \ MIGTurbo2.Core.Tests \ IntegrationTests \ SecurityPolicyRepositoryTest.cs:行 524 InnerException:
果然,没有数据发生变化。即。第一次更新时[Rank]仍为1(因此,INSERT)。但是,通过SQL事件探查器和Ayende的EF Profiler运行它,甚至不会调用数据库来进行更新。那么时间戳/ rowversion的相关性肯定是......无关紧要的?
可能导致这种情况的原因是什么?我不想每次保存都刷新数据库!
更新1
运行应该执行的SQL:
declare @t timestamp
select @t=[timestamp] from M2_Core_SecurityPolicyRule where ID=1
exec [sp_M2_Core_UpdateSecurityPolicyRule] @id=1, @roleName='User',@Rank=2,@Timestamp=@t
工作正常。 EF内部发生阻止呼叫的内容
更新2
通过突破代码,我发现发生以下情况:
那么为什么[Timestamp]字段不会被更新?
答案 0 :(得分:3)
一般来说,这是因为objectstatemanager中实体的时间戳不再与数据库中的实体相匹配。
呼叫 coreContext.Refresh(RefreshOptions.StoreWins(或.ClientWins取决于你想要的),实体);
在调用保存之前同步实体和数据库。
对于解释乐观并发的好帖子,请参阅 http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/457f2196-dd21-4188-8185-2561b954c54b 要么 http://msdn.microsoft.com/en-us/library/bb738618.aspx
答案 1 :(得分:1)
似乎我可能误解了Julie Lerman的书,或者她如何实施她的存储过程需要稍作修改。
我更改了模型和存储过程,以便SProcs返回时间戳,模型会选择它。因此,这意味着[Timestamp]字段不会为空。
所以INSERT SProc现在看起来像:
create PROCEDURE dbo.sp_M2_Core_InsertSecurityPolicy
@Name nvarchar(256),
@Comment nvarchar(max)=null
AS
declare @nameExists nvarchar(256)
declare @id int
select @nameExists= [Name] from M2_Core_SecurityPolicy where [Name]=@Name
if (not @nameExists is null)
begin
raiserror (N'Name is already in use: %s',
11,
1,
@Name)
end
else
begin
INSERT INTO M2_Core_SecurityPolicy
([Name],Comment)
values
(@Name,@Comment)
IF @@ROWCOUNT > 0
BEGIN
SET @id=SCOPE_IDENTITY()
SELECT @id as ID,[Timestamp] FROM M2_Core_SecurityPolicy WHERE ID=@id
END
end
go
并更改映射,以便它选择“新”字段: