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