阻止来自同一用户ID的多个请求到Web方法c#

时间:2016-10-11 11:11:43

标签: c# asp.net asmx

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

1 个答案:

答案 0 :(得分:5)

阻止字符串是不好的。阻止您的网络服务器很糟糕。

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方法中使用它,利用LockAsync的{​​{1}}返回类型的乐趣来编写一个IDisposable语句,整齐地包装关键部分:

using

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

当然,当您需要扩展到多个Web服务器时,此方法将不再有效,并且您需要使用不同的方法(可能通过数据库)进行同步。