2016-10-11 16 views
1

我有一个Web方法上传事务(ASMX Web服务),它接受XML文件,验证文件并将文件内容存储在SQL Server数据库中。我们注意到某些用户可以同时提交两次相同的文件。所以我们可以在我们的数据库中再次使用相同的代码(我们不能在数据库上使用唯一索引,或者在数据库级别执行任何操作,不要问我为什么)。我想我可以使用用户ID字符串上的锁定语句,但我不知道这是否会解决问题。或者如果我可以使用兑现对象来存储所有用户ID请求,并检查是否有来自同一用户ID的2个请求,我们将执行第一个请求并阻止第二个请求,并显示错误消息 ,因此如果有人有任何想法,请帮助阻止来自同一用户ID的多个请求到Web方法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(); 
     } 
    } 
} 

现在,如果你保持一个st这个地方的ATIC实例:

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

你可以用它在async方法,利用LockAsyncIDisposable返回类型的美食写一个using声明整齐地包装了关键部分:

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

如果我们在进入之前需要等待,它会异步完成,释放线程来处理其他请求,而不是阻塞,直到等待结束,并且可能在加载时混乱服务器的性能。

当然,当你需要扩展到多个web服务器时,这种方法将不再起作用,并且你需要使用不同的方法(可能通过数据库)进行同步。

+0

感谢您的快速回答,让我试试这个并给我反馈。 – Sharif

+0

实际上,我在这个来自不同客户端的网络服务上每秒钟有大约2000次点击率。我需要的是阻止从相同的客户端基于用户ID的多个请求(所有用户可以一次击中网络方法一次) – Sharif

+0

伟大的答案消费者! –

相关问题