2013-10-10 4 views
0

У меня есть определенный asmx WebMethod, который я хочу ограничить одним запущенным экземпляром за раз.Только один экземпляр. NET Web Service

Причина в том, что он считывает данные из базы данных, считывает данные из файла, обрабатывает данные, отправляет электронные письма и затем сохраняет данные обратно в базу данных, и я обеспокоен тем, что если два экземпляра запускаются за один раз, может вызвать проблемы.

Есть ли способ обеспечить соблюдение этого?

[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

ASMX является левым а также не должны использоваться для новой разработки. WCF следует использовать для всех новых разработок клиентов и серверов веб-сервисов.Один из намеков: Microsoft отстранила [ASMX Forum] (http://social.msdn.microsoft.com/Forums/en-US/asmxandxml/threads) в MSDN. Среди других причин использования WCF он имеет возможность быть одним экземпляром. –

+0

Да, я знаю об этом. Вы или кто-то еще позволяет мне знать об этом почти каждый раз, когда я отправляю вопрос об asmx. – kralco626

+0

Я или кто-то еще позволяет _all reader_ знать это каждый раз, когда вы публикуете вопрос об asmx. Это не все о _you_. –

ответ

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

Важно отметить, что это будет работать только на одном экземпляре веб-сервера - если вы используете кластер, статический объект не будет передаваться между веб-запросов, и это не будет работать. В этом случае вам нужно будет что-то использовать в базе данных.

+0

Мне это нравится! Как я могу попытаться получить блокировку, чтобы я мог вернуть сообщение «Process now running». В конце концов, возвращаемое значение этого метода просто будет сообщением для файла журнала, в котором говорится, что произошло. – kralco626

+0

Я уже обновил ответ, включив в него исключение, если вы не получили блокировку в течение 5 секунд. Вы можете адаптировать это к тому, что вам нравится. – Richard

+0

Отлично, спасибо. Если я установлю его на 0, то он сразу же перейдет к исключению? – kralco626

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

Это может быть полезным, поскольку оно позволяет также внешний процесс, чтобы также инициировать одно и то же действие, а затем может помешать пользовательскому интерфейсу что-либо сделать.

+0

Я буду иметь это в виду, если я нахожу, что мне нужно сделать это из другого процесса, кроме этого WebMethod, и хочу убедиться, что оба они не запускаются одновременно. Благодарю. – kralco626

+0

Могу ли я просто заблокировать таблицу sql? И тогда, если второй экземпляр попытается запустить, он не сможет получить блокировку таблицы? – kralco626

+0

Посмотрите на использование подсказки, приведенной здесь: http://technet.microsoft.com/en-us/library/aa213026(v=sql.80).aspx –

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>(); 
    } 
Смежные вопросы