使用 C# 实现控制台应用程序的会话超时
我正在用 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;
}}
我有两个问题。
- 有没有更好的方法来避免在每个方法开始时进行检查?显然,我可以从该代码中重构 IsAlive 属性,但尽管如此,我仍然需要在每个方法的开头检查 IsAlive。 .NET 中是否有可以继承我的类的 Session 类?
- 假设,我说会话类中的一个方法返回另一个对象。从超时角度来看,对此对象的任何调用也需要被视为更新上次访问时间。对于多个嵌套对象,这很快就会变得非常复杂。但这只是一个假设的问题,也许是YAGNI;但我想知道是否有任何方法可以实现这一点。
编辑:(因为原始问题中没有提到这些点)
- Session 类确实有一个回滚方法。我只是没有添加它以保持代码较小!
- 存储实际上进入数据库事务范围内的后端数据库。因此,此类中的所有 Store 调用实际上都会将数据写入数据库,但只有在调用会话提交之后才需要在数据库上提交。
I am implementing a Timeout for my console application in C#.
I have a class called MySession which has to be bound by a Session timeout. This means that in the constructor of the class, I configure a timeout for the objects created. If any method in the class is not called within that specified timeout, then on subsequent access of any methods, a Timeout exception has to be thrown.
This is the dummy implementation of that class.
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;
}}
I have a two questions.
- Is there a better way to do this that avoids the checking that comes at the beginning of every method? I can obviously refactor a IsAlive property out of that code, but nonetheless I still need to check for IsAlive at the beginning of every method. Is there any Session class in .NET that I could inherit my class from?
- Hypothetically, let me say that one of my methods in the session class returns another object. Any calls on this object also needs to be considered as updating the lastaccessed time from a timeout perspective. This could soon get pretty complicated with multiple nested objects. But this is only a hypothetical question and maybe YAGNI; but I would like to know if there is any way this can be accomplished.
EDIT: (since these points were not mentioned in the original question)
- The Session class does indeed have a rollback method. I just did not add it to keep the code small!
- The Stores actually go to a backend database within the scope of a database transaction. So all Store calls in this class, would actually write the data to the database, but commit on the DB needs to happen only after commit on the session is called.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
首先,您应该考虑重新思考您的会话对象应该负责什么。通常,当我看到
Commit
方法时,我也会寻找Rollback
方法,以确保如果操作失败,您的更新在某种程度上是一致的(尽管我是仍然不确定该类应该做什么)。另外,如果
Commit
提交由StoreSomething
方法添加的瞬态数据,那么我根本不明白为什么应该打开“会话”(同样,无论是什么)直到你决定真正承诺。从长远来看,为问题添加更好的描述和一些背景可能会让我们提供更好的解决方案。
话虽如此,稍微重构的版本可能是:
首先定义一个接口(里氏替换原则):
实现具有基本功能的最简单的“普通旧式”会话 (单一职责原则):
最后创建一个代理类,检查超时情况,并将方法调用转发给基类类:
总体结果:
其他您的代码的一部分应该接受一个
IMySession
对象,而不关心它在底层是如何实际实现的。即使是
TimeLimitedSessionProxy
也接受IMySession
并且不知道实际的实现;它只关心时间。如果您决定添加其他功能,请考虑保留这些类不变并代理或根据需要装饰它们。
Before anything else, you should consider rethinking what your session object should be responsible for. Usually, when I see a
Commit
method, I look for aRollback
method also, to make sure that your updates are in some way consistent if an operation fails (although I am still not sure what the class is supposed to do).Also, if
Commit
commits transient data added by yourStoreSomething
methods, then I don't see why a "Session" (again, whatever that is) should be opened at all until you decide to actually commit.Adding a better description and some background to the problem might allow us to provide a better solution in the long term.
Having said that, a slightly more refactored version might be:
Start by defining an interface first (Liskov Substitution Principle):
Implement a simplest "plain old" session with basic functionality (Single Responsibility Principle):
Finally, create a proxy class, which checks for timeouts, and forwards method calls to the base class:
Overall result:
Other parts of your code should accept an
IMySession
object, not caring how it's actually implemented under the hood.Even the
TimeLimitedSessionProxy
accepts anIMySession
and is ignorant about the actual implementation; all it cares about is timing.If you decide to add other functionality, consider leaving these classes intact and proxying or decorating them as needed.