/// <summary> /// Manual database locking is used here in order to avoid deadlocks (may depend on usage of READ_COMMITTED_SNAPSHOT), /// see integration test AddUnregisteredPrincipalsParallel. /// </summary> /// <remarks> /// Note that lock resource name is case-sensitive in SQL Server. /// </remarks> private CustomLockInfo CreteCustomLock(string userName) { string key = $"AuthorizationDataLoader.{userName}"; key = CsUtility.LimitWithHash(key, 255); // SQL Server limits key length to 255. try { _sqlExecuter.ExecuteSql( $@"DECLARE @lockResult int; EXEC @lockResult = sp_getapplock {SqlUtility.QuoteText(key)}, 'Exclusive'; IF @lockResult < 0 BEGIN RAISERROR('AuthorizationDataLoader lock.', 16, 10); ROLLBACK; RETURN; END"); return(new CustomLockInfo(key)); } catch (FrameworkException ex) when(ex.Message.TrimEnd().EndsWith("AuthorizationDataLoader lock.")) { throw new UserException( "Cannot initialize the new user, because the user record is locked by another command that is still running.", ex); } }