【问题标题】:Implementing a Session Timeout for a Console App in C#在 C# 中为控制台应用程序实现会话超时
【发布时间】:2011-07-26 09:13:49
【问题描述】:

我正在用 C# 为我的控制台应用程序实现超时。 我有一个名为 MySession 的类,它必须受 Session 超时的约束。这意味着在类的构造函数中,我为创建的对象配置了超时。如果在指定的超时时间内没有调用类中的任何方法,那么在后续访问任何方法时,都必须抛出 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. 假设,我说会话类中的一个方法返回另一个对象。对此对象的任何调用也需要被视为从超时角度更新上次访问时间。对于多个嵌套对象,这很快就会变得非常复杂。但这只是一个假设的问题,也许是 YAGNI;但我想知道是否有任何方法可以做到这一点。

编辑:(因为原始问题中没有提到这些点)

  1. Session 类确实有回滚方法。我只是没有添加它以保持代码小!
  2. 存储实际上进入数据库事务范围内的后端数据库。因此,此类中的所有 Store 调用实际上都会将数据写入数据库,但数据库上的提交只需要在会话上的提交被调用后发生。

【问题讨论】:

  • 嗯,这个类的用法好像有点奇怪。这实际上意味着您的应用程序一定会在某个时刻出现异常,因为它无法检查“会话”是否仍然存在。你实际上想要完成什么? cache with an expiration policy (.NET 4)?
  • @Groo,创建类的对象后,直到调用commit方法,如果该对象的用户在大于等于timeout的时间内没有调用该对象的任何方法,只有这样才会抛出异常。否则,不会抛出异常,最终用户将调用 Commit 并完成它。
  • 真实的“用户”还是其他代码?这不应该在 UI 中实现/检查吗?
  • @Deanna,我的意思是任何其他代码 :)
  • 但是 UI 中的任何活动都会重置超时。如果它只是你的代码,唯一真正的方法是检查你的代码的每个入口点。

标签: c# session timeout


【解决方案1】:

首先,您应该考虑重新考虑您的会话对象应该负责什么。通常,当我看到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;
        }
    }
    

总体结果:

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

  • 即使TimeLimitedSessionProxy 接受IMySession 并且对实际实现一无所知;它只关心时机。

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

【讨论】:

  • @Groo,我在问题末尾添加了一个编辑以进一步澄清问题。但是,您的解决方案确实提供了很好的重构。
猜你喜欢
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多