这一直困扰我一段时间。但我不是专家。这有点长......
我有一个带有Outlook样式UI的WinForms应用程序。可以说左侧窗格上有一个栏,允许您选择一个“屏幕”,这是一个WinForms控件,比如客户屏幕,在右侧窗格中会出现一个客户列表(即客户)控制)。我将其称为资源管理器界面。双击记录将在另一个窗口中显示非模态客户记录,就像在Outlook中打开电子邮件一样,我们称之为检查员。如果您双击多个记录,您将获得多个检查员。
使用数据绑定完成整个过程。客户列表控件上有一个BindingSource控件,客户检查器上有另一个控件。客户控制在其加载事件中新闻静态DataContext,并将简单的Linq-To-SQL查询的结果分配给BindingControl数据源属性。双击客户列表时,事件会查找记录,将其转换为Linq-To-SQL客户对象,并将其提供给客户检查器表单的构造函数。客户检查器获取客户对象并将其BindingSource控件的数据源属性分配给它。
由于BindingSource控件支持IBindingList,因此在单击“确定”按钮时,在此应用程序中调用EndEdit时,不会修改客户对象的内容。由于Linq to SQL实现了INotifyPropertyChanged接口,因此会更新客户列表。凉。
然而,当我想刷新客户列表的内容以获取其他用户所做的更改时,问题就出现了,我想每隔60秒就定期发生这种更改。如果我有一个计时器并在同一个datacontext上重新运行查询,则不会选择任何更改,因为Linq to SQL不希望压缩对datacontext控制下的数据所做的任何更改。奇怪的是,它针对数据库运行查询,但身份跟踪意味着只有从数据库返回的新客户对象才会添加到列表中。如果我新建了另一个datacontext,那么任何开放的客户检查员都不再使用相同的datacontext,因此未来对开放客户检查员的任何更改都不会反映在客户列表中。
基本上数据是陈旧的,因为我没有使用工作单元模式。所以(如果你还在我身边),问题是。
1。在这种情况下,我该如何使工作单元模式工作? ASP.NET很容易使用请求范围的datacontext,它很短暂,但在WinForms中?
2。在这种情况下是否有其他ORM可以更好地工作? NHibernate,EF,LLBLGEN等
第3。我该怎么办呢?
还有。
4。如果我可以使Linq to SQL像这样工作有人在Linq to SQL部分类中实现IBindingList,这将避免我不得不使用IBindingSource控件。 (我不确定我应该关心这个。)
5。如果我可以使Linq to SQL像这样工作,那么在SQL 2008中有任何使用SQL Notifications的方法,以便在基础查询结果发生变化时通知我,然后重新查询,而不是轮询。
谢谢!
P.S。我知道我可以使用
db.Refresh(System.Data.Linq.RefreshMode.KeepChanges, customers)
但是这会导致针对列表中每个客户的记录针对数据库运行查询。
答案 0 :(得分:3)
我要重述你的问题,以确保我理解它。
您有一个小部件,它显示了一个实体列表(LIST)。单击LIST中的项目时,会出现另一个小部件,允许用户编辑实体。当用户完成对实体的编辑后,他们的更改将提交给DB,并且还应反映在实体的LIST中。系统还应定期获取其他用户对LIST中项目所做的更改并更新LIST。
如果这是正确的,我将抛弃两个用户编辑同一实体的任何并发问题,因为这似乎不是您关注的问题,并将重点关注如何组织UI和工作单元。
您需要将LIST中的实体与您的检查员正在编辑的实体分开。检查员代表的业务流程是您的工作单位,每个实体一个单位。您的清单不代表工作单位。它是所有先前承诺的工作单元的组合工作的陈旧表示或时间点。您的LIST甚至不必直接处理您的实体,它可以持有ID或任何其他方式供您的检查员在用户点击它时从DB获取基础实体。现在,您可以随时更新列表,因为您的检查员根本不与它共享实例。
要向用户模拟当他们通过检查器编辑实体并使它们看起来绑定到同一个实体时,您有两个选择。
1)list仅绑定到DB中的已提交数据。这很容易,当检查员将本地更改刷新回数据库并成功提交时,检查员可以告诉列表自行更新。
2)list绑定到已提交的数据+本地未提交的数据。这有点困难,您需要在列表中公开允许检查员优先从Db返回的数据,并使用自己的本地脏数据覆盖它的方法。
答案 1 :(得分:0)
我目前正在尝试在WinForms SmartClient应用程序中实现相同的方案。
你有没有找到一个好的解决方案?
在我们的应用程序中,我们具有相同的并发UI要求,并且需要从共享源刷新数据,但是我们使用的是WCF服务而不是LinqToSql。我已经实现了自己的身份地图和更改跟踪解决方案。
显然,在检查器中对客户所做的更改会反映在其他视图中。但是这种情况给用户留下了他们的更改已被保存的印象。
我会在您阅读客户列表时绘制工作单元周围的线条,并在保存用户更改时结束。如果您重新阅读客户列表并且没有任何更改,那么这是一个新的工作单元。如果有更改,那么在开始新的工作单元之前,您需要关闭并保存用户所做的更改。
我们的问题是我们一次打开许多窗口,用户很容易进行一些编辑而不会将它们提交到后端。然后,为什么他们无法重新加载主列表并不明显。
我们决定在这种情况下自动保存用户的更改。
你有没有想出更好的解决方案?
答案 2 :(得分:0)
也许有点老......但关于第4/5点,请务必查看CodePlex上的Bindable LINQ项目。绝对是一些很好的代码,可以解决你的问题。
答案 3 :(得分:0)
@Andronicus
在查看了数十个ORMas后,我正在研究名为Genome的商业ORM(http://www.genom-e.com/)。
我似乎允许我做更多上面的事情,比如更高级的数据绑定,并且记录可以停止陈旧但我仍然找到了我的方式。我会让你知道我是怎么过的,但我可能会在某个时候。
@Reddog
我看了BindableLinq,我真的很喜欢它。但它是Linq to Objects,因此据我所知,它没有Linq to SQL翻译。 (除非我错了)。
非常感谢!
答案 4 :(得分:0)
我尝试过类似的事情,这是我的两分钱。
由于UI的工作方式,我认为你不能在这里实现工作单元模式。您可能已经知道,LinqToSql DataContext被设计为轻量级和短期对象。它自然地与“工作单元”结合。在您的情况下,将更改提交到DB是一个单元,从DB刷新更改是另一个单元。但是您希望一个DataContext实例同时执行这两个操作。
此外,我很好奇当用户编辑一条记录时你的UI应该做什么,而另一个用户只是在同一条记录上向DB提交了一些更改。从UI的角度来看,你如何处理这种并发冲突?
您可能需要在UI上做出一些妥协。一种方法是使客户详细信息视图具有“显示”和“编辑”两种模式。显示是一个简单的只读视图,在某个时间间隔内刷新计时器。编辑是一个快照,允许用户修改数据,但不知道其他人的更新。最后,当用户提交更新时,让乐观并发处理冲突。用户在编辑时无法看到实时更改。
你的观点#5很有意思。我们所做的只是查询DB以根据间隔或某些信号收集最新更新。我们称之为“出版服务”。为此,您需要在数据库表中添加时间戳列。
使用“发布服务”,您可以在不使用客户控件中的DataContext的情况下获取增量集(更新和新记录)。如果将delta集“合并”到本地DataBinding DataSource,则应刷新客户详细信息视图。现在,客户控件中的DataContext实例专用于更新。您可以选择让用户决定何时提交。或者,只要用户的焦点离开行(在验证期间),您就可以提交它。我个人会这样做,因为我不想让DataContext在不可预测的时间内有效。