-1

我有一个带有几个选项卡的表单,每个选项卡中都有一个网格控件。当用户选择要删除的行时,我想将其从网格中删除,并且如果数据库中存在该对象也将其删除,但不是永久的 - 仅当用户单击表单上的保存时。

现在,如果对象不存在于数据库中,我将其从列表中删除,如果对象存在于数据库中,我将其从数据库中删除并将其从列表中删除。但是,如果用户单击取消按钮,他希望不会从数据库中删除行。

我有两种可能的解决方案:1) - 从列表中删除对象,如果对象存在于 db 中,则将其添加到要删除的对象列表中 2) - 实现另一个列表,getter 将仅返回状态为 != ToBeDeleted 的对象(表现?)

注意:我没有使用 ORM 工具,而是使用我自己的基于 ado.net 的数据访问框架。

4

1 回答 1

0

我认为您正在描述的案例只是要求进行交易。

ADO.Net 可以轻松处理它们,前提是您使用的是合理的数据库引擎(例如:没有 SqlServerCE :))

例如参见TransactionScope类。您在与数据库交互之前构造这样的对象,当且仅当您调用 Complete() 时,更改才会“提交”。如果您只是不理会它,或者如果您 Dispose() 它,事务将被取消,数据库上的所有更改都将被“回滚”,因此,恢复。

因此,在您的情况下,您可以在表单的 ctor 或 onLoaded() 中打开事务,并在“保存”时打开 Complete(),在任何其他窗口关闭时打开 Dispose()。

虽然这是处理小型系统(尤其是单用户系统)的正常方式,但请注意:如果您的系统必须处理许多并发用户,您可能无法以这种方式使用它。事务会阻塞行和表,直到它完成或取消,因此“其他用户”可能会看到很大的延迟。

那么,您需要支持多少用户,他们多久会尝试编辑相同的内容?

-- 编辑:(10 个用户)

有了这么多用户,您将希望避免长时间运行的事务。在表单加载时打开事务将是不可接受的,并且会锁定许多用户,直到一个当前用户关闭窗口。但是,在 Save() 中使用事务来推送一批中的所有更改是可以的。

当然,如果您可以完全消除交易——那就太好了!但是,如果您还需要保持数据完整性,这是非常困难的事情。为了消除事务的需要,您几乎总是必须重新设计数据库端的数据结构,以及获取和使用数据的方式。数据。如果您想重新设计两者,那么我真的建议您首先尝试重新设计它以使用一些现有的数据访问框架,因为即使是基本的 .Net ADO 也具有非常好的在线编辑在 SqlClient 兼容数据库中保存的数据库的功能。 .

因此,假设您不想重写/重新考虑大部分代码,您只需要缓冲数据并延迟数据库上的所有实际操作。

您可能希望以“简单”形式执行此操作:当您显示表单时,而不是将您的表单直接绑定到数据库驱动的数据源 - 将所有必需的数据下载到一些 BindingList<>s、DataTables 等 - 无论您使用什么容器像。并将您的表单绑定到它们。可能你已经设置了类似的东西。但是,重要的是所有这些数据容器必须离线或至少readonly+delayloaded

接下来,您必须拦截用户在 UI 上执行的所有操作。当然,您已经完成了,因为我假设应用程序可以工作:) 由于您的表单绑定到该离线缓存项目,您的应用程序应该对该缓存数据执行操作,并且根本不接触数据库。但还有更多:除了在缓存数据上执行它们之外,您还应该记录哪个表发生了什么。

然后,当最终用户停止玩耍并按下 CANCEL :) - 你只需丢弃所有内容并关闭表单。数据库没有改变。

保存时 - 您打开一个新事务,然后遍历更改列表并在数据库上有效地重播您的记录器更改,然后提交事务。

但是请注意两件事:在用户缓存数据的时间和他按下保存的时间数据库可能已经改变。您必须检测到这一点并中止或解决冲突。您应该在该事务中执行此操作,无论是在执行记录的更改期间还是之前。您可以通过简单地将在线数据与离线缓存数据(未更改的原始值,而不是用户修改的数据)进行比较来检测它,或者您可以使用其他一些机制,例如 OptimisticLocking,只比较行上的版本标签。

