每个开发人员都知道,要让完全不同的系统协调工作是一个常见的难题。不论部门大小,许多 IT 部门都没有标准化到一个开发平台上。公司并购经常会引进不同的元素和完全不同的系统,比如比较旧的应用程序与新的平台共存。虽然像 IBM Lotus Domino 这样的产品为开发企业应用程序提供了功能强大的平台,但是与许多企业级软件产品一样,它仍需要与其他系统进行互操作。
本文学习在 Microsoft .NET 开发平台上集成 Lotus Domino;重点讨论 Lotus Domino 7、Microsoft .NET Framework V1.1 和 Microsoft Visual Studio .NET 2003。本文假设您是一名有经验的 Notes/Domino 应用程序开发人员,熟悉 LotusScript 和 Domino Web 服务设计元素,并了解 Microsoft .NET Framework。
注意:本文描述的解决方法可以用于 Microsoft .NET Framework V2.0 和 V1.1。
Lotus Domino 是一个用于企业数据的优秀平台。可以通过简单的 Open Database Connectivity(ODBC)或者使用 IBM Lotus Enterprise Integrator 和 Domino Enterprise Connection Service 所提供的强大的功能很容易地访问非 Domino 数据源。可是,有时可能需要从其它的应用程序访问 Lotus Domino。所幸的是,可以向其它使用组件对象模型(Component Object Model,COM)的应用程序开放 Domino 环境,允许外部应用程序通过 COM 使用各种开放的对象和类很容易地与 Lotus Domino 协同工作。一个很好的例子是在使用 .NET Framework 构建的应用程序中集成 Lotus Domino 功能。
COM 是一种相对比较旧的技术,但是 .NET 提供了可调用的包装器,允许 .NET 和 COM 进行互操作。通过执行时可调用的包装器,可以在 .NET 中使用 COM 组件。这听起来很复杂,但是使用开发工具(比如 Microsoft Visual Studio)是很简单的。虽然必须为 COM 组件创建包装器并在 .NET 应用程序中通过 interop 使用它们,但是当执行以下步骤时, Visual Studio .NET 会自动地创建包装器:
至此,Visual Studio .NET 把 COM Type Library 文件的对象和成员转换成对等的 .NET 的集合。当生成 .NET 集合的时候,可以使用它们的类创建对象并调用成员,就像 COM 对象和成员是原生 .NET 的类和成员一样。也可以与这个过程相反,在基于 COM 的应用程序中使用 .NET 集合。
注意:如果没有使用 Visual Studio .NET,那么 .NET Framework 软件开发包(SDK)提供了 Type Library Importer(tlbimp.exe)工具,这个命令行工具可以把 COM Type Library 包含的类和接口转换成元数据。这个工具为类型信息自动地创建一个 interop 集合和命名空间。
说明这个步骤的一个好方法是在 Visual Studio 解决方案中产生可用的 Lotus Domino 对象。图 1 显示了 Add Reference 对话框,并突出显示了 Lotus Domino Objects。
从 Lotus Notes 和 Domino 5.02b 开始,可以使用 Lotus Domino Objects 通过 COM 来访问 Domino 对象,在 Domino 服务器或者任何一个 Notes 客户机(IBM Lotus Domino Designer、Lotus Domino Administrator 或者 Lotus Notes)的安装中都包括 Lotus Domino Objects。在基本安装目录中可以找到 domobj.tlb。Lotus Domino 6.x 包括 Lotus Domino Objects 版本 1.1;Lotus Domino 7 包括版本 1.2。
虽然必须安装 Lotus Domino,但是它不必运行。另一个关键点是通过 COM 访问 Lotus Domino 时只有 backend 类可用,因此没有可以访问的用户接口(UI)类。
通过 COM 访问 Lotus Domino 资源类似于在 Lotus Domino Designer 中使用 Java 或者 LotusScript 编程语言;任何与 Lotus Domino 的交互都开始于一个会话。Lotus Domino Objects 包括所有通过 COM 使用 Lotus Domino 所必需的类,而且 COM 对象对于任何使用 LotusScript 或者 Java 语言开发 Domino 对象模型的开发人员来说都是很熟悉的。
包含在 Visual Studio 集成开发环境(IDE)中的是 IntelliSense 特性,它通过在键入的时候提供上下文敏感的帮助和自动完成代码来帮助使用 COM 对象,也因此减少拼写错误。
第一个例子构建了一个 .NET Windows 应用程序,从 Domino Directory 获取数据并使用 Lotus Domino 发送电子邮件消息(参见图 2)。.NET Windows 应用程序使用了以下 .NET 元素:
清单 1 显示了大部分的应用程序代码(省略了为应用程序生成的 Visual Studio 代码)。单击 Send 按钮时调用 btnSend_Click 方法。这个方法创建 NotesSession、NotesDatabase、NotesView 和 NotesDocument 对象,依次用于创建和发送消息。
应用程序加载时调用 Form_Load 方法。它使用 NotesSession、NotesDatabase、NotesView 和 NotesDocument 对象填充 ComboBox。在视图中把文档的单独值添加到 ComboBox 控件中。附加域名到选定的人名来协助消息分发。
清单 1. .NET Windows 应用程序
Private Sub btnSend_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles btnSend.Click Try Dim ns As New NotesSession Dim db As NotesDatabase Dim doc As NotesDocument If Not (ns Is Nothing) Then ns.Initialize("password") db = ns.GetDatabase("ServerName//Domain", "names.nsf", False) If Not (db Is Nothing) Then doc = db.CreateDocument() doc.ReplaceItemValue("Form", "Memo") doc.ReplaceItemValue("SendTo", cboNames.SelectedText + "//Domain") doc.ReplaceItemValue("Subject", txtSubject.Text) Dim rt As NotesRichTextItem rt = doc.CreateRichTextItem("Body") rt.AppendText(rtbBody.Text) doc.Send(False) rt = Nothing doc = Nothing lblMessage.Text = "Message Sent." End If db = Nothing ns = Nothing End If Catch ex As Exception lblMessage.Text = "Error: " + ex.Message.ToString() End Try End Sub |
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles MyBase.Load Dim s As New NotesSession s.Initialize() Dim db As NotesDatabase Dim vw As NotesView Dim doc As NotesDocument db = s.GetDatabase("ServerName//Domain", "names.nsf", False) If Not (db Is Nothing) Then vw = db.GetView("_People") doc = vw.GetFirstDocument() While Not (doc Is Nothing) cboNames.Items.Add(doc.GetFirstItem("FirstName").Text + " " + doc.GetFirstItem("LastName").Text) doc = vw.GetNextDocument(doc) End While End If End Sub |
要发送电子邮件消息,应用程序完成如下步骤:
使用 COM 不仅仅局限在 Windows 应用程序中。还可以在 Web 或者其它的应用程序类型中使用。
下面,我们学习使用其它的方法与 Lotus Domino 通信,比如 Web 服务和 XML。
Lotus Domino Objects 提供了一种非常好的在非 Domino 应用程序中访问 Domino 资源的方法,而这并不是惟一可用的方法。另一种集成两个系统的方法涉及到 Web 标准和在 .NET 代码中使用 Domino Web 服务。
Lotus Domino 7 中添加了一个Web 服务设计元素。基于 Domino 的 Web 服务与其他标准的 Web 服务一样被使用。利用 Web 服务描述语言(Web Services Description Language,WSDL)可以找到 Web 服务中的可用方法。通过带有 ?wsdl 命令的 Web 服务 URL 可以使用基于 Domino 的 Web 服务的 WSDL。例如,下面的 URL 获取名为 testws 的 Web 服务的 WSDL:
http://localhost/dominodatabase.nsf/testws?wsdl
Visual Studio .NET IDE 使得在项目中使用 Web 服务变得很容易。为此,要为项目添加一个 Web Reference,先右键单击项目,然后选择 Add Web Reference,如图 3 所示。
在集成中可以使用的 Domino 的另一个特性是 XML。访问普通 Domino 元素的 XML 比较容易。一个很好的例子是使用 Domino 视图。使用视图的 URL 和 ?ReadViewEntries 命令可以很容易地访问视图的 XML。例如,使用下面的 URL,可以在 Domino Directory 7 中访问 _People 视图的 XML:
http://domain/names.nsf/_People?ReadViewEntries
清单 2 提供了一个在 Lotus Domino 7 服务器的目录中使用 ?ReadViewEntries 命令生成 XML 的示例。
清单 2. 使用 ?ReadViewEntries 命令生成的 XML
<?xml version="1.0" encoding="UTF-8" ?> <viewentries toplevelentries="3"> <viewentry position="1" unid="A819181402AC862F85257199007D02BC" noteid="1342" siblings="3"> <entrydata columnnumber="0" name="$18"> <number>0</number> </entrydata> <entrydata columnnumber="1" name="$17"> <text>Patton , Tony</text> </entrydata> <entrydata columnnumber="2" name="$12"> <text /> </entrydata> <entrydata columnnumber="3" name="CompanyName"> <text /> </entrydata> <entrydata columnnumber="4" name="$16"> <text>Tony Patton/Patton@Test</text> </entrydata> <entrydata columnnumber="5" name="$21"> <text>TestServer/Test</text> </entrydata> </viewentry> </viewentries> |
可以将生成的 XML 与 .NET 的 XML 支持相结合,交换或者使用 Domino 数据。例如,可以把生成的 XML 与 Microsoft ASP.NET 数据控件结合。如清单 3 所示的 ASP.NET 页面使用 _People 视图的 XML 填充 DropDownList 控件。
清单 3. 使用 _People 视图中的 XML 的 ASP.NET 页面
<%@ Import Namespace="System.Net" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.XML" %> <%@ Page Language="vb" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Populate DropDownList via XML</title> <script language="vb" runat="server"> Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) If Not (IsPostBack) Then Try GetData() Catch ex As Exception Response.Write(ex.ToString()) End Try End If End Sub Public Sub GetData() Dim strResult As String Dim objResponse As WebResponse Dim objRequest As WebRequest objRequest = _ HttpWebRequest.Create("http://192.168.1.103/names.nsf/ _People?ReadViewEntries") objResponse = objRequest.GetResponse() Dim sr As StreamReader sr = New StreamReader(objResponse.GetResponseStream()) strResult = sr.ReadToEnd() Dim doc As New XmlDocument sr.Close() doc.LoadXml(strResult) Dim nodes As XmlNodeList nodes = _ doc.SelectNodes("viewentries/viewentry/entrydata _[@columnnumber=""1""]") ddlNames.DataSource = nodes ddlNames.DataTextField = "innertext" ddlNames.DataValueField = "innertext" ddlNames.DataBind() End Sub </script> </head> <body> <form id="frmPopulateControlViaXML" method="post" runat="server"> <asp:DropDownList id="ddlNames" runat="server" Width="336px" /> </form> </body> </html> |
这段代码是使用 Microsoft Visual Basic .NET 的标准的 ASP.NET 页面(没有 codebehind)。这段代码做了如下工作:
在 Web 页面上显示结果,并可以从列表中选择一个名字。
第一步使用 .NET System.Net 和 System.IO 命名空间,用于网络(Internet)请求和返回的数据。这个页面使用 WebRequest 对象调用合适的 URL,用 WebResponse 对象获得响应。
结合 WebResponse 对象的 ResponseStream 属性,使用 StreamReader 对象读取 Web 请求返回的 XML。然后,使用 XML 填充并实例化 XMLDocument 对象。从 XMLDocument 对象中选中单独的节点并存储在 XMLNodeList 对象中。使用 XPath 表达式,可以选择合适的节点。
使用选中的节点集填充数据控件,填充通过指定用于在控件中显示值及当用户选中一个单独的条目时返回的值的单独列(innertext)实现。所有操作都和获取 XML 并填充 GetData 方法的数据控件有关,并在 ASP.NET Web 窗体的 Page_Load 事件中调用。要想仅当页面最初被请求时填充控件,需要使用页面的 IsPostBack 属性,以确保页面只加载一次,而不是页面刷新或者重载。最后,页面使用 try 块捕获任何不可预见的错误,并在页面上显示错误描述。图 4 显示了 ASP.NET 页面。
为了演示说明,把 Domino 邮件应用程序的例子与 Domino Web 服务相结合,通过 Web 服务调用获取并显示 Domino 数据。但是,在介绍 .NET 应用程序中使用 Web 服务之前,必须首先介绍一下 Domino Web 服务。
Lotus Domino Web 服务获取 Domino 数据
|
Web 服务很简单。它接受一个用户名称(格式就像姓后面跟着名,中间用逗号分开 —— 在前面的例子中使用过的格式)并从 Domino Directory 获取与该用户相关联的值。服务在 LotusScript 中编写。
示例的服务位于一个名为 GetUserInfo 的类,它包括以下公有方法:
GetPhoneNumber:为用户返回办公室电话号码
GetAddress:为用户返回办公室地址
GetCity:为用户返回办公室所在城市名称
GetState:为用户返回办公室所在州名称
GetZip:为用户返回办公室所属邮政编码
GetCountry:为用户返回办公室所在国家名称
所有方法都接受一个字符串参数(用户名称)并返回一个字符串值。NotesSession、NotesDatabase 和 NotesView 对象用类的新块中发生的初始化定义。清单 4 显示了 Web 服务代码。
清单 4. Domino Web 服务
%INCLUDE "lsxsd.lss" Dim session As NotesSession Dim db As NotesDatabase Dim vw As NotesView Dim doc As NotesDocument Public Class GetUserInfo Sub New Set s = New NotesSession Set db = s.CurrentDatabase Set vw = db.GetView("(PeopleLookup)") End Sub Public Function GetPhoneNumber(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetPhoneNumber = doc.GetItemValue("OfficePhoneNumber")(0) Else GetPhoneNumber = "" End If End Function Public Function GetCity(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetCity = doc.GetItemValue("OfficeCity")(0) Else GetCity = "" End If End Function Public Function GetState(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetState = doc.GetItemValue("OfficeState")(0) Else GetState = "" End If End Function Public Function GetAddress(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetAddress = doc.GetItemValue("OfficeStreetAddress")(0) Else GetAddress = "" End If End Function Public function GetZip(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetZip = doc.GetItemValue("OfficeZip")(0) Else GetZip = "" End If End Function Public function GetCountry(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetCountry = doc.GetItemValue("OfficeCountry")(0) Else GetCountry = "" End If End Function Public Function GetFullName(uName As String) As String Set doc = vw.GetDocumentByKey(uName) If Not (doc Is Nothing) Then GetFullName = doc.GetItemValue("FirstName")(0) + " " + _ doc.GetItemValue("LastName")(0) Else GetFullName = "" End If End Function End Class |
可以在浏览器中键入 URL http://dominoserver/names.nsf/GerUserInfo?wsdl
,很容易地查看到这个 Web 服务的 WSDL。
注意:每个方法查找都会添加要用到的一个新的视图。这个隐藏视图称为 PeopleLookup,只显示 Person 窗体创建的文档,包含用以下公式填充的一列:
@Trim(@Subset(LastName;1))+@If(Firstname !="";" , "+@Trim(@Subset(FirstName;1));"")+@If(MiddleInitial !="";" "+@Trim(@Subset(MiddleInitial;1));"")
可以在 ASP.NET 应用程序中使用这个 Web 服务来将 Web 服务调用获取的数值填充到 Web 页面的字段中。首先,在 .NET 应用程序中向 Web 服务添加 reference。图 5 显示了添加的 Web reference,以及用户输入服务地址并单击 Go 之后所列出的方法。
在 Add Web Reference 对话框的 Web reference 名字字段中键入在代码中访问 Web 服务所使用的名字。在添加 Web reference 之后,代码就会简单易懂。清单 5 显示了后面跟着 codebehind 文件的 ASP.NET 页面的代码。
清单 5. 使用 Domino Web 服务的 ASP.NET Web 窗体
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="DominoTest.aspx.vb" Inherits="DominoIntegration.Test2"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Domino Web Service Demo</title> </head> <body> <form id="frmDominoIntegration" method="post" runat="server"> <asp:Label id="lblSelect" runat="server" Width="144px"> Select Name:</asp:Label> <asp:DropDownList id="ddlNames" runat="server" OnSelectedIndexChanged="ddlNames_SelectedIndexChanged" AutoPostBack="True" Width="336px"></asp:DropDownList><br> <asp:Label id="lblName" runat="server" Width="144px"> Address:</asp:Label> <asp:TextBox id="txtFullName" runat="server" Width="360px" /><br> <asp:Label id="lblAddress" runat="server" width="144px"> Address:</asp:Label> <asp:TextBox id="txtAddress" runat="server" Width="360px" /><br> <asp:Label id="lblCityState" runat="server" width="144px"> City, State</asp:Label> <asp:TextBox id="txtCity" runat="server" Width="264px" /><br> <asp:Label id="lblComma" runat="server" Width="8px" Font-Bold="True"> ,</asp:Label> <asp:TextBox id="txtState" runat="server" Width="80px" /><br> <asp:Label id="lblZip" runat="server" Width="144px"> Zip Code:</asp:Label> <asp:TextBox id="txtZip" runat="server" Width="200px" /><br> <asp:Label id="lblCountry" runat="server" Width="144px"> Country:</asp:Label> <asp:TextBox id="txtCountry" runat="server" Width="200px" /><br> <asp:Label id="lblPhone" runat="server" Width="144px"> Phone:</asp:Label> <asp:TextBox id="txtPhoneNumber" runat="server" Width="200px" /><br> </form> </body> </html> |
清单 6 显示了用 Visual Basic 写的 ASP.NET Web 窗体的 codebehind 文件。
清单 6. ASP.NET Web 窗体的 Visual Basic. NET 代码
Imports System.Net Imports System.IO Imports System.Xml Public Class DominoIntegration Inherits System.Web.UI.Page Protected WithEvents ddlNames As DropDownList Protected WithEvents lblSelect As Label Protected WithEvents txtFullName As TextBox Protected WithEvents lblName As Label Protected WithEvents lblPhone As Label Protected WithEvents lblCountry As Label Protected WithEvents txtCountry As TextBox Protected WithEvents lblZip As Label Protected WithEvents lblCityState As Label Protected WithEvents txtZip As TextBox Protected WithEvents txtState As TextBox Protected WithEvents txtCity As TextBox Protected WithEvents txtPhoneNumber As TextBox Protected WithEvents txtAddress As TextBox Protected WithEvents lblAddress As Label Protected WithEvents lblComma As Label Private Sub Page_Init(ByVal sender As System.Object, ByVal e As System.EventArgs) _ Handles MyBase.Init InitializeComponent() End Sub Public Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)Handles MyBase.Load If Not (Me.IsPostBack) Then Try GetData() Catch ex As Exception Response.Write(ex.ToString()) End Try End If End Sub Public Sub GetData() Dim strResult As String Dim objResponse As WebResponse Dim objRequest As WebRequest objRequest = System.Net.HttpWebRequest.Create( "http://192.168.1.103/names.nsf/_People?ReadViewEntries") objResponse = objRequest.GetResponse() Dim sr As System.IO.StreamReader sr = New StreamReader(objResponse.GetResponseStream()) strResult = sr.ReadToEnd() Dim doc As New XmlDocument sr.Close() doc.LoadXml(strResult) Dim nodes As XmlNodeList nodes = doc.SelectNodes("viewentries/viewentry/entrydata _[@columnnumber=""1""]") ddlNames.DataSource = nodes ddlNames.DataTextField = "innertext" ddlNames.DataValueField = "innertext" ddlNames.DataBind() End Sub Public Sub ddlNames_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Dim ws As New WebReference.GetUserInfoService txtFullName.Text = ws.GETFULLNAME(ddlNames.SelectedValue.ToString()) txtAddress.Text = ws.GETADDRESS(ddlNames.SelectedValue.ToString()) txtCity.Text = ws.GETCITY(ddlNames.SelectedValue.ToString()) txtState.Text = ws.GETSTATE(ddlNames.SelectedValue.ToString()) txtZip.Text = ws.GETZIP(ddlNames.SelectedValue.ToString()) txtCountry.Text = ws.GETCOUNTRY(ddlNames.SelectedValue.ToString()) txtPhoneNumber.Text = ws.GETPHONENUMBER(ddlNames.SelectedValue.ToString()) End Sub End Class |
Web 窗体包括一个清单 3 所描述的 GetData 方法,但是它现在能够使用文本字段接收通过 Web 服务调用获取的数据。在 DropDownList 控件中执行选择的时候,调用 Web 服务。用户从列表中选择一个值时,自动调用控件的 SelectedIndex 事件(控件的 AutoPostBack 属性设置为 True)。
在控件中选中的值传递到 Web 服务,以获取单独调用得到的每一个数据(比如地址和城市)。这段代码使用在 Add Web Reference 对话框添加的 WebReference 对象访问 Web 服务。
结束语
集成完全不同的系统对于企业开发人员来说是一项常见的开发任务。完成这个工作的一种方法是将 Microsoft .NET 平台与 Lotus Domino 结合,还可以使用其它方法来实现这种集成,例如使用 COM 技术以及已经可用的 XML 和 Web 服务。这些方法使得为了满足业务需求而整合各种 IT 资源变得十分容易。