超越Web的宏:使用.NET开发Access Services事件接收器

Access Services面临的一个挑战是要确保各种对象背后的逻辑在Access本身和SharePoint网站所宿主的Access web数据库之间的可移植性。Access中使用用宏来实现逻辑。具体到Access Services,这意味着我们需要在一个Web浏览器中使用Access内容中各种对象背后的VBA。

以此类推,对于Access开发人员的我们就需要面临新的挑战:如何在Web上实现客户端中通过宏为客户提供的功能?

很幸运,Richard Fennell,一个Visual Studio Team System MVP,他曾经写了很多关于Access 2010的经验分享性文章可供我们来学习,我们可以使用SharePoint对象模型和Visual Studio 2010来实现应用程序中的业务逻辑。随着开发环境转向.NET框架和标准的语言,Web数据库不必再受限于宏。因此,很有必要创建一个概念证明性质的演示,以解释如何利用SharePoint事件来扩展全新的Access Web数据库。

为此,我还创建了一个视频来配合这篇博文。该视频长约10分钟,可以从这里下载: AccessServicesEventReceiver.mov (64MB)。

Visual Studio 2010和SharePoint模板

Visual Studio 2010的一大的增强功能是,现在操作SharePoint对象模型变得非常简单 - 有许多现成的模板,针对不同的SharePoint应用,包括(但不限于)Web部件,工作流和事件接收器。正如在视频中看到的, Visual Studio 2010现在支持自动化处理解决方案的打包,添加到Feature中,然后激活。以往这些繁琐的工作都需要手工来完成。安装工作相对比较简单 - 使用GuidGen.exe(Visual Studio工具的一部分)来创建一个新的GUID来唯一标识程序集,然后确定哪些列表和事件需要自定义,使我们可以集中精力开始自定义业务逻辑的编码工作。完成后,可以选择“部署解决方案”,Visual Studio会自动执行所需的步骤,更新SharePoint,从而使所有解决方案中的所有部件立即可用。

这里特别引起我们兴趣的是名为“事件接收器”模板。该模版我们会指导选择一个SharePoint网站,然后选择一个列表来关联该事件接收器,然后选择你想要接收的事件就可以开始编码了。虽然事件接收器只能与单个列表相关联,但是它可以支持在该列表上多个事件上自定义功能,我们可以在一个项目中添加许多列表和事件接收器。

当我们把Access web数据库发布到SharePoint网站,web数据表会转化成SharePoint列表,这样我们就能够使用SharePoint对象模型与Visual Studio来自定义操作了。 Richard Fennell先生很慷慨的分享他在SharePoint列表上关联事件的代码,他不是走的向导,因为对于该应用程序来说,只能通过手工的方式来挂接。使用他的代码,我们可以选择任何Access Services的列表来关联的任何事件。

附加事件至列表

在本演示中,我们希望能够读取MySQL数据库的数据并写入到Access web数据库,需要能够同时在浏览器和富客户端实现相同的功能。在富客户端,解决的办法很简单:直接连接到该表就好了。在浏览器里,我们受限于已发布的应用程序范围内,Web宏不允许运行代码或链接到外部数据(注:Web数据库中连接表在以后的版本会解决)。因此,我们会实现包含两个事件的接收器: ItemAdding和ItemAdded。然后事件接收器与一个列表,“Action”进行挂接。在第一个窗体里,人力资源的工作人员将操作一个过滤器窗体,输入筛选条件,以确定应该从MySQL检索什么样的人力资源数据子集。当“载入”按钮被按下时,会把新的过滤条件保存Action列表中,从而引发ItemAdding事件。

当事件中的C#代码被触发,我们就可以检查一下刚刚添加的信息,打开一个到MySQL数据库的连接,并传递过滤条件到MySQL的存储过程。然后我们可以在该事件中使用DataReader类,只读取一条人力资源记录。 这样就完成了ItemAdding事件。按照设计,在SharePoint对象模型里所有的“- ing”的事件都是同步执行的,因此用户必须等待它完成。

当ItemAdded被触发,它将异步的运行在它自己的线程里,是和应用程序线程分开的,无论是在客户端还是浏览器都是如此。从ItemAdding里用DataReader读完数据后,会继续添加该记录。具体而言,这意味着用户在Access Services运行完其中的宏并返回保存记录的命令,包括接下来来的BrowseTo宏打开绑定到HR记录的窗体之前,只需要等待记录的加载就可以了。在窗体加载到用户的浏览器时,至少有一个记录已被从MySQL提取到Access Services的列表里,准备接受用户的浏览和编辑。在用户浏览人力资源记录的时候,ItemAdded事件仍然在运行中,用户可以点击“获取更多的记录”,在窗体中加载由ItemAdded事件添加的新的记录。

这一方案中最妙的部分是,不需要为管理线程而特别编写代码 – 这部分工作已经由SharePoint处理好了,减轻了开发人员的工作量,并使其能够专注于事件内部的业务逻辑。利用Visual Studio 2010在SharePoint方面的增强,开发Access web应用程序类型的SharePoint解决方案完全具有可行性,对于需要超越内置的Web宏进行自定义开发的人员无疑很有帮助。

边界间通信

接下来的一个挑战是,想办法把.NET代码的执行进度和最终结果返回给应用程序。 As noted earlier, the web application cannot interact with the outside world and .NET code is outside.如前所述,Web应用程序不能与外界进行交互,而.NET代码就属于外界的部分。对这一难题的解决办法是利用Action列表来存储信息,这样我们的代码运行的过程中可以不断的更新该列表。这里有一个Action列表的实例:

 

