源代码的情况下进行部署

时发生的编译延迟?尽管该问题本身在某种程度上无关紧要(延迟非常小,并且延迟的开销被成千上万甚至数以百万的后续请求所分摊),但Microsoft仍然感到有必要采取相应的措施来减轻开发人员的担忧。这一“措施”就是能够通过提交对名为precompile.axd的幻像资源的请求,来预编译应用程序中的所有页面。

    但预编译并不仅限于此。另一个经常被请求的功能是:能够将整个应用程序预编译为可以在不带源代码的情况下进行部署的托管程序集(该功能在宿主方案中尤其有用)。ASP.NET 2.0包含一个名为Aspnet_compiler.exe的新的命令行工具,它能够执行预编译并且在不带源代码的情况下进行部署;Visual Studio 2005将包含类似的功能。下面的命令将预编译Web1目录中的应用程序,并且在不带源代码的情况下将其部署到Web2:

Aspnet_compiler -v /web1 -p c:/web1 c:/web2

    之后,目标目录将包含空的ASP.NET文件(ASPX、ASCX、ASIX等等)以及源目录中存在的所有静态内容(如HTML文件、.config文件和图像文件)的副本。在不带源代码的情况下进行部署并不会为您的知识产权提供牢不可破的保护,因为聪明的ISP仍然可以通过反编译生成的程序集来弄清楚应用程序的来龙去脉,但是,它确实对一般的代码窃取者设置了更大的阻碍。

新的代码分隔模型

    ASP.NET 1.x支持两种编程模型:内联模型—HTML和代码共存于同一个ASPX文件中;代码隐藏模型—它将HTML分隔到ASPX文件中,并将代码分隔到源代码文件(例如,C#文件)中。ASP.NET 2.0引入了第三个模型:一种新的代码隐藏形式,它依赖于Visual C#和Visual Basic .NET编译器中的不完全类支持。新的代码隐藏解决了原来的代码隐藏中存在的一个恼人的问题:传统的代码隐藏类必须包含受保护的字段,这些字段的类型和名称需要映射到ASPX文件中声明的相应控件。

    图10 显示了新的代码隐藏模型的工作方式。Hello.aspx包含页面的声明部分,Hello.aspx.cs包含代码。您应该注意@ Page指令中的CompileWith属性。此外,请注意MyPage类中缺少的字段(它们提供到ASPX文件中声明的控件的映射)。旧样式的代码隐藏仍然受支持,但新样式将是今后的首选编程模型。一点都不奇怪,Visual Studio 2005天生就支持新的代码分隔模型。

Hello.aspx

<%@ Page CompileWith="Hello.aspx.cs" ClassName="MyPage" %>

<html>
    <body>
        <form runat="server">
            <asp:TextBox ID="Input" RunAt="server" />       
            <asp:Button Text="Test" OnClick="OnTest" RunAt="server" />
            <asp:Label ID="Output" RunAt="server" />
        </form>
    </body>
</html>

Hello.aspx.cs

using System;

partial class MyPage
{
    void OnTest (Object sender, EventArgs e)
    {
        Output.Text = "Hello, " + Input.Text;
    }           
}

图10 Codebehind模型

客户端回调管理器

    ASP.NET 2.0中我最喜欢的功能之一就是由新的客户端回调管理器提供的“轻量级回发”功能。在过去,ASP.NET页面必须回发给服务器才能调用服务器端代码。回发是低效的,因为它们将包含由页面控件生成的所有回发数据。它们还强制页面刷新,从而导致不雅观的闪烁。

    ASP.NET 2.0引入了客户端回调管理器,它使页面无需完全回发就可以回调到服务器。回调是异步的,并且通过XML-HTTP来完成。它们不包含回发数据,并且不会强制页面刷新。(在服务器端,页面像平常一样执行至PreRender事件,但在即将呈现任何HTML之前停止。)它们确实需要支持XML-HTTP协议的浏览器(这通常意味着Microsoft Internet Explorer 5.0或更高版本)。

    使用客户端回调管理器涉及三个步骤。首先,调用Page.GetCallbackEventReference以获取对某个特定函数(可以从客户端脚本中调用该函数,以执行到服务器的XML-HTTP回调)的引用。ASP.NET提供了该函数的名称和实现。其次,在客户端脚本中编写一个将在回调返回时调用的方法。方法名称是传递给GetCallbackEventReference的参数之一。第三,在页面中实现ICallbackEventHandler接口。该接口包含一个方法—RaiseCallbackEvent,当回调发生时,该方法将在服务器端调用。RaiseCallbackEvent所返回的字符串将被返回到第二步所述的方法。

    图11 中的代码显示了客户端回调的工作方式,并且演示了它们的一个非常实际的用途。该页面显示了一个请求姓名和地址的窗体。在Zip Code字段中键入378xx或379xx邮政编码,然后单击Autofill按钮,City字段中将显示一个名称。值得注意的是,该页面会返回到服务器以获取城市名称,但它使用客户端回调而不是完全回发来完成此工作。在实际操作中,它可以找到某个数据库以将邮政编码转换为城市名称。请注意,该页面并不像页面在回发到服务器时通常所做的那样进行重新绘制。相反,更新是快速且简洁的!

<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

<html>
    <body>
        <h1>Please Register</h1>
        <hr>
        <form runat="server">
            <table>
                <tr>
                    <td>First Name</td>
                    <td><asp:TextBox ID="FirstName" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Last Name</td>
                    <td><asp:TextBox ID="LastName" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Address 1</td>
                    <td><asp:TextBox ID="Address1" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Address 2</td>
                    <td><asp:TextBox ID="Address2" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>City</td>
                    <td><asp:TextBox ID="City" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>State</td>
                    <td><asp:TextBox ID="State" RunAt="server" /></td>
                    <td></td>
                </tr>
                <tr>
                    <td>Zip Code</td>
                    <td><asp:TextBox ID="Zip" RunAt="server" /></td>
                    <td><asp:Button ID="AutofillButton" Text="Autofill"
                        RunAt="server" /></td>
                </tr>
            </table>
        </form>
    </body>
</html>

<script language="javascript">
// Function called when callback returns
function __onCallbackCompleted (result, context)
{
    // Display the string returned by the server's RaiseCallbackEvent
    // method in the input field named "City"
    document.getElementById ('City').value = result;
}
</script>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    // Get callback event reference (e.g., "__doCallback (...)")
    string cbref = GetCallbackEventReference (this,
        "document.getElementById ('Zip').value",
        "__onCallbackCompleted", "null", "null");

    // Wire the callback event reference to the Autofill button with
    // an onclick attribute (and add "return false" to event reference
    // to prevent a postback from occurring)
    AutofillButton.Attributes.Add ("onclick",
        cbref + "; return false;");
}

