0

一度に実行するインスタンスを 1 つに制限したい特定の asmx WebMethod があります。

その理由は、データベースからデータを読み取り、ファイルからデータを読み取り、データを処理し、電子メールを送信してからデータベースにデータを保存するためです。

これを強制する方法はありますか?

[WebMethod]
public List<string> MyMethod()
{
    using (myEntities context = new myEntities())
    {
        //read database, files and do other stuff here
        context.SaveChanges();
    }
}

または、データベースを強制的にロックして、using ステートメント内に 1 つのスレッドしか存在できないようにすることはできますか?

現在の解決策:

[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()最も単純なレベルでは、Web サーバー内の静的オブジェクトを利用するだけです。これにより、2 番目の要求が確実にキューに入れられますが、実行が停止することはありません。

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 要求間で共有されず、これは機能しません。この場合、データベースで何かを使用する必要があります。

于 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 を設定したトランザクションを使用しました。

これを複数回同時に実行すると、次のものが混在しました。

  • 両方の例外ハンドラに該当するプロセス
  • 後に待機して実行中のプロセス

いずれにせよ、2 つのプロセスが同時に実行されることはなく、データベースはそのまま残ります :)

[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 に答える