在视频中显示了.NET代码获取一条完整的人事记录的步骤(请注意,一条雇员记录包括三个子关系,因此,必须同时考虑获取这三个子表的相关记录才是真正的'完成'),在获取下一条个人记录之前,首先会更新操作列表,记一条“fetched”记录。边注:出于性能考虑,更好可以批量更新Action列表,而不是单个添加记录,代码移植到生产环境时应予以考虑。

在Web窗体的页脚部分,包括了返回Action列表的链接以及不同的状态报告,这取决于正在执行代码的哪一部分。当代码异步运行时,页脚会显示成这样:

用户可以按下按钮来刷新web窗体,以便看到上次加载后代码获取到的最新的记录。这就跟一个静态的进度条的意思基本上一样了。

.当代码已经完成收集所有匹配的记录,它会用最终的数字更新'matched'栏,以便指示所有获取记录的工作已完成。此时页脚将看起来像这样:

这样就为查看当前数据的用户提供了一个非常有用的反馈,可以了解加载进度。此外,这一切是通过使用宏和检查Action列表的行来实现的。

Access客户端和离线模式

因为该事件接收器与SharePoint列表相关联,所以无论我们是通过浏览器访问或者客户端访问(或任何相关的客户端来访问)应用程序,该代码都将运行。但是,Access客户端具有脱机工作的能力。当Access客户端处于离线模式时,这时意味着直到下一次同步前事件接收器不会执行。在本次演示中,这意味着离线模式下代码无法正常为HR用户工作,因为在此过程中事件不会触发,代码将在进行同步时开始执行,因此,我们必须在代码中考虑好离线模式下运行的场景。最简单的办法是提醒用户,此操作无法在离线模式下使用。

并发的后果

有一个我们需要认真考虑的问题是,如何使代码在多用户环境下运行。SharePoint的对象模型已经精心设计为适应多用户操作,但开发人员还是有责任验证运行在这种环境下的代码,使得对列表的修改不会干扰其他用户的工作流程,至少保证不要出现不良的操作 。还应该认识到事件接收器内的代码所进行的SharePoint列表修改对于任何其他用户都是可见的,这一点或许是你所期望的,或许不是。

为了简便起见,我选择使用一个专用的表格来接受筛选条件,只使用插入事件,这样我就不必担心两个用户在一个时间点附件开始修改时,可能出现的更新冲突。另一种可能的方法是顺序分配行给用户,每个用户只更新其自身的行。此外,因为此演示的目的是为每个用户提供一个“工作区”来使用自己的过滤器,所以必须区分过滤器的数据是属于什么用户的,以及如何处理某个用户请求的一条已经由其他用户已经“签出”的雇员记录。因此,如果是在生产环境,我必须实现某些审计,以跟踪雇员表,标识出哪一行被哪一个action_id所调用,从而有效关联用户的请求条件所对应的Action列表项和雇员记录。在另一个不同的应用中,可能没有这个必要,但开发人员仍然有责任验证代码在多用户环境下的运行没有问题。

注意事项

巨大的能力意味着巨大的责任。这也正是在ItemAdding事件中只加载一条记录的一个原因,基于同样的道理Access团队限制了Web宏在修改数据前事件中的一些操作:ItemAdding是同步的,过长的操作可能会导致超时等其他问题。这一个点在浏览器处于断开状态时特别明显 - 如果它不能从服务器获得响应,它可能会超时,用户可能需要重新执行该操作。这不是个令人愉快的状态!还有另外一个缩小Web宏范围的原因:SharePoint服务器是一个共享的资源,一个解决方案的糟糕表现会加深对SharePoint性能的伤害,危害所有用户的体验。虽然SharePoint团队提供了类似沙箱解决方案的功能来处理这样的场景,但你也不能总是坐在系统管理员对面,偷偷跟他解释你做了什么!

正如在视频中看到的,Visual Studio使得我们可以通过将代码附加到一个IIS工作进程(w3wp.exe)实现调试和单步执行,你可以观察代码的运行。但是,如果设置断点并通过单步执行代码,可能会导致超时, Access Services会断开在客户端中的表,并假设最近的保存已失败。.当单步执行代码通过后,且Access客户端已被重新定向到数据表,它会要求你重试之前的保存操作,但实际上是多余的,因为保存已经成功了,只是完成操作的时间点没有在超时长度范围内。

在跟踪ItemAdded事件(应该是异步执行)时,我所观察到的另一个异常事件是,当在ItemAdded中添加断点时,浏览器会继续等待ItemAdded执行完成以触发该断点。但是,如果没有在ItemAdded中设置断点,则该事件就又变成异步执行的了。

结论

考虑到所有这些因素后,再来回答客户问我们这个问题,“Access Service里可以这么做吗?”我们不必将自己只局限于Web宏。对于绝大多数的解决方案里,web宏将承担很重要的工作。在剩下的百分之十的解决方案中,我们可以告诉客户,可以从另一方面考虑,依靠.NET来实现。

虽然本演示提供了一种“链接”到外部数据源的方法,并且已表示这一功能将在未来版本中成为标准的功能,但是这却不是它唯一用途。这里有一些其他可能的用途,以帮助你进行头脑风暴:

一)使用SQL Server Reporting Services来生成一个复杂的报告,并通过电子邮件异步的发送给用户。他们只需要填写一份web表单,几分钟后,报表就会出现在他们的邮箱里。

二) 挂接到现有的SharePoint工作流,这样Access web应用程序就可以参与到一个较长的,复杂的工作流里。

三)与其他Web服务的通信。

四)________(等你来填空)

 

参考资料

Going beyond Web Macros: Using Event Receivers & .NET with Access Services


posted @ 2010-09-17 16:42  Sunmoonfire  阅读(1783)  评论(0编辑  收藏  举报