// Server-side callback event handler
string ICallbackEventHandler.RaiseCallbackEvent (string arg)
{
    if (arg.StartsWith ("378"))
        return "Oak Ridge";
    else if (arg.StartsWith ("379"))
        return "Knoxville";
    else
        return "Unknown";
}
</script>

图11 callback.aspx

验证组

    验证控件是ASP.NET 1.x中更为卓越的创新。诸如RequiredFieldValidator和RegularExpressionValidator之类的控件使开发人员能够在客户端和服务器上进行更为智能的输入验证,而不必成为客户端脚本编写和浏览器DOM方面的专家。遗憾的是,版本1.x验证控件存在一个致命的缺陷,即:没有一种比较好的方法来将这些控件组合在一起,以便页面的一个部分上的验证程序可以重写该页面其他部分上的验证程序,并且无论其他验证程序的状态如何,都可以使回发发生。

    该问题由ValidationGroups1.aspx阐明,它包含在您可以针对本文下载的示例中。页面的设计者预计用户能够填写一组TextBox并回发到服务器,而不必同时填写另一个组,但它并不按此方式工作。除非所有输入字段都被填充,否则验证程序将抱怨不休,如图12所示。


图12 ASP.NET 1.x中的验证控件

    ASP.NET 2.0中的新验证组功能一劳永逸地解决了该问题。现在,可以使用ValidationGroup属性来组合验证控件。可以用相同的方式将按钮控件分配给组,并且当一个组中的所有验证程序都对输入感到满意时,它们才允许回发发生,当然,前提是回发是由验证程序同一组中的控件生成的。ValidationGroups2.aspx演示了该技术(参见图13)。从表面上看,该页面与ValidationGroups1.aspx完全相同。但在内部,它们却完全不同。现在,您可以填写任一组TextBox,并且通过单击TextBox验证组中的按钮进行回发。

