2016-10-11 2 views
1

У меня есть веб-метод загрузки транзакции (веб-службы ASMX), которые берут XML-файл, проверяют файл и сохраняют содержимое файла в базе данных SQL-сервера. мы заметили, что некоторые пользователи могут одновременно отправить один и тот же файл в одно и то же время. поэтому мы можем снова использовать те же коды в нашей базе данных (мы не можем использовать уникальный индекс в базе данных или делать что-либо на уровне базы данных, не спрашивайте меня почему). Я думал, что могу использовать оператор блокировки для строки идентификатора пользователя, но я не знаю, решит ли это проблему. или если я могу использовать обложенный объект для хранения всех запросов идентификатора пользователя и проверить, есть ли у нас 2 запроса от того же идентификатора пользователя, мы выполним первый и заблокируем второй запрос с сообщением об ошибке , поэтому, если у кого-нибудь есть идеи, пожалуйста, помогитеблокировать множественный запрос от одного и того же идентификатора пользователя к веб-методу C#

+0

Содержится в точном дубликате? Разрешены ли дубликаты? Если нет, вы можете просто отказаться от повторяющихся запросов (если они дважды отправят контент, но _far_, чтобы избежать блокировки) –

+0

Никогда не блокируйте строки. Когда-либо. В самом деле. http://stackoverflow.com/questions/12804879/is-it-ok-to-use-a-string-as-a-lock-object – spender

ответ

3

Блокировка строк неверна. Блокировка вашего веб-сервера плохая.

AsyncLocker - удобный класс, который я написал, чтобы разрешить блокировку любого типа, который хорошо ведет себя как ключ в словаре. Он также требует асинхронной ожидании перед входом в критическую секцию (в отличие от нормального поведения блокировки замков):

public class AsyncLocker<T> 
{ 
    private LazyDictionary<T, SemaphoreSlim> semaphoreDictionary = 
     new LazyDictionary<T, SemaphoreSlim>(); 

    public async Task<IDisposable> LockAsync(T key) 
    { 
     var semaphore = semaphoreDictionary.GetOrAdd(key,() => new SemaphoreSlim(1,1)); 
     await semaphore.WaitAsync(); 
     return new ActionDisposable(() => semaphore.Release()); 
    } 
} 

Это зависит от следующих двух вспомогательных классов:

LazyDictionary:

public class LazyDictionary<TKey,TValue> 
{ 
    //here we use Lazy<TValue> as the value in the dictionary 
    //to guard against the fact the the initializer function 
    //in ConcurrentDictionary.AddOrGet *can*, under some conditions, 
    //run more than once per key, with the result of all but one of 
    //the runs being discarded. 
    //If this happens, only uninitialized 
    //Lazy values are discarded. Only the Lazy that actually 
    //made it into the dictionary is materialized by accessing 
    //its Value property. 
    private ConcurrentDictionary<TKey, Lazy<TValue>> dictionary = 
     new ConcurrentDictionary<TKey, Lazy<TValue>>(); 
    public TValue GetOrAdd(TKey key, Func<TValue> valueGenerator) 
    { 
     var lazyValue = dictionary.GetOrAdd(key, 
      k => new Lazy<TValue>(valueGenerator)); 
     return lazyValue.Value; 
    } 
} 

ActionDisposable:

public sealed class ActionDisposable:IDisposable 
{ 
    //useful for making arbitrary IDisposable instances 
    //that perform an Action when Dispose is called 
    //(after a using block, for instance) 
    private Action action; 
    public ActionDisposable(Action action) 
    { 
     this.action = action; 
    } 
    public void Dispose() 
    { 
     var action = this.action; 
     if(action != null) 
     { 
      action(); 
     } 
    } 
} 

Теперь, если вы держите ул матический экземпляр этого где-то:

static AsyncLocker<string> userLock = new AsyncLocker<string>(); 

вы можете использовать его в async методы, используя прелести IDisposable возвратного типа LockAsync «s, чтобы написать using заявления, аккуратно облегает критическую секцию:

using(await userLock.LockAsync(userId)) 
{ 
    //user with userId only allowed in this section 
    //one at a time. 
} 

Если нам нужно подождать до ввода, это будет сделано асинхронно, освобождая поток для обслуживания других запросов, вместо того, чтобы блокировать до тех пор, пока не закончится ожидание и потенциально испортит производительность вашего сервера под нагрузкой.

Конечно, если вам нужно масштабировать более чем на один веб-сервер, этот подход больше не будет работать, и вам нужно будет синхронизировать его с помощью другого средства (возможно, через БД).

+0

спасибо за ваш быстрый ответ, позвольте мне попробовать это и дать вам мою обратную связь , – Sharif

+0

На самом деле у меня около 2000 просмотров в секунду на этом веб-сервисе от разных клиентов. что мне нужно, чтобы заблокировать несколько запросов к одной и той же веб-службе от одного и того же клиента на основе идентификатора пользователя (все пользователи могут ударить по веб-методу один раз за раз) – Sharif

+0

Отличный ответ spender !! –