Asp.Net 和 AJAX.Net 的区别第2/2页

 更新时间:2007年03月07日 00:00:00   作者:  

这与您通常使用的任何其他函数一样:它需要我们想要获得的国家/地区的 ID,并将该请求传递给 DAL。唯一的不同是我们已使用 AjaxMethodAttribute 标记了该方法。最后剩余的服务器端步骤是通过调用 RegisterTypeForAjax 使用 Ajax.NET 来注册包含上述方法的类(在此情况下,是我们的下面的代码)。

//C#
Ajax.Utility.RegisterTypeForAjax(typeof(Sample));
'VB.NET
Ajax.Utility.RegisterTypeForAjax(GetType(Sample))

我们已基本完成;剩余的就是从 JavaScript 调用 GetStates 方法和处理响应。当用户从国家/地区列表中选择新项时,我们想在逻辑上调用 GetStates。为此,我们将触发 JavaScript onChange 事件。这样就稍微更改了我们的 Web 窗体代码。

<asp:DropDownList onChange="LoadStates(this)"
ID="countries" Runat="server" />

JavaScript LoadStates 函数将负责通过由 Ajax.NET 创建的代理发出异步请求。请记住,默认情况下,Ajax.NET 创建的代理的格式为 <RegisteredTypeName>.<ServerSideMethodName>。在我们的示例中,将为 Sample.GetStates。我们还想传入国家/地区 ID 参数和完成服务器端函数后 Ajax.NET 应调用的回调函数。

//JavaScript
function LoadStates(countries)
{
var countryId = countries.options[countries.selectedIndex].value;
Sample.GetStates(countryId, LoadStates_CallBack);
}

最后一个步骤是处理我们的 LoadStates_CallBack 函数中的响应。Ajax.NET 最有用的功能大概是它支持很多 .NET 类型(我已经多次提到这一点)。回顾一下返回 DataView 的服务端函数。JavaScript 知道 DataView 什么?什么也不知道,但是 JavaScript 是面向对象的语言,而且 Ajax.NET 不只能够创建与 .NET DataView 相似的对象,还能将该函数返回的值映射到 JavaScript 副本。您应该记住 JavaScript DataView 只不过是实际 DataView 的副本,目前除了能够遍历行和访问列值以外不支持其他更多功能(例如设置 RowFilter 或 Sort 属性的功能)。

function LoadStates_CallBack(response)
{
//如果服务器端代码出现异常
if (response.error != null)
{
//我们应该能做得更好
alert(response.error);
return;
}
var states = response.value;
//如果不是我们所希望的响应
if (states == null || typeof(states) != "object")
{
return;
}
//获得州下拉列表
var statesList = document.getElementById("states");
statesList.options.length = 0; //重置州下拉列表
//记住,其长度不是 JavaScript 中的 Length
for (var i = 0; i < states.length; ++i)
{
//如命名属性一样公开行的列
statesList.options[statesList.options.length] =
new Option(states[i].State, states[i].Id);
}
}

经过一些错误检查之后,前面的 JavaScript 获得州下拉列表,遍历响应的值,并动态地将选项添加到该下拉列表中。代码清晰、简单并与 C# 和 Visual Basic .NET 非常相似。就我个人而言(作为基于服务器端变量创建了 JavaScript 数组并将它们链接在一起的开发人员),我还要一段时间才能相信它真的起作用了。

有一个可能不太明显的主要问题。由于 DropDownList 是在 JavaScript 中动态创建的,因此它的项不属于 ViewState,并且不被维护。这意味着按钮的 OnClick 事件处理程序需要进行一些额外的修改。

'VB.NET
Private Sub submit_Click(sender As Object, e As EventArgs)
Dim selectedStateId As String = Request.Form(states.UniqueID)
'应进行一些用户验证...
states.DataSource =
DAL.GetCountryStates(Convert.ToInt32(countries.SelectedIndex))
states.DataTextField = "State"
states.DataValueField = "Id"
states.DataBind()
states.SelectedIndex =
states.Items.IndexOf(states.Items.FindByValue(selectedStateId))
End Sub

首先,我们不能使用 states.SelectedValue 属性,而必须使用 Request.Form。其次,如果我们想向用户重新显示该列表,需要重新使用相同的数据访问方法绑定州 DropDownList。最后,必须以编程方式设置选定的值。

示例 2:文档锁定程序