<html>
    <body>
        <form runat="server">
            <h1>New Users</h1>
            <table>
                <tr>
                    <td>User Name</td>
                    <td><asp:TextBox ID="NewUserName" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewUserName" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Password</td>
                    <td><asp:TextBox ID="NewPassword1" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewPassword1" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Retype Password</td>
                    <td><asp:TextBox ID="NewPassword2" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewPassword2" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>E-Mail Address</td>
                    <td><asp:TextBox ID="NewEMail" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator ValidationGroup="NewUsers"
                        ControlToValidate="NewEMail" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td></td>
                    <td><asp:Button ValidationGroup="NewUsers"
                        Text="Create Account" OnClick="OnCreateAccount"
                        RunAt="server" /></td>
                    <td></td>
                </tr>
            </table>
            <hr>
            <h1>Existing Users</h1>
            <table>
                <tr>
                    <td>User Name</td>
                    <td><asp:TextBox ID="UserName" RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator
                        ValidationGroup="ExistingUsers"
                        ControlToValidate="UserName" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td>Password</td>
                    <td><asp:TextBox ID="Password" TextMode="Password"
                        RunAt="server" /></td>
                    <td><asp:RequiredFieldValidator
                        ValidationGroup="ExistingUsers"
                        ControlToValidate="Password" ErrorMessage="Required"
                        RunAt="server" /></td>
                </tr>
                <tr>
                    <td></td>
                    <td><asp:Button ValidationGroup="ExistingUsers"
                        Text="Log In" OnClick="OnLogIn" RunAt="server" /></td>
                    <td></td>
                </tr>
            </table>
        </form>
    </body>
</html>

<script language="C#" runat="server">
void OnCreateAccount (Object sender, EventArgs e) {}
void OnLogIn (Object sender, EventArgs e) {}
</script>

图13 ValidationGroups2.aspx

跨页面发送

    有关ASP.NET 1.x的最多抱怨是只允许页面回发到其本身。在版本2.0中,这一点通过引入跨页面发送而得到改变。要设置跨页面发送,您需要使用导致回发发生的控件的PostBackUrl属性来指定目标页面,如PageOne.aspx中的以下代码所示:

<html>
    <body>
        <form runat="server">
            <asp:TextBox ID="Input" RunAt="server" />
            <asp:Button Text="Test" PostBackUrl="PageTwo.aspx"
                RunAt="server"
            />
        </form>
    </body>
</html>

    当被单击时,PageOne.aspx中的按钮将回发到PageTwo.aspx:

<html>
    <body>
        <asp:Label ID="Output" RunAt="server" />
    </body>
</html>

<script language="C#" runat="server">
void Page_Load (Object sender, EventArgs e)
{
    TextBox input = (TextBox) PreviousPage.FindControl ("Input");
    Output.Text = "Hello, " + input.Text;
}
</script>

    PageTwo.aspx使用Page类的新PreviousPage属性来获取对起始页面的引用。对FindControl的简单调用将返回对PageOne.aspx中声明的TextBox的引用,以便可以检索用户的输入。

    默认情况下,System.Web.UI.Page.PreviousPage返回对引起回发的页面的弱类型化引用。但是,如果PageOne.aspx是唯一能够向PageTwo.aspx发送的页面,则PageTwo.aspx可以使用新的@ PreviousPageType指令来获取对PageOne.aspx的强类型化访问,如下面的代码所示:

<%@ PreviousPageType TypeName="ASP.PageOne.aspx" %>
...

小结

    ASP.NET 2.0还包含其他我尚未讨论的新功能。例如,内置的站点计数器服务使您能够记录站点使用情况的统计信息,并且在Webadmin.axd中或者在您自己的自定义GUI中查看它们。新的Web组件子系统提供了一个用于构建SharePoint服务器样式门户的框架(有关ASP.NET 2.0中的Web部件和门户的详细信息,请参阅本期杂志《利用 ASP.NET 2.0 中的 Web 部件和个性化释放站点的潜能》,而集成的移动设备支持则意味着,您不再需要安装单独的工具包来使输出适合PDA及其他小型设备。对现有控件进行的无数增强使得这些控件在生成基于组件的Web页方面比以往任何时候都更加灵活。

    现在正是学习ASP.NET 2.0的最佳时机,因为要在目前规划能够在将来轻松升级的体系结构,需要知道哪些功能即将问世(以及哪些功能不会问世)。您的ASP.NET 1.x应用程序无需修改就可以在2.0版本上运行,因为Microsoft已经允诺新平台将向后兼容旧平台。但是,未来将属于ASP.NET 2.0,而这一未来将意味着更加丰富的功能和更少的代码。有什么理由不接受它呢?

    作者介绍:Jeff Prosise是MSDN Magazine的特约编辑,并且是多部书籍的作者,其中包括Programming Microsoft .NET(Microsoft Press,2002)。他还是Wintellect的共同创始人之一,该公司是一家专门研究Microsoft .NET

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值