Реализация времени ожидания сеанса для консольного приложения в C # - PullRequest
0 голосов
/ 26 июля 2011

Я реализую тайм-аут для моего консольного приложения в C #. У меня есть класс с именем MySession, который должен быть связан тайм-аутом сеанса. Это означает, что в конструкторе класса я настраиваю тайм-аут для созданных объектов. Если какой-либо метод в классе не вызывается в течение указанного времени ожидания, то при последующем доступе к каким-либо методам должно возникать исключение Timeout.

Это фиктивная реализация этого класса.

public class MySession {
private TimeSpan timeout;
private DateTime lastAccessedTime;

public MySession(TimeSpan timeout) {
    this.timeout = timeout;
    lastAccessedTime = DateTime.Now;
}

public void StoreName(string name) {
    TimeSpan diff = DateTime.Now - lastAccessedTime;
    if(diff.Ticks - timeout.Ticks > 0) {
        throw new TimeoutException("session timed out");
    }
    //store logic
    lastAccessedTime = DateTime.Now;
}

public void StoreAddress(string address) {
    TimeSpan diff = DateTime.Now - lastAccessedTime;
    if(diff.Ticks - timeout.Ticks > 0) {
        throw new TimeoutException("session timed out");
    }
    //store logic
    lastAccessedTime = DateTime.Now;
}

public void Commit() {
    TimeSpan diff = DateTime.Now - lastAccessedTime;
    if(diff.Ticks - timeout.Ticks > 0) {
        throw new TimeoutException("session timed out");
    }
    //commit logic
    lastAccessedTime = DateTime.Now;
}}

У меня есть два вопроса.

  1. Есть ли лучший способ сделать это, чтобы избежать проверки, которая происходит в начале каждого метода? Очевидно, что я могу реорганизовать свойство IsAlive из этого кода, но, тем не менее, мне все равно нужно проверять IsAlive в начале каждого метода. Есть ли в .NET класс Session, от которого я мог бы унаследовать свой класс?
  2. Гипотетически, позвольте мне сказать, что один из моих методов в классе сеанса возвращает другой объект. Любые обращения к этому объекту также должны рассматриваться как обновление времени последнего доступа с точки зрения тайм-аута. Это может скоро стать довольно сложным с несколькими вложенными объектами. Но это только гипотетический вопрос и, возможно, ЯГНИ; но я хотел бы знать, есть ли способ сделать это.

РЕДАКТИРОВАТЬ: (поскольку эти пункты не были упомянуты в первоначальном вопросе)

  1. Класс Session действительно имеет метод отката. Я просто не добавил его, чтобы код был маленьким!
  2. Хранилища фактически переходят на внутреннюю базу данных в рамках транзакции базы данных. Таким образом, все вызовы Store в этом классе фактически записывают данные в базу данных, но фиксация в БД должна происходить только после вызова фиксации в сеансе.

1 Ответ

2 голосов
/ 26 июля 2011

Прежде всего, вы должны рассмотреть переосмысление того, за что должен отвечать ваш объект сеанса. Обычно, когда я вижу метод Commit, я ищу также метод Rollback, чтобы убедиться, что ваши обновления каким-то образом согласованы в случае сбоя операции (хотя я все еще не уверен, что должен делать класс ).

Кроме того, если Commit фиксирует временные данные, добавленные вашими StoreSomething методами, то я не понимаю, почему вообще нужно открывать "Сеанс" (опять же, что бы это ни было), пока вы не решите фактически зафиксировать.

Добавление лучшего описания и некоторой предыстории проблемы может позволить нам предложить лучшее решение в долгосрочной перспективе.

Сказав это, немного более измененная версия может быть:

  1. Сначала определите интерфейс ( Принцип подстановки Лискова ):

    public interface IMySession
    {
        void StoreName(string name);
        void StoreAddress(string address);
        void Commit();
    }
    
  2. Реализация простейшего "простого старого" сеанса с основными функциями ( Принцип единой ответственности ):

    public class BasicSession : IMySession
    {
        #region IMySession members
    
        public void StoreName(string name)
        {
            // plain store
        }
    
        public void StoreAddress(string address)
        {
            // plain store
        }
    
        public void Commit()
        {
            // plain commit
        }
    
        #endregion
    }
    
  3. Наконец, создайте прокси-класс, который проверяет время ожидания и перенаправляет вызовы метода в базовый класс:

    public class TimeLimitedSessionProxy : IMySession
    {
        private readonly IMySession _baseSession;
        private readonly TimeSpan _timeout;
        private DateTime _lastAccessedTime = DateTime.Now;
    
        public TimeLimitedSessionProxy(IMySession baseSession, TimeSpan timeout)
        {
            _baseSession = baseSession;
            _timeout = timeout;
        }
    
        #region IMySession members
    
        public void StoreName(string name)
        {
            IfNotTimedOut(() => _baseSession.StoreName(name));
        }
    
        public void StoreAddress(string address)
        {
            IfNotTimedOut(() => _baseSession.StoreAddress(address));
        }
    
        public void Commit()
        {
            IfNotTimedOut(() => _baseSession.Commit());
        }
    
        #endregion
    
        private void IfNotTimedOut(Action action)
        {
            if (DateTime.Now - _lastAccessedTime > _timeout)
            {
                throw new TimeoutException("session timed out");
            }
    
            action();
    
            _lastAccessedTime = DateTime.Now;
        }
    }
    

Общий результат:

  • Другие части вашего кода должны принимать объект IMySession, не заботясь о том, как он фактически реализован под капотом.

  • Даже TimeLimitedSessionProxy принимает IMySession и не знает о фактической реализации; все, что его волнует, это время.

  • Если вы решите добавить другие функции, рассмотрите возможность оставить эти классы без изменений и проксировать или , украшая их при необходимости.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...