仅限单实例.NET Web服务

时间:2013-10-10 16:02:52

标签: .net entity-framework asmx

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

注意:

我目前的解决方案似乎不适合我运行多台服务器的情况,我这样做。也许在表上获取表锁,如果我无法获取锁,则抛出异常。我可以这样做吗?

3 个答案:

答案 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>();
    }