1

我有一个自定义 ASP.NET 成员资格提供程序,我正在尝试向其中添加密码历史记录功能。用户的密码在 X 天后过期。然后他们必须将密码更改为过去 X 次更改中未使用过的密码。

我已经有了 User 实体,它有一个密码属性作为他们当前的密码。这映射到数据库中的用户表。由于我需要以前密码的列表,因此我创建了一个 UserPassword 表来存储此信息以及对 UserId 的 FK 引用。

由于密码是值对象,在用户之外没有任何意义,它们属于用户聚合,用户作为根。但我的困境就在这里。当我从存储库中检索用户时,我是否总是必须获取他们以前使用的所有密码?99% 的时间我都不关心他们的旧密码,所以每次我需要用户实体时检索它们对于数据库性能来说似乎是一件愚蠢的事情。我不能使用延迟加载,因为 User 实体与上下文断开连接。

我正在考虑创建一个 PasswordHistory 实体,但由于上述原因,密码并不是真正的实体。

你们那里的 DDD 专家将如何处理这种情况?

谢谢。

编辑1:在考虑了更多之后,我意识到这本质上是一个关于延迟加载的问题。更具体地说,您如何处理断开连接的实体中的延迟加载?

编辑 2:我正在使用 LINQ to SQL。使用CodePlex中的this将实体与上下文完全分离。

4

1 回答 1

0

很难完全回答这个问题,因为您没有指定平台,所以我不能完全确定您所说的“断开连接”是什么意思。使用 Hibernate “断开连接”意味着您在有效会话中有一个对象,但数据库连接当前未打开。这很简单,您只需重新连接并延迟加载。更复杂的情况是您有一个“分离”的对象,即根本不再与活动会话相关联,在这种情况下您不能简单地重新连接,您必须获取一个新对象或将您拥有的对象附加到一个活动会话。

无论哪种方式,即使在更复杂的场景中,延迟加载策略仍然不是很多,因为要求是如此不灵活:您必须“连接”才能加载任何东西,无论是延迟加载还是其他方式。时期。我将假设“断开连接”与分离的含义相同。您的策略归结为两个基本场景:这是您可能只需要即时重新连接/附加到延迟加载的情况,还是您想要决定有时在断开连接之前有条件地加载其他对象的情况首先?

有时您实际上可能需要为这两种可能性编写代码。

在您的情况下,您不仅要连接到延迟加载旧密码,还要首先更新 User 对象。此外,由于这是 ASP.NET,您可能会为每个请求使用会话,在这种情况下,您的选择现在基本上只有一个 - 在断开连接之前有条件地延迟加载,仅此而已。

最常见的情况是一个人登录,系统确定他们需要更改密码,并在继续之前要求他们这样做。在这种情况下,您最好在登录后立即处理它并保持用户连接。但是您可能正在使用每个请求的会话,所以您可以做的是在第一个请求过程中的时间限制,如果它已过期,您仍然在这里连接,所以继续并返回一个完全加载的用户(假设您使用的是历史某种客户端脚本验证中的密码)。然后在提交过程中,您可以重新附加或获取一个新的用户实例并更新它。

然后,您总是有可能还必须为他们提供随时更改密码的选项。他们已经登录了。在这里没关系,您有一个用户,但请求很久以前就结束了,并且没有加载密码。在这里,我可能只是编写一个服务方法,当他们调用更改密码函数时,服务会获取带有完整历史记录的用户对象的第二个副本,仅用于更新目的,然后更新密码,然后丢弃该对象,甚至没有将其用于会话或身份验证目的。或者,如果您根据请求使用 Session,则必须执行等效操作 - 获取一个完全初始化的对象以进行客户端验证,

如果在开始经过身份验证的会话后需要密码,您仍然可以执行相同的操作并替换本地用户或更新本地用户的内存密码版本。

如果您有太多的事情要进行多级身份验证,很可能您将不得不要求他们注销并在更改密码后重新完全登录,因此用户的状态一旦他们就无关紧要了请求更改密码。

在任何情况下,如果您对每个请求都使用会话并且您的对象在每次请求后完全分离,在第一种情况下,您仍然可以在原始请求的服务器上延迟加载以返回数据以进行客户端验证。在第二种情况下,您必须进行另一次旅行(这里确实没有延迟加载之类的东西)。在这两种情况下,您都必须权衡您的两个更新选项,因为您总是在更新之前断开连接。您可以在提交过程中从数据库中获取第二个实例以进行更新,也可以重新附加已有的实例。这取决于什么是最佳/最简单的 - 为不常见的事件保存数据库往返真的很重要吗?无论如何,使用您选择的 ORM 重新附加是否可能再次访问数据库?我可能不会费心重新附加,而是根据需要为实际更新获取一个新实例。

于 2011-03-02T05:16:40.993 回答