ПРИМЕЧАНИЕ: это основано на моем вопросе здесь , но это немного отличается. Я решил проблему по другому вопросу, это конкретно касается дочерних объектов.
У меня есть такая структура данных:
SecurityPolicy 1 <--- * SecurityPolicyRule </p>
Следовательно, SecurityPolicy может иметь 0, одну или несколько SecurityPolicyRules.
Я использую книгу Entity Framework Джулии Лерман для реализации некоторой степени проверки параллелизма, поддержки TDD и POCO.
Я понимаю, что в каждой таблице должно быть поле rowversion / timestamp, которое помечено как ConcurrencyMode == Исправлено.
Я решил внедрить CUD в хранимых процедурах. Мой ОБНОВЛЕНИЕ 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
Я тестирую это с помощью кода, который:
- Создает политику безопасности
- Добавляет созданное правило политики безопасности в политику безопасности
- Добавляет политику безопасности
- Сохраняет обновления
- Добавляет 1 к рангу правила политики безопасности
- Сохраняет обновления
Тест ниже:
[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
был не обработан кодом пользователя
Сообщение = Обновление магазина, вставка или
оператор удаления затронул
неожиданное количество строк (0).
Объекты могут быть изменены или
удалено, поскольку объекты были загружены.
Обновите записи ObjectStateManager.
Источник = System.Data.Entity
Трассировки стека:
в System.Data.Mapping.Update.Internal.UpdateTranslator.ValidateRowsActed (Int64
rowAffect, источник UpdateCommand)
в 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 \ Data \ 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:
И, конечно же, данные не изменились. то есть. [Ранг] по-прежнему 1 из первого обновления (следовательно, ВСТАВКА). Однако, запустив его через SQL Profiler и Ayende EF Profiler, в базу данных даже не производятся ОБНОВЛЕНИЯ. Значит, актуальность метки времени / обращения строк, безусловно, не имеет значения?
Что может быть причиной этого? Я не хочу обновлять БД при каждом сохранении! Это ТОЛЬКО происходит при обновлении дочерней сущности. Смена родительского SecurityPolicy работает отлично.
Обновление 1:
При запуске кода INSERT, SecurityPolicyRule создается нормально. Эта проблема относится только к операциям ОБНОВЛЕНИЕ и УДАЛЕНИЕ.
Обновление 2:
Я понял, что если я установлю состояние объекта, он будет отправлен в базу данных. EF теперь забирает мою государственную собственность (используя механизм, описанный в книге Джулии) в порядке. Итак, мой тестовый код теперь выглядит так при обновлении элемента:
entity.SecurityPolicyRules[0].Rank=originalRank+1;;
entity.SecurityPolicyRules[0].State = IWW.Elements.State.Modified;
Однако, несмотря на то, что вызов и параметры, отправленные в базу данных, присутствуют и корректны, он все еще не работает. Интересно, что, как показано на скриншоте ниже, последняя строка отображается ПОСЛЕ исключения. Хотя я уверен, что это связано только с тем, что профиль Entity Framework «очищает» свои мониторы.
Хранимая процедура дочерней сущности возвращает правильные данные?
![Result from Ayende's Entity Framework Profiler](https://i.stack.imgur.com/H2BJQ.png)