对于下一个示例,我们将引入更加完整的功能,然后使用 AJAX 改进它。此示例属于简单的文档管理系统。如任何正式的文档管理系统一样,我们必须提供并发管理。即,我们需要一种方法来处理两个用户尝试编辑同一个文档的问题。我们将通过创建某种类型的锁定机制,来使正在编辑的文档不能再由另一个用户编辑,从而达到上述目的。我们将利用 AJAX 让用户有更愉快的锁定机制体验。首先,我们将创建用户尝试编辑但无法编辑(因为其他用户正在编辑该文档)的文档队列,当这些文档可用时自动通知用户。其次,我们将确保当用户关闭其浏览器或导航到其他位置时,解除对文档的锁定。后一个功能帮助确保文档不会永远处于锁定状态。为此,在本指南中,我们将跳过与 AJAX 实现不相关的功能;但是,可下载项目包含所有功能。

首先,当用户尝试编辑文档时,我们会尝试对其建立排它锁,如果失败,我们会将此文档添加到用户的队列然后使其返回到主页。对此处的 AJAX 没有什么特别之处,但是我们将查看一下代码,以便给出示例必要的上下文。在用于编辑的 PageOnLoad 事件中,添加以下代码。

//C#
if (!Page.IsPostBack)
{
//应验证用户输入
Document document = GetDocument(Request.QueryString["id"]);
//我们拥有此文档,但不能编辑它!
if (!Locker.AcquireLock(document))
{
//让我们将它添加到要查看的用户文档列表
User.CurrentUser.AddDocumentToQueue(document.DocumentId);
Response.Redirect("DocumentList.aspx");
}
//好了,我们拥有此文档,并且可以编辑它
//...
}

关键行的位置是将文档添加到当前用户的队列中(这会将文档添加到会话中)。接下来,我们将创建用户控件,该控件可以被放置到任何页上,用于当队列文档可用时通知用户。此用户控件将包含一个 AJAX 方法以及注册 AJAX 的类所需的代码。

'VB.NET
Private Sub Page_Load(s As Object, e As EventArgs)
Handles MyBase.Load
Ajax.Utility.RegisterTypeForAjax(GetType(UnlockNotifier))
End Sub
'遍历队列文档并检查它们是否可用
<Ajax.AjaxMethod()> _
Public Function GetUnlockedDocuments() As DocumentCollection
'获得属于用户的所有队列文档的 ID
Dim queuedDocument As ArrayList = User.CurrentUser.DocumentQueue
Dim unlocked As DocumentCollection = New DocumentCollection
For Each documentId As Integer In queuedDocumentIds
'如果队列文档不再被锁定
If Not Locker.IsLocked(documentId) Then
unlocked.Add(Document.GetDocumentById(documentId))
End If
Next
Return unlockedDocuments
End Function

现在需要的是使一些 JavaScript 发出请求并处理响应。我们将基于响应在要动态创建的表中放置已发布的文档信息(如果有)。为此,我们将开始编写 HTML。

<div id="notifyBox" style="display:none;">
<b>The following queued documents can now be edited</b>
<table cellpadding="5" cellspacing="0"
border="0" style="border:1px solid #EEE;"
id="notifyTable">
</table>
</div>

如果没有可用的文档(或是没有为该用户列出文档),我们使用 DIV 标记隐藏所有内容,用 TABLE 标记来显示结果。我们将使用轮询系统来检查是否存在任何可用的队列文档。一般来说,这意味着我们将在稍后一段时间内一直调用服务器端方法,并显示结果。在加载页面时仅发生第一次调用,每隔 X 秒发生后续调用。

<script language="javascript">
window.setTimeout("PollQueue();", 2000);
//每隔 2 秒激发以检查在具有许多用户的实际系统中是否发布了
//队列文档,2 秒可能会使服务器承受
//过高的负荷。我们甚至可以首先检查用户是否
//拥有队列,但是我们确实需要进行一些
//性能测试
function PollQueue()
{
//UnlockNotifier 是我们使用 Ajax.NET 注册的类型
//GetUnlockedDocuments 是该类型中的方法,标有
//AjaxMethod 属性
UnlockNotifier.GetUnlockedDocuments(PollQueue_CallBack);
//每隔 2 秒调用其本身
window.setTimeout("PollQueue();", 2000);
}
</script>

剩下的就是处理响应。这与以前示例中的代码相似。首先,检查是否存在错误,获得响应,遍历可用的文档,动态创建 HTML,在这种情况下,向表中添加行和列。

function PollQueue_CallBack(response)
{
var notifyBox = document.getElementById("notifyBox");
var notifyTable = document.getElementById("notifyTable");
//如果我们无法找到表通知框
if (notifyBox == null || notifyTable == null)
{
return;
}
//如果服务器端代码出现异常
if (response.error != null)
{
notifyBox.style.display = "none";
alert(response.error); //我们应该能做得更好
return;
}
var documents = response.value;
//如果不是我们所希望的响应
if (documents == null || typeof(documents) != "object")
{
notifyBox.style.display = "none";
return;
}
for (var i = 0; i < notifyTable.rows.length; ++i)
{
notifyTable.deleteRow(i);
}
for(var i = 0; i < documents.length; ++i)
{
var row = notifyTable.insertRow(0);
row.className = "Row" + i%2;
var cell = row.insertCell(0);
cell.innerHTML = documents[i].Title;
cell = row.insertCell(1);
var date = documents[i].Created;
cell.innerHTML = date.getDay() + "/" + date.getMonth()
+ "/" + date.getYear();
cell = row.insertCell(2);
cell.innerHTML = "<a href='DocumentEdit.aspx?id="
+ documents[i].DocumentId + "'>edit</a>";
}
notifyBox.style.display = "block";
}

