我有一个特定的asmx WebMethod,我希望一次限制一个正在运行的实例。
原因是它从数据库读取数据,从文件读取数据,处理数据,发送电子邮件,然后将数据存储回数据库,我担心如果两个实例同时运行,它可以引起问题。
有没有办法强制执行此操作?
[WebMethod]
public List<string> MyMethod()
{
using (myEntities context = new myEntities())
{
//read database, files and do other stuff here
context.SaveChanges();
}
}
或许我可以强制锁定数据库,以便只有一个线程可以在using语句中?
当前解决方案:
[WebMethod]
public List<string> MyMethod()
{
List<string> log = new List<string>();
if(!Monitor.TryEnter(_syncRoot)) {
log.Add("Proccess Currently Running");
return log;
}
try
{
using (myEntities context = new myEntities())
{
//doStuff
context.SaveChanges();
}
log.Add("Success");
}catch (Exception ex) {
log.Add(ex.Message);
} finally {
Monitor.Exit(_syncRoot);
}
return log;
}
注意:
我目前的解决方案似乎不适合我运行多台服务器的情况,我这样做。也许在表上获取表锁,如果我无法获取锁,则抛出异常。我可以这样做吗?
答案 0 :(得分:2)
在最简单的级别,您可以在网络服务器中的静态对象上使用lock()
。这将确保第二个请求排队,但不会阻止它运行。
public static object _syncRoot = new object();
[WebMethod]
public List<string> MyMethod()
{
lock (_syncRoot) {
using (myEntities context = new myEntities())
{
//read database, files and do other stuff here
context.SaveChanges();
}
}
}
下一个复杂程度是尝试使用Monitor.TryEnter获取锁定,超时并显示错误页面。
[WebMethod]
public List<string> MyMethod()
{
if (!Monitor.TryEnter(_syncRoot, 5000)) {
throw new TimeoutExpiredException();
}
try {
// ...
} finally {
// Exit the lock if it was obtained
Monitor.Exit(_syncRoot);
}
}
重要的是要注意,这只适用于单个实例Web服务器 - 如果您正在使用群集,则静态对象将不会在Web请求之间共享,这将无效。在这种情况下,您需要在数据库中使用某些东西。
答案 1 :(得分:1)
或者,您在第一次开始进程时在数据库中存储一个标志,对于所有其他标记,它可以立即退出,如:
[WebMethod]
public List<string> MyMethod()
{
using (myEntities context = new myEntities())
{
if (/* check if flag set in db table */) {
return null; //exit and don't allow anyone else to use
}
//read database, files and do other stuff here
//set flag to false
context.SaveChanges();
}
}
这可能是有益的,因为它还允许外部进程也触发相同的操作,然后可以阻止UI执行任何操作。
答案 2 :(得分:0)
我最终使用了一个设置为Serializable的交易。
当我同时多次运行时,我得到了一个混合:
无论哪种方式,两个进程都没有同时运行,使我的数据库保持不变:)
[WebMethod]
public List<string> MyMethod()
{
var transactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.Serializable, Timeout = TimeSpan.MaxValue };
try
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
try
{
using (myEntities context = new myEntities())
{
//doStuff
context.SaveChanges();
}
}catch (Exception ex) {
//handle database exception here
}
}
} catch(Exception ex){
//handle transaction scope exception here
}
return new List<string>();
}