如果您不喜欢记录回放,您可以实现一个“DIFF”实用程序,该实用程序获取修改后的离线数据并以通用方式将其与当前在线表进行比较。这有点困难,但有一个好处:使用这样的实用程序,您最初可以双重缓存数据:一份用于离线参考(只是存储并且用户从未接触过)和一份用于离线编辑(所有那些绑定到形式)。现在,在保存时打开事务并将参考数据与在线数据库进行比较。如果有任何区别 - 您刚刚检测到碰撞。解决/合并/中止/等。如果没有差异,则将修改后的数据与在线数据进行比较,并将找到的所有差异应用于数据库并提交事务。

这些方法中的任何一种都有其优点和缺点:除了实现困难之外,还有缓存的内存问题,如果你敢于复制太大的表,则会出现延迟问题等。

但是 - 一旦解决,它会工作得很好。

当你完成时,你可以去吹嘘你刚刚实现了一个较小的数据集+数据表。我不是在开玩笑,也不是在嘲笑你。我只是想向您展示为什么每个人都告诉您重新调整您的 DAO 层并尝试理解和使用平台设计人员/开发人员已经为您完成的辛勤工作:)

无论如何,我说过如果你重新考虑你的数据结构,你可以完全避免冲突和事务。例如:你为什么要删除行?我知道 SQL 中有一个漂亮的 DELETE 语句,但是,你真的需要删除那一行吗?你不能只添加一些“bool isDeleted”列,当用户从网格中删除该行时 - 只需将该行单元设置为 True 并让应用程序过滤掉任何 isDeleted=true 行而不显示它们?并且不将它们包含在视图和聚合中?奖励:sys/db 管理员现在有一个神奇的工具:取消删除..

让我们更进一步:您需要更新行吗?也许您可以附加一些从(此日期)该行应该有新价格的信息?当然,结构必须有很大的改变:实体没有属性,但有时间戳属性更改的日志(或者行必须有版本号并且是重复的..),查询必须只针对最新的版本数据等优点:数据库现在只能追加。交易,如果需要的话,是超短的。缺点:SELECT 查询很复杂并且可能很慢,尤其是在连接许多表时。

赞成/反对:你的数据库实际上开始看起来非常元而不是数据库......

缺点:将现有应用程序“升级”到这种数据库结构确实是一项艰巨的任务。从头开始编写新应用程序并从 odl 系统导入数据可能会快几倍。

现在,总结一下:

  • 我不推荐任何描述的方式。
  • 首先,我建议您使用一些 ORM 框架,例如 NHibernate、EntityFramework、DevExpress 的 XPO 或其他任何东西。其中任何一个都会为您节省大量时间。我在这里列出的这三个甚至内置了 OptimisticLocking 碰撞检测。既然有这样的工具,为什么还要使用 SQL 自写框架呢?
  • 如果没有,那么接下来我建议使用框架中的现有工具。你使用 SqlClient,为什么不使用 DataSet 和 DataTables?它们与 SqlClient 一起提供,并且它们内置了许多有用的机制,否则您将花费​​数周时间自行实现和测试。学习使用 DataSet 及其碰撞检测及其合并算法,并使用它们。你会在实验和学习上浪费一点时间,但你会在不重新发明轮子上节省大量时间。
  • 如果您真的想手动执行,请从数据缓存和记录重放开始。它很容易理解,很容易在你目前使用普通 SQL 查询的任何地方介绍,并且会快速向你介绍各种缓存同步和版本检查问题,你很快就会详细了解为什么所有这些奇怪的上述框架中的机制是否已实施,它们是如何工作的以及它们有什么优点/缺点。
  • 关于双重缓存差异方法.. 编写该记录回报会更诱人,但请:只有在您非常了解如何检测/解决/合并冲突时才使用它。在尝试之前至少实施一种记录重放方法。

..and of course yo umay use long-lasting transactions. Dumb-easy to introduce, and they "just irritate" the users.. Well, or even make the system unusable when >90% of the users constantly collide and hit the locks, heh.. No, that was a joke. Don't use long-lasting transactions. They are ok for 1-4 users, or for very sparse databases..

于 2012-10-08T19:26:07.303 回答