在C#中为控制台应用程序实现会话超时

时间:2011-07-26 09:13:49

标签: c# session timeout

我在C#中为我的控制台应用程序实现了一个Timeout。 我有一个名为MySession的类,它必须受会话超时的约束。这意味着在类的构造函数中,我为创建的对象配置了超时。如果在指定的超时内未调用类中的任何方法,则在后续访问任何方法时,必须抛出超时异常。

这是该类的虚拟实现。

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. 假设我要说会话类中的一个方法返回另一个对象。对此对象的任何调用也需要被视为从超时角度更新最后访问时间。对于多个嵌套对象,这很快就会变得非常复杂。但这只是一个假设的问题,也许是YAGNI;但我想知道是否有任何办法可以实现。

  3. 编辑:(由于原始问题中未提及这些要点)

    1. Session类确实有一个回滚方法。我只是没有添加它来保持代码小!
    2. 商店实际上转到数据库事务范围内的后端数据库。因此,此类中的所有Store调用实际上都会将数据写入数据库,但只有在调用会话提交后才需要对DB进行提交。

1 个答案:

答案 0 :(得分:2)

在其他任何事情之前,您应该考虑重新思考您的会话对象应该负责的内容。通常,当我看到Commit方法时,我也会查找Rollback方法,以确保在操作失败时您的更新在某种程度上是一致的(尽管我仍然不确定该类是什么应该这样做。

此外,如果Commit提交由StoreSomething方法添加的瞬态数据,那么我不明白为什么应该打开“会话”(再次,无论是什么),直到您决定实际提交。

为问题添加更好的描述和一些背景可能使我们能够长期提供更好的解决方案。

话虽如此,稍微重构的版本可能是:

  1. 首先定义一个界面(Liskov Substitution Principle):

    public interface IMySession
    {
        void StoreName(string name);
        void StoreAddress(string address);
        void Commit();
    }
    
  2. 使用基本功能(Single Responsibility Principle)实施最简单的“普通旧”会话:

    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;
        }
    }
    
  4. 总体结果:

    • 代码的其他部分应该接受IMySession对象,而不是关心它是如何实际实现的。

    • 即使TimeLimitedSessionProxy接受了IMySession,也对实际执行情况一无所知;所有关心的都是计时。

    • 如果您决定添加其他功能,请考虑保留这些类,并根据需要proxyingdecorating