这个例子展示了如何通过点击help.aspx页面并注入自身响应到HelpDiv中来进行加载的方法。该响应可以通过设置页面help.aspx.的输出缓存指令来进行缓存。因此,下次当用户点击该连接时,UI就会立即弹出来。help.aspx文件没有<html>块,仅仅只是将内容设置到DIV中了。
Inherits="Help" %>
<%@ OutputCache Location="ServerAndClient" Duration="604800" VaryByParam="none" %>
<div class="helpContent">
<div id="lipsum">
<p>
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Duis lorem
eros, volutpat sit amet, venenatis vitae, condimentum at, dolor. Nunc
porttitor eleifend tellus. Praesent vitae neque ut mi rutrum cursus.
使用这种方法,你可以把UI分解成更多小的*.aspx 文件。虽然这些*.aspx 文件不能包涵有JavaScript或者stylesheet块,但是它们可以包涵大量你需要显示到UI上的HTML。因此,你可以保持只下载加载所需要的基本信息。当用户在站点上浏览新的特性时,就会立即加载这些区域。
优化 ASP.NET 2.0 Profile Provider
你可知道在ASP.NET 2.0 Profile Provider中有两个能进行优化的重要存储过程吗?如果在没有进行任何必要优化的情况下使用过它们,你的服务器将会因为业务量的增长而变得异常繁忙。这里有一个故事:
在2006年3月份的MIX大会上展示了Pageflakes。当时我们是我们最富有魅力的时候。我们是作为支持Showcase of Atlas Web site的第一家公司。每天访问站点的用户数量接连攀升。有一天我们注意到,数据库服务器不再工作了。然后我们重新启动了服务器,工作恢复了正常,可过了一小时后,服务器再次死掉了。在我们对服务器主体部分进行了检查分析之后,我们发现CPU占用率高达100%并且IO使用率更高。
硬盘驱动器发热,并进行了自动关闭以保护其不受损坏。这对我们来说感到十分惊奇,因为我们原以为我们一直都很聪明,并且针对每个单独的Web服务功能都使用了profile。因此,我们对上百兆的日志进行了分析希望能找到那个Web服务耗费了这么多时间。我们对其中一个产生了怀凝。它是加载用户页面配置的第一个功能。我们将这个功能分解成了很多小的部分以便我们能快速找到那一部分花费了大部分时间。
{
if( Profile.IsAnonymous )
{
using (new TimedLog(Profile.UserName,"GetPageflake"))
{
正如你所看到的情形,整个方法主体就是用于计时。如果你想了解这种计时是如何工作的,我会在一篇新的文章中进行解释。我们也对其中一小部分我们怀凝最耗费资源的功能进行了计时。但是在我们的代码中需要花费大量时间处理的部分很多。我们的代码一直都是经过优化的(毕竟,你知道是谁在查看它,就是我)。
同时,用户开始了大叫,管理也开始混乱,支持部门的员工也开始抱怨这么多电话。开发人员搞得焦头乱额此时也变得胡言乱语。这并不是什么特殊情况,仅仅就是一个每个月会遇到2次的一个典型解决方案。
现在你一定在大声叫喊了,“你可以使用SQL Profiler啊,你这个傻瓜!”。问题是我们使用的是SQL Server工作组版本。不支持SQL Profiler这个功能。因此我们不得不采用我们的方法来解决这个问题,无论怎样也得使其在服务器上正常运行。不要问这个到底如何实现。在运行了SQL Profiler以后,孩子,真让我们吃惊!原来才是这个巨大的存储过程dbo.aspnet_Profile_GetProfiles给我们带来了痛苦!
我们习惯大量使用(并且一直使用)Profile provider这个工具。
该存储过程如下:
@ApplicationName nvarchar(256),
@ProfileAuthOptions int,
@PageIndex int,
@PageSize int,
@UserNameToMatch nvarchar(256) = NULL,
@InactiveSinceDate datetime = NULL
AS
BEGIN
DECLARE @ApplicationId uniqueidentifier
SELECT @ApplicationId = NULL
SELECT @ApplicationId = ApplicationId
FROM aspnet_Applications
WHERE LOWER(@ApplicationName)
= LoweredApplicationName
IF (@ApplicationId IS NULL)
RETURN
-- Set the page bounds
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @TotalRecords int
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageSize - 1 + @PageLowerBound
-- Create a temp table TO store the select results
CREATE TABLE #PageIndexForUsers
(
IndexId int IDENTITY (0, 1) NOT NULL,
UserId uniqueidentifier
)
-- Insert into our temp table
INSERT INTO #PageIndexForUsers (UserId)
SELECT u.UserId
FROM dbo.aspnet_Users
u, dbo.aspnet_Profile p
WHERE ApplicationId = @ApplicationId
AND u.UserId = p.UserId
AND (@InactiveSinceDate
IS NULL OR LastActivityDate
<= @InactiveSinceDate)
AND (
(@ProfileAuthOptions = 2)
OR (@ProfileAuthOptions = 0
AND IsAnonymous = 1)
OR (@ProfileAuthOptions = 1
AND IsAnonymous = 0)
)
AND (@UserNameToMatch
IS NULL OR LoweredUserName
LIKE LOWER(@UserNameToMatch))
ORDER BY UserName
SELECT u.UserName, u.IsAnonymous, u.LastActivityDate,
p.LastUpdatedDate, DATALENGTH(p.PropertyNames)
+ DATALENGTH(p.PropertyValuesString)
+ DATALENGTH(p.PropertyValuesBinary)
FROM dbo.aspnet_Users
u, dbo.aspnet_Profile p, #PageIndexForUsers i
WHERE
u.UserId = p.UserId
AND p.UserId = i.UserId
AND i.IndexId >= @PageLowerBound
AND i.IndexId <= @PageUpperBound
DROP TABLE #PageIndexForUsers
END
END