0

我有一个特定的 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;
}

笔记:

我目前的解决方案对于我运行多台服务器的情况似乎并不合适,我确实这样做了。也许在桌子上获得一个表锁,如果我无法获得锁,则抛出异常。我可以这样做吗?

4

3 回答 3

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);
    }
}

需要注意的是,这仅适用于单实例网络服务器 - 如果您使用集群,则静态对象将不会在网络请求之间共享,这将不起作用。在这种情况下,您需要使用数据库中的某些内容。

于 2013-10-10T16:07:28.350 回答
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 执行任何操作。

于 2013-10-10T16:09:28.547 回答
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>();
    }
于 2013-10-10T20:18:12.873 回答