使用对象数据库的替代智能客户端离线应用程序块






3.48/5 (14投票s)
微软智能客户端离线应用程序块的替代方案,构建于开源的 Db4O 之上。
引言
大约六个月前,我有机会参与我们公司首个智能客户端应用程序的开发。毋庸置疑,作为一家非常成功的网站开发公司,这被视为一个高风险高回报的项目。如果我们成功了,我们将打开一个全新的市场;如果我们失败了,就得回到旧的计划(至少在一段时间内)。
所以我们采取了稳妥的策略,坚持使用推荐的应用程序块和最稳定的开发工具版本等。令人惊讶的是,我们使用 .NET 1.1、2005 年和 2004 年发布的 MS DAAB(数据访问应用程序块)和 SOAB(智能客户端离线应用程序块)以及 WSE 2.0 SP 3,在创纪录的时间内完成了我们的第一个原型。
现在,我们已经将代码迁移到了 .NET 2.0、新的 DAAB 和 WSE 3.0。但是,新的离线应用程序块在哪里呢?在使用 2004 年的最后一个版本时,我们发现它并没有真正提供我们所需的所有功能。我们曾希望下一个版本能包含其中的一些功能。嗯,对我个人而言,我厌倦了等待……
基于 Db4O 的 SOAB
现在,如果你看微软的离线应用程序块,它将缓存对象和命令队列项序列化到其数据库中。一旦这样做,你就会失去很多灵活性。当然,在关系型数据库(RDBMS)的世界里,几乎没有其他选择。在 MSDE 的世界里,没有办法设计一个缓存数据库或队列数据库来存储任意的应用程序特定对象。对于 SQL 2005,我想你可以使用 XML 列和 XQuery 来实现这一点。但话又说回来,有一个更好的方法。
去年,在为我的毕业设计做研究时,我了解了 Db4O。Db4O 是一个用于 .NET/MONO 和 Java 的开源对象数据库。我当时只是抱着玩玩的想法,想用对象数据库来做缓存/队列数据库,结果不到五分钟,它的优势就显而易见了。缺点是免费版的 Db4O 是在 GPL 许可下分发的,如果你想在商业应用程序中使用它,你需要从开发者那里获得商业许可。
新 SOAB 的优点
- 简化部署。
无需部署 SQL Server,无需运行数据库脚本,无需创建登录名;只需将应用程序复制到你想要的位置并运行它。缓存和队列数据库将在首次需要时动态创建。就是这么简单!这意味着它可以实现基于 SQL Server 的智能客户端无法实现的功能:能够从一个小小的 U 盘上运行。
- 相互依赖的队列元素。
想象一个用于图书编目的智能客户端应用程序。中央服务器维护着一个图书和作者的目录。要为一本书编目,其作者必须存在于数据库中。(这样的应用程序究竟如何有用,我们在这里不关心 :)。智能客户端在上线时会从服务器下载作者列表。当一本书连同其作者需要在离线状态下创建时,问题就出现了。当离线创建一个作者时,一个“创建作者”请求会进入命令队列。同样,当创建一本书时,一个“创建书籍”请求也会进入队列。新的 SOAB 提供了一种机制,智能客户端的程序员可以通过它来指定,一个“创建书籍”的请求只有在其相关的作者对象成功存入中央数据库后才应被执行(传输到 Web 服务)。
- Db4O 的所有荣耀。
Db4O 是一个功能强大的对象数据库,具有原生查询、SODA 查询等高级功能。虽然
SOAB.Data.Database
类试图封装与底层 Db4O 对象容器的大部分交互,但对于勇敢的人来说,它仍然是可访问的。这开启了许多额外的可能性,例如修改/删除队列中的单个项目、查询缓存或队列中的特定项目、确定在执行排队元素时要修改哪些项目等。 - 代码库精简,基于 .NET 2.0 构建。
该应用程序块总共只有大约十五个类和接口:其中你只需要关心六个。你可能花不到两个小时就能弄清楚如何充分利用 SOAB 的潜力。新的 SOAB 使用了泛型和新的基于事件的异步调用方法,打破了旧的做事方式。
使用 SOAB
使用 SOAB 几乎不费吹灰之力;只需遵循以下几个步骤:
- 在你的智能客户端应用程序中,添加一个派生自
SOAB.ServiceAgent
的类。在示例应用程序中,我将派生类 (ServiceClient
) 实现为单例。虽然这不总是必需的,但这样做可能是一个好主意。 - 创建一个
ServiceContext
类并保留它,因为它可以用于所有的服务请求。由于ServiceClient
是一个单例,我已将ServiceContext
实例作为其字段之一。ServiceContext
类基本上是你的 Web 服务代理类的构建器。请确保将代理的完全限定类名传递给其构造函数。 - 当你需要调用一个 Web 方法时,创建并填充一个
ServiceRequest
对象,并使用QueueManager.Instance.Enqueue(...)
方法将其入队。要填充ServiceRequest
,请赋值:- 将
ServiceContext
实例赋给ServiceRequest.ServiceContext
属性。 - 将你正在调用的 Web 方法的名称赋给
ServiceRequest.RemoteMethodName
。 - 将 Web 方法的参数赋给
ServiceRequest.Payload
。 - 将一个属于
ServiceAgent
派生类的、接受ServiceResponse
参数的方法的名称赋给ServiceRequest.CallbackMethodName
。
- 将
请记住,你应该将你的 Web 方法编写为只接受单个参数。虽然可以将 Payload
属性编写为一个数组,从而允许 Web 方法有多个参数,但这会降低效率并导致智能客户端端的代码变得杂乱。
一旦 ServiceRequest
被执行,CallbackMethodName
属性中指定的方法就会被调用。你应该使用传递给该方法的 ServiceResponse
对象中的属性来确定请求是成功、被取消,还是产生了异常。
如果你有任何可能依赖于其他对象的对象类型(例如依赖于 Author
的 Book
),你应该在依赖类上实现 ICacheDependant
接口。在 IsResolved
属性的 getter 中提供依赖解析逻辑,如果对象已准备好传输到 Web 方法,则返回 true
。
演示应用程序
我编写了一个非常精巧的应用程序,用来演示 SOAB 的一些关键特性。
- 缓存和命令队列。
- 相互依赖的服务请求。
- 查看命令队列。
为了测试,我使用了一种伪连接检测策略,该策略根据一个布尔字段来模拟在线和离线状态。
代码包不包含所需的 Db4O DLL。这是因为我不清楚与我的非 GPL 源代码一起分发该库所涉及的许可问题。你当然可以从 www.db4o.com 免费获取 Db4O。但是,要在商业应用程序中使用该库,请务必获得商业许可。你可以在 Apache 许可证 2.0 下自由使用我的源代码,并在适当的地方注明出处。
结论
有关我的离线应用程序块的更多信息,请阅读此博客。