/// <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);
            }
        }