我们要看到的最后一个快速改进是当用户关闭浏览器、导航到其他链接或单击“后退”按钮时,将自动解除文档锁定。通常,可以通过触发 JavaScript OnBeforeUnLoad 事件或 OnUnload 事件达到此目的,这会打开新的小型弹出式窗口,该弹出式窗口在加载页面时做一些清理然后自行关闭。您自己可以使用弹出式窗口,但是其他人则不能使用,它将导致弹出式窗口受阻并使文档永久保持锁定状态。要解决此问题,我们仍需要两个 JavaScript 事件,但是并不是启动弹出式窗口,而是将通过 AJAX 执行服务器端方法。在用于编辑文档的页上(即,放置锁的页),我们添加一些简单的 JavaScript。

<script language="javascript">
//如果用户关闭浏览器或点击“后退”按钮,
//确保该文档会被解除锁定
window.onbeforeunload = ReleaseLock;
function ReleaseLock() {
Locker.ReleaseDocument(<%=DocumentID%>);
}
</script>

在这里,DocumentId 是在后面的代码中定义和设置的变量。另外,我们可以在会话中存储 DocumentId,并在服务器端 ReleaseDocument 中访问。通常,ReleaseDocument 从锁定的文档列表中删除文档。

示例 3:论坛主题搜索

我们要看的最后一个示例是对现有应用程序的修改。我首次听到这个想法是 Josh Ledgard(英文)设想在 MSDN 论坛(英文)里添加一个功能。目的是尝试帮助用户自己找到问题的答案,以及限制重复发布的数量。一般来说,用户在论坛中提出新问题时,他或她会输入主题和问题。他们通常都不会先进行搜索,来查看是否已经提出和回答过该问题。输入 AJAX。用户输入主题(并将 Tab 键移出该字段)后,我们基于该主题异步搜索论坛,并适时地向用户显示结果。有时这些结果会有帮助,有时候则不会。

为使结果有帮助,我们将修改 asp.NETPRO Reader's Choice Award for Best Forum Application, CommunityServer。可下载的示例中不包含这一部分(或论坛)的代码,但是您能在 http://communityserver.org/(英文)学到关于 CommunityServer 的更多知识,并且可在其中应用下面这些代码片断。

安装 CommunityServer 并配置 Ajax.NET(已将引用和处理程序添加到 web.config)后,我们只需要进行一些更改就可以获得所需的功能。首先,我们转到 CommunityServerForums 项目中的 CreateEditPost.cs 文件。将它视为此页的后面的代码,其中用户可以添加新的发布。下面我们将添加启用了 AJAX 的函数。

//C#
[Ajax.AjaxMethod()]
public static ArrayList Search(string search)
{
SearchQuery query = new SearchQuery();
query.PageIndex = 0; //获得前 10 个结果
query.PageSize = 10;
query.UserID = Users.GetUser().UserID;
query.SearchTerms = search;
return new ForumSearch().GetSearchResults(query).Posts;
}

我们能够利用已经在 CommunityServer 中创建的搜索功能,只需要我们的函数能应用它。如往常一样,类型必须使用 Ajax.NET 注册。我们将在同一文件的 InitializeSkin 函数(将其视为 Page_Load)中进行此操作。

//C#
Ajax.Utility.RegisterTypeForAjax(typeof(CreateEditPost));

在转跳到 JavaScript 之前,我们需要进行最后的服务器端更改。返回到 Ajax.NET 的自定义类(例如我们正在返回的 ArrayList 所包含的 ForumPost)必须标有 Serializable 属性。我们要做的是转到 CommunityServerForums 项目中的 Components/ForumPost.cs 文件,并添加此属性。

//C#
[Serializable]
public class ForumPost :Post
{
...
}

显示时,我们仅需要修改 CommunityServerWeb 项目中的 Themes/default/Skins/View-EditCreatePost.cs。首先,我们将触发主题文本框的 onBlur 事件。

<asp:textbox onBlur="Search(this.value);"
id="PostSubject" runat="server" ... />

接着,我们编写 JavaScript Search 方法,以便调用服务器端 Search

