每个开发人员都知道,要让完全不同的系统协调工作是一个常见的难题。不论部门大小,许多 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。
编程模型:.NET 和 COM
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 中,选择 Project —— Add Reference。
选择 Add Reference 对话框的 COM 选项附签,然后双击所列出的合适的 Type Library 文件。
单击 OK 添加 reference。
至此,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。
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
可以将生成的 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)。这段代码做了如下工作:
%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 窗体创建的文档,包含用以下公式填充的一列:
ASP.NET 应用程序
可以在 ASP.NET 应用程序中使用这个 Web 服务来将 Web 服务调用获取的数值填充到 Web 页面的字段中。首先,在 .NET 应用程序中向 Web 服务添加 reference。图 5 显示了添加的 Web reference,以及用户输入服务地址并单击 Go 之后所列出的方法。
图 5. 向 .NET 应用程序添加 Web 服务 reference
在 Add Web Reference 对话框的 Web reference 名字字段中键入在代码中访问 Web 服务所使用的名字。在添加 Web reference 之后,代码就会简单易懂。清单 5 显示了后面跟着 codebehind 文件的 ASP.NET 页面的代码。 清单 5. 使用 Domino Web 服务的 ASP.NET Web 窗体
清单 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 资源变得十分容易。