var oldValue = '';
function Search(value)
{
//不要再次搜索刚搜索过的内容
//如果用户向后或向前移动 Tab 键将会发生
if (value != oldValue)
{
CreateEditPost.Search(value, Search_CallBack);
oldValue = value;
}
}

最后,剩下的就是处理响应。由于上一个示例介绍了在表中显示结果的稍微正规的方式,我们将仅仅创建一些动态的 HTML,并将它粘贴到虚拟的 DIV 中。

function Search_CallBack(response)
{
//由于没有结果时搜索功能将自动重定向,
//因此,我们不能使用 response.error。
var results = response.value;
//如果我们没有获得结果
if (results == null)
{
return;
}
//我们用于放置结果的 DIV
var someDiv = document.getElementById("someDiv");
var html = "";
for (var i = 0; i < results.length; ++i)
{
var result = results[i];
html += "<a target=_blank href='" + result.PostID
html += "/ShowPost.aspx'>";
html += result.Subject;
html += "</a><br />"
}
someDiv.innerHTML = html;
}

通过对 CommunityServer 应用程序的三个文件(加上用于配置的 web.config)稍微进行修改,我们可以添加一些非常有用的功能。但是,只向现有的应用程序添加启用 AJAX 功能时要小心操作。正在进行实际搜索的预先存在的 ForumSearch 类可能并不是为我们介绍的使用类型设计的。我们的代码很可能会导致执行一些额外的搜索,影响可能会很显著。

AJAX 与您

AJAX 如何以及哪里适合您的应用程序,以及是否已经存在要根据情况而定。尽管我们已经看到使用 Ajax.NET 可以轻松地创建启用 AJAX 的解决方案,但还存在一些其他的注意事项。一个需要着重关注的问题是对您的应用程序的整体体系结构和可维护性的影响。AJAX 会进一步模糊系统的各层之间的界限,显著影响显示层、显示逻辑层和业务层。这不是 AJAX 本身的问题,而是使用方式的问题。只要您知道它很容易导致各层之间的某些冲突,并适当操作,就不会产生问题。

使用 AJAX 的应用程序更难于维护吗?答案主要取决于您已经使用的 JavaScript 的数量,以及您组织和维护它的好坏程度。很多开发人员认为 JavaScript 难于编写、测试和调试(不是因为 JavaScript 本身,而是因为工具支持和开发人员的知识)。如果您当前正在使用 JavaScript 实现链接的下拉列表,并切换到 AJAX,您的代码可能较为容易维护(Ajax.NET 对 .NET 类型和数组的支持是重要原因)。但是,如果您使用返回方式来实现,现在您将向您的应用程序 (JavaScript) 引入崭新的语言。您将必须处理这样的情况:存在某些不参与 ViewState 的数据(这一点我们在按钮单击事件中可以看到)。

另一个需要考虑的是 AJAX 对您的网站可用性的影响。即使 AJAX 最后能创建响应更及时的界面,开发人员仍需要注意两件事情。首先,很明显 AJAX 依赖 JavaScript。我们都知道一些用户禁用 JavaScript,一些标准(例如加拿大政府 Common Look and Feel [考虑加拿大的 508])要求网站使用 JavaScript 来运行,或不使用它来运行。因此,您不应假定 AJAX 功能正在运行。如果 AJAX 功能不可用,则应使您的应用程序退回到更普通的 Web 处理。其次,AJAX 应用程序可能不熟悉(即使它具有较高级别)用户习惯使用应用程序的方式。例如,通过 AJAX 执行不同功能的页可能不以用户认为的方式表现“后退”按钮、“收藏夹”菜单和其他浏览器功能。

结论

AJAX 不只是令人兴奋的即将面世的技术,它是具体的框架,在建立 Web 应用程序时可以采用它来解决每天遇到的问题。Ajax.NET 使 ASP.NET 开发人员轻松掌握 AJAX。我们看到的三个示例和可下载的项目可以帮助您了解如何使用 AJAX 和 Ajax.NET。您还可以利用这些示例来尝试一些自己的想法。AJAX 不仅可以创建简洁和强大的应用程序,它还可以使您提高客户满意度和竞争优势。正在讨论的 Atlas 的一些高级概念可能显著改进我们提供的产品。就个人而言,我见过的最佳的 AJAX 实现非常轻便合适。您自己的实现应为您的用户提供同样积极的体验。但是,对于某个具体问题,记住 AJAX 可能不是唯一的解决方案,也可能不是最佳的解决方案。现在,让我们证明 ASP.NET 社区是首屈一指的,让我们收拾屋子吧。

关于作者

Karl Seguin 将他的大部分时间用在 Microsoft ASP.NET 新闻组,帮助其他开发人员并寻找和编写帮助性的主题。当他不工作或不提供帮助时,他喜欢无情地清洁 Gnomish 灾难的 Azeroth。

转到原英文页面

相关文章

最新评论