[转载]ASP.NET MVC 多套皮肤解决方案 - Daniel Chow - 博客园

mikel阅读(867)

[转载]ASP.NET MVC 多套皮肤解决方案 – Daniel Chow – 博客园.

原理自己写了一个ThemeController继承于Controller,先看我的文件结构:

ThemeController的具体实现:

 

复制代码
  public class ThemeController : Controller
{
private static string[] sPathTemplate = new string[]{
/Themes/{0}/{1}/{2}.aspx,
/Themes/{0}/Shared/{1}.Master};

        private static ThemeController m_ThemeController = new ThemeController();

public static ViewResult View(string sTheme, string sController, string sView, string sMaster, object oModel)
{
string sViewPath = string.Format(sPathTemplate[0], sTheme, sController, sView);
string sMasterPath = string.Format(sPathTemplate[1], sTheme, sMaster);

return m_ThemeController.View(sViewPath, sMasterPath, oModel);
}

public static ViewResult View(string sTheme, string sController, string sView, object oModel)
{
return View(sTheme, sController, sView,Site , oModel);
}

public static ViewResult View(string sTheme, string sController, string sView)
{
string sViewPath = string.Format(sPathTemplate[0], sTheme, sController, sView);
string sMasterPath = string.Format(sPathTemplate[1], sTheme, Site);

return m_ThemeController.View(sViewPath, sMasterPath);
}

//protected override ViewResult View(string viewName, string masterName, object model)
//{
//    if (viewName == null && model != null)
//        viewName = model.GetType().Name.ToLower().Replace(“model”, “view”);

//    return base.View(viewName, masterName, model);
//}
    }

复制代码

 

调用方法:

复制代码
[HandleError]
public class HomeController : ThemeController
{
public ActionResult Index()
{
string m_sTheme = default; //默认的皮肤

if (Request.QueryString[Theme!= null)
{
m_sTheme 
= Request.QueryString[Theme];
}

ViewData[Message= Welcome to ASP.NET MVC!;

return View(m_sTheme, HomeIndex);
}

public ActionResult About()
{
return View();
}
}

复制代码

 

显示效果:

 

自己觉得这是一个比较粗浅的解决方案,算是抛砖引玉吧,敬请多多拍砖!

 

附上源码!

 

更好的解决方案:重写视图引擎 附上源代码

[转载]给Jquery动态添加的元素添加事件_xiaofeixia7527

mikel阅读(743)

[转载]给Jquery动态添加的元素添加事件_xiaofeixia7527.

我想很多人都会向我一样曾经 被新元素的事件绑定困惑很久也就是
在页面加载完成后给元素绑定了事件,但又新增加的元素上却没有绑定任何事件。

js的事件监听跟css不一样,css只要设定好了样式,不论是原来就有的还是新添加的,都有一样的表现。而事件监听不是,你必须给每一个元素单独绑定事件。

常见的例子是处理表格的时候。每行行末有个删除按钮,点了这个能够删除这一行。

< table >
< tbody >
< tr >
< td > 这行原来就有 </ td >
< td >< button class = del > 删除 </ button ></ td >
</ tr >
< tr >
< td > 这行原来就有 </ td >
< td >< button class = del > 删除 </ button ></ td >
</ tr >
</ tbody >
</ table >

通常,我会这么绑定

  1. JQuery(function($){
  2.    //已有删除按钮初始化绑定删除事件
  3.     $(.del).click(function() {
  4.         $(this).parents(tr).remove();
  5.    });
  6. });

对于在domready之前就存在的删除按钮,一切都很完美。但如果在domready之后用js动态添加几行,那新增的几行中的这些按钮都将失去任何作用。

如何解决这个问题?以下提供4种解决方案:
=============================
0号解决方案——onclick法
如果不顾结构与行为分离的准则的话,通常,我会这么做。
注意,此时的deltr这个function必须是全局函数,得放JQuery(function($) {})外面,放里边就成局部函数了,html里的onclick就调用不到了!

  1. <td><buttononclick=deltr(this)>删除</button></td>
  1. JQuery(function($){
  2.    //添加行
  3.     $(#add2).click(function(){
  4.         $(#table2>tbody).append(<tr><td>新增行</td><td><button nclick=”deltr(this)”>删除</button></td></tr>)
  5.    });
  6. });
  7. //删除行的函数,必须要放domready函数外面
  8. function deltr(delbtn){
  9.     $(delbtn).parents(tr).remove();
  10. };

=============================
1号解决方案——重复绑定法
即,在domready的时候就给已有的元素绑定事件处理函数,
而后当新增加的元素的时候再次绑定。

  1. <td><buttonclass=del>删除</button></td>
  1. jQuery(function($){
  2.    //定义删除按钮事件绑定
  3.    //写里边,防止污染全局命名空间
  4.    function deltr(){
  5.         $(this).parents(tr).remove();
  6.    };
  7.    //已有删除按钮初始化绑定删除事件
  8.     $(#table3 .del).click(deltr);
  9.    //添加行
  10.     $(#add3).click(function(){
  11.         $(<tr><td>新增行</td><td><button class=”del”>删除</button></td></tr>)
  12.            //在这里给删除按钮再次绑定事件。
  13.             .find(.del).click(deltr).end()
  14.             .appendTo($(#table3>tbody));
  15.    });
  16. });

=============================
2号解决方案——事件冒泡法
利用事件冒泡的原理,我们给这个按钮的祖先元素绑定事件处理函数。
然后通过event.target这个对象来判断,这个事件是不是我们要找的对象触发的。
通常可以利用一些DOM属性,比如event.target.className、event.target.tagName等之类的来判断。

  1. <td><buttonclass=del>删除</button></td>
  1. jQuery(function($){
  2.    //第四个表格的删除按钮事件绑定
  3.     $(#table4).click(function(e) {
  4.        if (e.target.className==del){
  5.             $(e.target).parents(tr).remove();
  6.        };
  7.    });
  8.    //第四个表格的添加按钮事件绑定
  9.     $(#add4).click(function(){
  10.         $(#table4>tbody).append(<tr><td>新增行</td><td><button class=”del”>删除</button></td></tr>)
  11.    });
  12. });

[转载]【精心挑选】8款用于布局和用户界面增强的 jQuery 插件 - 梦想天空(山边小溪) - 博客园

mikel阅读(1028)

[转载]【精心挑选】8款用于布局和用户界面增强的 jQuery 插件 – 梦想天空(山边小溪) – 博客园.

有时,你可能有一个项目要求独特的布局或用户界面的要求,单独使用 CSS 无法实现的,像这类项目就需要 JavaScript 来实现,因此,这篇文章中收集了一组优秀的 JQuery 插件,可以帮助你完成一些奇妙的布局和界面效果。

 

 

 

equalize.js

 

用于均衡元素的的高度或宽度的 JQuery 插件。这对于统一页面元素规格的布局非常有用。

 

均衡 id 为 width-example 的元素的宽度的使用实例:

 

1
$('#width-example').equalize('width');

 

均衡 class 为 parent 的元素的子段落的使用实例:

 

1
$('.parent').equalize({children: 'p'});

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

jQuery Scroll Path

 

这款插件用于实现自定义路径的滚动效果。使用 Canvas 绘制路径线条和弧形,动画效果形象。

 

使用实例:

 

1
2
3
4
5
$(".your-container-element").scrollPath({
    drawPath: true,
    wrapAround: true,
    scrollBar: false
});

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

freetile.js

 

Freetile 这款 JQuery 插件,用于高效的组织网页内容为动态、响应式的布局。

 

使用示例:

 

1
$('#container').freetile({ animate: true, elementDelay: 30 });

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

gridster.js

 

这款插件用于实现神话般的可拖放的多列网格布局,允许建立直观的跨越多个列的拖动布局元素。

 

  示例 HTML 代码:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<div class="gridster">
    <ul>
        <li data-row="1" data-col="1" data-sizex="1" data-sizey="1"></li>
        <li data-row="2" data-col="1" data-sizex="1" data-sizey="1"></li>
        <li data-row="3" data-col="1" data-sizex="1" data-sizey="1"></li>
 
        <li data-row="1" data-col="2" data-sizex="2" data-sizey="1"></li>
        <li data-row="2" data-col="2" data-sizex="2" data-sizey="2"></li>
 
        <li data-row="1" data-col="4" data-sizex="1" data-sizey="1"></li>
        <li data-row="2" data-col="4" data-sizex="2" data-sizey="1"></li>
        <li data-row="3" data-col="4" data-sizex="1" data-sizey="1"></li>
 
        <li data-row="1" data-col="5" data-sizex="1" data-sizey="1"></li>
        <li data-row="3" data-col="5" data-sizex="1" data-sizey="1"></li>
 
        <li data-row="1" data-col="6" data-sizex="1" data-sizey="1"></li>
        <li data-row="2" data-col="6" data-sizex="1" data-sizey="2"></li>
    </ul>
</div>

 

示例 JavaScrpt 代码:

 

1
2
3
4
5
6
7
8
$(function(){
 
    $(".gridster ul").gridster({
        widget_margins: [10, 10],
        widget_base_dimensions: [140, 140]
    });
 
});

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

rcarousel

 

rcarousel 是基于 jQuery UI 的连续传送带效果插件,支持高度定制和 IE6 等古老的浏览器。

 

使用示例:

 

1
2
3
4
5
6
7
8
9
10
11
12
<div id="carousel">
    <img src="flowers/tulip.jpg" alt="a tulip"/>
    <img src="flowers/rose.jpg" alt="a rose"/>
    <img src="flowers/daisy.jpg" alt="a daisy"/>
    <img src="flowers/sunflower.jpg" alt="a sunflower"/>
    <img src="flowers/pansy.jpg" alt="a pansy"/>
</div>
<script type="text/JavaScript">
    jQuery(function($) {
        $( "#carousel" ).rcarousel( {width: 200, height: 200} );
    });
</script>

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

jQuery HiddenPosition

 

jQuery HiddenPosition 能够定位任何的页面元素,即使它们被隐藏。

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

turn.js

 

Turn.js 是一个 JavaScript 库,使您的网页内容看起来像一本真正的书或杂志。使用示例:

 

1
2
3
4
5
$("#flipbook").turn({
    width: 400,
    height: 300,
    autoCenter: true
});

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

 

 

 

BookBlock

 

这款 jQuery 插件用于创建类似小册子风格的效果,让您通过项目导航翻转页面。

 

8 jQuery插件布局和用户界面增强技术

 

插件下载     效果演示

[转载]【jquery仿dataList】应用之——模仿igoogle【定制化、拖动排序,最大化、分屏】 - 叶小钗 - 博客园

mikel阅读(956)

[转载]【jquery仿dataList】应用之——模仿igoogle【定制化、拖动排序,最大化、分屏】 – 叶小钗 – 博客园.

接上一次日志哈,这一次用原来写的datalist实现了简单应用,模拟igoogle。

做的过程中发现代码很多问题,主要是流程上的问题。

主要是自己层次不够,明明已经感到这样那样的问题,都能说出来就是不知道怎么改。主要问题:

1 初始化时候参照其他JQuery框架,应该达到配置最小化,却怎么都做不好

2 现在控件必须提供数据源datalist与模板template,数据源还好说,但是模板的写法真的太坑爹,现在是放在数据库里面了

以后怎么做还说不清哦

3 与.net中datalist一致,不论datalist还是item都会生成多余的标签,这里是生成的div,看上去很是别扭

4 最大问题还是,混乱!!!就像这篇文章一样,js控制力不同于服务器语言,所有整个做起来,不论事件绑定

还是其他什么定义都感觉有点混乱,流程不清,这是我主要应该改进的地方。


主要流程:

1 加载小工具模板:现在先一次性加载,以后用到一个保存一个作为全局参数
2 加载该用户的所有小工具:现在先一次性加载,以后先加载第一页,
根据页索引判断加载
3 根据用户小工具数据,初始化所有小工具外框
由于数据以及模板已经取出,此时加载速度应该非常快,左中右一次加载
**此处注意,虽说小工具外框一样,但是新增的小工具可能会在外框加载结束后再修饰外框
比如,weibo、top…..,此数据存于小工具定制列(CustomData)
4 此时进行数据异步加载,
***数据可以统一加载,但是此处先加载所有小工具每列前两名数据
在根据后台用户干预加载那些大数据小工具,加载结束后,当用户滑动鼠标便进行以下
控件加载,每次控件加载数量可配置化
5 所有控件加载结束,已加载结束控件,可能会根据后台配置要求动态刷新数据
此处需要做轮询
6 控件加载结束后,开始做控件事件绑定
***注意粒度尽量小,不要和其他模块相互影响


首页效果图:

此处点击都会触发事件,一个是异步加载选项卡数据,一个是展开摘要:

也可以延后加载,比如:开始只加载前三个模块,鼠标滑动后,再加载后面的模块:

一下是,初始化后鼠标滑动前与鼠标滑动后的效果:

 


分屏、分页效果

点击上面1,2,3肯定会指向不同也了,其实现在用户是我,若是用户换了也会有所不同

下面是第二页和第三页


拖动排序也实现了,之前想自己写一个拖动插件,拖动时实现了却发现插件非常困难,

局限于自己的层次,就直接用的JQuery ui的拖动

下面是拖动排序的展示,保存到数据库的,最后一张是刷新后重新排序的图:


点击最大化,并实现鼠标滚动到最下边重新加载数据(滚动分页嘛):

 


数据库简要设计,因为我没有安装数据库,所有优点不好弄:

设计缘由:由于今后可能不止五页,做到页数动态化

 

NO.

字段名称

说明

1

uuid 页面唯一标识

2

pageSort 页面排序

3

pageName 页面标题,默认1,2,3,4,5

4

pageDes 页面描述,鼠标划上显示

5

   

 

小工具模板表 templates(基本字段)

 

NO.

字段名称

说明

1

uuid 页面唯一标识

2

templateKey 标识

3

templateValue 模板内容

4

des 描述

5

   

 

 

 

小工具表 modules(基本字段)

小工具参数化,可变参数,具有验证性

 

NO.

字段名称

说明

1

uuid 小模块唯一标识

author 小工具作者

2

alias 模块前台标识名,对应category

3

title 小模块名称

4

dataSourceUrl 数据源链接地址

5

pageId 当前小工具属于第几屏,对应pages 的 uuid

6

colId 当前小工具属于第几列(暂分为1,2,3列)

7

sort 当前小工具顺序

8

templateId 当前小工具对应模板,对应templates表

9

type 当前小工具类型,frame或者json

   

   

 

参数化后

NO.

字段名称

说明

1

uuid 小模块唯一标识

2

alias 模块前台标识名,对应category

3

title 小模块名称

5

pageId 当前小工具属于第几屏,对应pages 的 uuid

6

colId 当前小工具属于第几列(暂分为1,2,3列)

7

sort 当前小工具顺序

8

moduleParameter 当前小工具具有的参数对象,现在采用json对象

采用键值对,加一个描述字段

{dataSourceUrl:XXXXX ,templateId:XXXX , type:XXX }

以上是基本属性,应该是字段吧…..

灰色部分可以不用,直接默认在第一页第一排,默认最大

其默认值在用户小工具表中体现出来

 

 

小工具参数moduleParameter

 

NO.

字段名称

说明

1

uuid 小模块唯一标识

2

moduleKey  

3

moduleValue  

4

des  

5

isTransfer 是否形成json传向前台

   
 

 

 

 

用户小工具设置表(用户参数设置)

userModules

NO.

字段名称

说明

1

uuid 小模块唯一标识

2

userId 用户id

3

moduleId 小工具Id

userPrefers 用户偏好设置

   
 

 

 

用户小工具参数偏好userModulePre

 

NO.

字段名称

说明

1

uuid 小模块唯一标识

2

preKey  

3

preValue  

4

des  

5

isTransfer 是否形成json传向前台

   
 

 

最终表设计


核心实现代码如下:原来的datalist的代码我就不发了:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
/// <reference path="../scripts/jQuery-1.4.1.js" />
/// <reference path="dataList.js" />
/// <reference path="itemTemplate.js" />
//全局变量设置
var templateWarehouse = {};
var userModuleData = {};
var leftColModule, centerColModule, rightColModule;
//首页模块加载参数
var moduleLoadPara = {};
moduleLoadPara.index = 1; //初始化加载索引
moduleLoadPara.num = 1; //每次索引加载个数
moduleLoadPara.isLoad = false; //当前是否在加载
//---------------------------------------------------------------------------
//第一步,加载模板
function loadTemplate() {
    $.ajax({
        type: "post",
        url: "../Ajax.aspx?SQL=select * from templates ",
        type: "json",
        async: false,
        success: function (data) {
            $.each(data, function (i, item) {
                templateWarehouse[item.templateKey] = item.templateValue;
            });
        }
    });
}
//加载所有模板数据
function loadModuleData(isAsync) {
    var _isAsync = false;
    if (isAsync)
        _isAsync = isAsync;
    var userId = "wl";
    $.ajax({
        type: "post",
        url: "../Ajax.aspx?SQL=select * from modules m ,userModules um where 1=1 and m.uuid=um.moduleId and um.userId='" + userId + "'",
        type: "json",
        async: _isAsync,
        success: function (data) {
            userModuleData["1"] = [];
            userModuleData["2"] = [];
            userModuleData["3"] = [];
            userModuleData["4"] = [];
            userModuleData["5"] = [];
            $.each(data, function (i, item) {
                var userPrefs = item.userPrefers;
                if (typeof (userPrefs) == "string") {
                    userPrefs = eval("(" + userPrefs + ")"); ;
                }
                item.userPrefers = userPrefs;
                userModuleData[userPrefs.pageId].push(item);
            });
        }
    });
}
//---------------------------------------------------------------------------
//根据用户id,按要求加载其模板
function initModuleFrame(_pageId) {
    var pageId = 1;
    if (_pageId)
        pageId = _pageId;
    var $div = $("#main");
    $div.html("");
    loadColModule(pageId, "left_conter");
    loadColModule(pageId, "cell_conter");
    loadColModule(pageId, "right_conter");
}
function loadColModule(pageId, colId) {
    var $div = $("#main");
    var colData = getColData(pageId, colId);
    var colModule = new dataList(colId, "commonFrame");
    colModule.itemElementEvent = {
        _sortItems: {
            elementKey: ".content",
            eventType: "ready",
            funcName: itemSort
        },
        _itemMaxClick: {
            elementKey: ".btn_close",
            eventType: "click",
            funcName: itemMaxClick
        },
        _itemMenuClick: {
            elementKey: ".btn_more",
            eventType: "click",
            funcName: itemMenuClick
        }
    };
    colModule.className = "column";
    colModule.dataSource = colData;
    colModule.dataBind($div);
    if (colId == "left_conter") {
        leftColModule = colModule;
    } else if (colId == "cell_conter") {
        centerColModule = colModule;
    } else if (colId == "right_conter") {
        rightColModule = colModule;
    }
}
function getColData(pageId, colId) {
    var tempData = [];
    var data = userModuleData[pageId];
    $.each(data, function (i, item) {
        var userPrefs = item.userPrefers;
        if (userPrefs.pageId == pageId && userPrefs.colId == colId) {
            tempData.push(item);
        }
    });
    tempData = tempData.sort(function (a, b) {
        if (a.userPrefers.sort < b.userPrefers.sort) {
            return -1;
        }
        if (a.userPrefers.sort > b.userPrefers.sort) {
            return 1;
        }
        return 0;
    });
    return tempData;
}
//---------------------------------------------------------------------------
function loadItemMax(sender, param, e, isClick) {
    var dataSourceUrl = param.dataSourceUrl;
    var templateId = param.templateId + "-max";
    var title = param.title;
    var type = param.type;
    var id = sender.id;
    var $max = $("#list-max");
    var $main = $("#main");
    var $content = $("#list-max #list-max-content");
    if (isClick)
        $content.html("");
    var $title = $("#list-max #list-max-title");
    var $itemList = $("#list-max .list");
    var $listMore = $("#list-max #list-max-more");
    var isLoad = $listMore.val();
    $listMore.val("-1");
    var len = $itemList.length;
    var _index = len + 1;
    var itemStr = '<fieldset  class="list"><legend><span class="index">page' + _index.toString() + '</span></legend><div id="itemContent' + _index.toString() + '" style=" margin:10px 10px 10px 10px;">数据加载中......</div></fieldset>';
    $main.hide();
    $max.show();
    $title.html(title);
    if (type && type == "s-html") {
        var html = '<iframe scrolling="no" style="width:100%;height:200px" frameborder="0" src="' + dataSourceUrl + '"></iframe>';
        $content.html(html);
    } else {
        $content.append(itemStr);
        $.ajax({
            type: "post",
            url: dataSourceUrl,
            type: "json",
            async: true,
            success: function (data) {
                var $itemContent = $("#list-max #itemContent" + _index.toString() + "");
                $itemContent.html("");
                var dataNews = data;
                if (typeof (dataNews) == "string") {
                    dataNews = eval("(" + data + ")");
                }
                var listMax = new dataList(id + "_max", templateId);
                listMax.dataSource = dataNews;
                listMax.dataBind($itemContent);
                $listMore.val("1");
            }
        });
    }
}
//小工具事件定义,小工具事件处理函数
function itemMaxClick() {
    var sender = this;
    var param = arguments[0];
    var e = arguments[1];
    loadItemMax(sender, param, e, true);
    $(document).unbind("scroll");
    $(document).bind("scroll", function (e) {
        var windowHeight = $(window).height();
        var windowScrollTop = $(window).scrollTop();
        var documentHeight = $(document).height();
        var $listMore = $("#list-max #list-max-more");
        var isLoad = $listMore.val();
        if ((windowHeight + windowScrollTop + 100) > documentHeight && isLoad == "1") {
            loadItemMax(sender, param, e);
        }
    });
}
//小工具事件定义,点击出现菜单栏
function itemMenuClick() {
    var sender = this;
    var param = arguments[0];
    var e = arguments[1];
    //    alert("弹出菜单" + "--" + param.uuid1 + "--" + sender.id)
    var uuid = param.uuid1;
    var popupMenu = $("#popupMenu");
}
//定义小工具会用到的工具类
var moduleInitTool = {};
//适用于不同模板,若是有新模板,只需根据规范添加函数即可,工厂方法
moduleInitTool.jsonModule = function (sender, param, e) {
    var templateId = param.templateId;
    var id = param.uuid;
    var dataSourceUrl = param.dataSourceUrl;
    var $itemContent = sender.getItemElement(".content");
    if (dataSourceUrl) {
        moduleLoadPara.isLoad = false;
        $.ajax({
            type: "post",
            url: dataSourceUrl,
            type: "json",
            async: true,
            success: function (data) {
                $itemContent.html("");
                var dataNews = data;
                if (typeof (dataNews) == "string") {
                    dataNews = eval("(" + data + ")"); ;
                }
                var listItemNews = new dataList(id + "_news", templateId);
                listItemNews.itemElementEvent = {
                    clickTitle: {
                        elementKey: ".span2",
                        eventType: "click",
                        funcName: titleClick
                    }
                }
                listItemNews.dataSource = dataNews;
                listItemNews.dataBind($itemContent);
                //                if (sender.id == "right_conter_id_1")
                //                    setInterval(function () {
                //                        alert("重新加载" + sender.id);
                //                    }, 5000);
                moduleLoadPara.isLoad = true;
            }
        });
    }
}
moduleInitTool.htmlModule = function (sender, param, e) {
    var id = param.uuid;
    var dataSourceUrl = param.dataSourceUrl;
    var $itemContent = sender.getItemElement(".content");
    if (dataSourceUrl) {
        var html = '<iframe scrolling="no" style="width:100%;height:200px" frameborder="0" src="' + dataSourceUrl + '"></iframe>';
        $itemContent.html(html);
    }
}
moduleInitTool.weiboModule = function (sender, param, e) {
}
moduleInitTool.labelModule = function (sender, param, e) {
    var templateId = param.templateId;
    var id = param.uuid;
    var dataSourceUrl = param.dataSourceUrl;
    var customData = param.customData;
    var modulePara = param.moduleParameter;
    if (typeof (modulePara) == "string") {
        modulePara = eval("(" + modulePara + ")"); ;
    }
    templateWarehouse["labelHeadTemplate"] = modulePara.labelHeadTemplate;
    var $itemContent = sender.getItemElement(".content");
    $itemContent.html(customData);
    var lableHead = sender.getItemElement("#labelHead");
    moduleLoadPara.isLoad = false;
    $.getJSON("../Ajax.aspx?sql=select * from bigType", function (data) {
        var labelHeadList = new dataList(id + "_head", "labelHeadTemplate");
        labelHeadList.dataSource = data;
        labelHeadList.className = "labelHead";
        labelHeadList.itemEvent = {
            _labelHeadClick: {
                eventType: "click",
                funcName: function () {
                    var _sender = this;
                    var _param = arguments[0];
                    var _e = arguments[1];
                    var labelBody = sender.getItemElement("#labelBody");
                    var bigTypeId = _param.id;
                    var url = "../Ajax.aspx?sql=select * from smallType where bigTypeId=" + bigTypeId + "";
                    $.getJSON(url, function (_data) {
                        labelBody.html("");
                        var labelBodyList = new dataList(bigTypeId + "_head", templateId);
                        labelBodyList.dataSource = _data;
                        labelBodyList.dataBind(labelBody);
                    });
                }
            }
        };
        labelHeadList.dataBind(lableHead);
        moduleLoadPara.isLoad = true;
    });
}
function elementDatabind(s) {
    var sender = this;
    var param = arguments[0];
    var e = arguments[1];
    var type = param.type;
    if (type && type.length > 2) {
        var _funcName = type.substring(2) + "Module";
        //        eval(_funcName);//此方法不能异步调用
        var funcName = moduleInitTool[_funcName];
        if (funcName && typeof (funcName) == "function") {
            funcName(sender, param, e);
        }
    }
}
function titleClick() {
    var sender = this;
    var param = arguments[0];
    var e = arguments[1];
    var summary = sender.getItemElement(".summary");
    var isShow = summary.css("display");
    if (isShow == "none") {
        summary.show();
    } else {
        summary.hide();
    }
}
//用于小工具排序
function itemSort() {
    var sender = this;
    var html = sender.htmlElement;
//    alert(html.offset().top);
    $(".column").sortable({
        connectWith: ".column",
        cursor: 'move',
        opacity: 0.7,
        handle: " .title",
        stop: sortItemsAjax
    });
}
//向后台发送数据
function sortItemsAjax() {
    submitSortData("left_conter");
    submitSortData("cell_conter");
    submitSortData("right_conter");
}
function submitSortData(colId) {
    var parent = $("#" + colId);
    var $children = parent.children();
    $children.each(function (i, item) {
        var id = item.id;
        var itemId = "#" + id + " #itemId";
        var $uuid = $(itemId);
        var uuid = $uuid.html();
        var userPrefers = item.userPrefers;
        $.ajax({
            type: "post",
            url: "../Ajax.aspx?sort=" + i + "&colId=" + colId + "&id=" + uuid + "&no=no",
            type: "json",
            async: true,
            success: function (data) {
            }
        });
    });
}
//---------------------------------------------------------------------------
//总体外部流程,外部方法
function mainProcess() {
    var $max = $("#list-max");
    var $main = $("#main");
    var $btn_Narrow = $("#list-max .btn_Narrow");
    $btn_Narrow.unbind("click");
    $btn_Narrow.bind("click", function () {
        if ($max.css("display") == "none") {
            $main.hide();
            $max.show();
        } else {
            $(document).unbind("scroll");
            var $listMore = $("#list-max #list-max-more");
            $listMore.val("-1");
            $main.show();
            $max.hide();
        }
    });
}
function LaterEvent() {
    var commomEvent = {
        _loadItems: {
            elementKey: ".content",
            eventType: "ready",
            funcName: elementDatabind
        }
    };
    var num = (moduleLoadPara.index) * (moduleLoadPara.num);
    for (var i = 0; i < num; i++) {
        if (leftColModule.items[i])
            leftColModule.items[i].eventAdd(commomEvent);
        if (centerColModule.items[i])
            centerColModule.items[i].eventAdd(commomEvent);
        if (rightColModule.items[i])
            rightColModule.items[i].eventAdd(commomEvent);
    }
    moduleLoadPara.index++;
    
    //    leftColModule.eventAdd(commomEvent);
    //    centerColModule.eventAdd(commomEvent);
    //    rightColModule.eventAdd(commomEvent);
}
function delayLoad() {
    var commomEvent = {
        _loadItems: {
            elementKey: ".content",
            eventType: "ready",
            funcName: elementDatabind
        }
    };
    $(document).unbind("scroll");
    $(document).bind("scroll", function (e) {
        if (moduleLoadPara.isLoad) {
            var num = (moduleLoadPara.index) * (moduleLoadPara.num);
            var oldNum = (moduleLoadPara.index-1) * (moduleLoadPara.num);
            for (var i = oldNum; i < num; i++) {
                if (leftColModule.items[i])
                    leftColModule.items[i].eventAdd(commomEvent);
                if (centerColModule.items[i])
                    centerColModule.items[i].eventAdd(commomEvent);
                if (rightColModule.items[i])
                    rightColModule.items[i].eventAdd(commomEvent);
            }
            moduleLoadPara.index++;
        }
    });
}
function pageClick() {
    for (var i = 1; i <= 5; i++) {
        var page = $("#pageIndex" + i.toString());
        page.unbind("click");
        page.bind("click", function (e) {
            var _p = $(this);
            initModuleFrame(_p.text());
            LaterEvent();
            var $max = $("#list-max");
            var $main = $("#main");
            $main.show();
            $max.hide();
            $(document).unbind("scroll");
            var $listMore = $("#list-max #list-max-more");
            $listMore.val("-1");
            //置顶
            $('html,body').animate({ scrollTop: '0px' }, 800);
            //            A:return false --->In event handler ,prevents default behavior and event bubbing 。
            //return false 在事件的处理中,可以阻止默认事件和冒泡事件。
            //B:event.preventDefault()---> In event handler ,prevent default event (allows bubbling) 。
            //event.preventDefault()在事件的处理中,可以阻止默认事件但是允许冒泡事件的发生。
            //C:event.stopPropagation()---> In event handler ,prevent bubbling (allows default behavior).
            //event.stopPropagation()在事件的处理中,可以阻止冒泡但是允许默认事件的发生。
            e.stopPropagation();
            return false;
        });
    }
}
function pageLoad() {
    mainProcess();
    loadTemplate();
    loadModuleData();
    initModuleFrame();
     
    LaterEvent();
    pageClick();
    //置顶
    $('html,body').animate({ scrollTop: '0px' }, 800);
    delayLoad();
    //5分钟更新一次模块数据
    var updateModuleData = setInterval("loadModuleData(true)", 50000);
}
//---------------------------------------------------------------------------

 

 

 


代码太多不做说明了,以后肯定会封装,

就现在看来整个应用感觉问题不少,实际用处不大吧。

后面点代码整理后再一并发出吧,如果有需要的话。

[转载]15本免费的WEB开发书籍 - 末世码农 - 博客园

mikel阅读(889)

[转载]15本免费的WEB开发书籍 – 末世码农 – 博客园.

当今互联网已经成为每一个人的信息知识来源。假如你想学习任何事情,可以很容易在互联网上轻松找到相关的信息,即使它是很简单的事情。在互联网上有成千上万的教程和指南可以用来学习与工作相关的技术和内容。

Web开发人员同样也经常通过互联网获取关于HTML和编码的相关知识。为了帮助这些开发人员,以下是我们收集的一些优秀电子书供大家学习。

1. HTML5 Quick Learning Guide

html5quickguide1

 

如果您想学习HTML5,这本电子书将帮助您学习所有HTML5的基本标签元素。

2. Head First HTML with CSS and XHTML

head-first-html

有了这本电子书,你可以使用HTML和CSS技术轻松地创建基于标准的网页。

3. Best Practices for Developing a Web Site

developing-website

这是一个很好的电子书,将引导你建立一个网站和计划项目,无论你正在开发一个内部网站或外包项目。

4. HTML Wtf?

html5-wtf-full

这本书简要概述了由HTML5带来的变化。

5. HTML And XHTML

html-xhtml

一个了解HTML和XHTML的简单而全面的指南。

6. Javascript Programming for The Absolute Beginner

js-programming

JavaScript不了解?不要担心,这本书将帮助你学习JavaScript,即使你不知道任何关于JavaScript的知识。

7. Dive into Accessibility

dive-into-accessiblity

这本电子书回答了一些关于如何建设一个网站的简单问题。

8. Getting Real

getting-real

这本电子书介绍了在建设一个网站时需要注意的一些问题。

9. Web Design in a Nutshell

webdesign-in-nutshell

这本书包含了CSS与XHTML编码和Web设计所有相关的信息。

10. Foundations of Ajax

ajax

本书介绍了Ajax技术到现有应用或新开发应用程序所需要所有工具。

11. Up to Speed on HTML5 and CSS 3

html5-css3

介绍关于新兴的HTML5和CSS3标准。

12. Learning PHP 5

php5

这本将帮助你学习PHP5拥有的一些高级功能,如面向对象功能和对XML与Web Services的支持等。

13. 20 Things I Learned about Browsers and The Web

2-thing-browser-web

这本书包含了所有你想了解的关于网站、Cookie和历史记录等知识。

14. Web Style Guide: 3rd Edition

web-style-guide

这本书将告诉你如何使你的网站更容易让大家使用。

15. Essential Javascript and JQuery Design Patterns

essential javascript

通过这本电子书,你将学习到如何使用Javascript和JQuery设计模式

[转载]你应该知道的asp.net 之 服务器端包括指令 - for certain - 博客园

mikel阅读(919)

[转载]你应该知道的asp.net 之 服务器端包括指令 – for certain – 博客园.

ASP.NET中的服务端包括指令简单点就是一个<!– #include file|virtual=”filename” –>这样的指令,msdn中的名词解释是:将指定文件的内容插入 ASP.NET 文件中,包括网页(.aspx 文件)、用户控件文件(.ascx 文件)和 Global.asax 文件。插入静态文件这个基本功能就不说了,插入aspx、ascx,这功能算是挺强了,asax哥就有点困惑了,这个暂且不管,今天要说的就是这个指令。

尴尬的存在

服务器端包括指令在web的开发技术中肯定是个标配了。ASP.NET前身asp中就已经有了这个指令,做为唯一一个引用外部文件的指令,入门的开发人员 都应该使用过;php中的include、require,几乎用滥了,曾经使用过一段php,这指令常用的不能再常用了;jsp没过开发经验,搜索了 下,也有这个include指令,使用情况应该也很平常。但是asp.net中的include几乎是在被遗忘的角落里,读过的asp.net的书里,很 少会提到它。因此,对于没有asp开发经验直接学习asp.net的来说,可能要本不知道它的存在。为什么??

造成尴尬存在原因之一,就是webform中最重要的功能之一:用户控件。为什么要使用include,是因为被引用的内容会在很多地方被使用到–重 用,例如页头、页脚、网站功能块。而恰恰用户控件就是为此而生,它天生可重用的特性,及强大的控件事件流程,就如MSDN中所说:

“尽管您仍然可以使用 #include 标记(通过将公共服务器端代码、控件或 HTML 标记放入要包括在其他网页中的文件内)以实现代码重用,ASP.NET 常用的首选方法是使用 Web 用户控件。用户控件提供了一个面向对象的编程模型,并且提供了比服务器端包含文件更多的功能。”

微软都不待见它了,紧接着,include也被开发人员打入冷宫,但是,请不要忘记,它存在。

include详解

说是详解,其实不打算在这里详解,请参见:服务器端包括指令语法。语法、用法、示例什么的,这里也不说,大家自己去看或自己试验吧。

include优势

说到优势,肯定要有个比较的对象了,对了,这个对象就是用户控件。用户控件是啥玩意,不解释。

优势之一:静态文件的重用。

用户控件功能强大,但是即使是简单如静态内容以用户控件的形式实现,它也必须经历用户控件的生命周期,相比性能就差了。

你可能会说,现如今网站又有多少还能是纯静态的内容呢,譬如页脚,那些项肯定也是网站CMS后台管理的,怎么可能以静态文件的形式存在,只能使用用户控件,并且给用户控件加上outputcache,这样即保证了灵活性,性能也能有保证,这样才是最完美的。

不错,第一,不同的项目不同的需求,肯定有些时候就是会引用静态内容,这时要保证使用这些静态内容以静态的形式存在,不要放到用户控件里。第二,网 站的优化,无论何时,总有一种观点,就是静态化,例如上边的页脚,我们可以通过生成一个静态的页脚文件,当CMS对这些项有修改时,重新生成这个文件,然 后通过include方式引用,相对于缓存的不可预测性和一定的不可控性,优势比用户控件还要灵活,并且性能更好。第三,协作,就是对于比较大型的网站, 页面某些控件,但是这些数据是其它团队负责的,而一些相对稳定的内容或者对变化不太敏感的内容,生成静态文件之后自动分发,而对于这些文件的引用也是很常 见的。

其实这个也可能是唯一的include的优势了。

优势之二,可穿透的封装。

可重用的东西一般都是封装的。用户控件是一个独立的个体,它寄宿于页面,但本身却像是页面上一个自由王国,碰到王国内部的处理,页面会把处理权交给 控件自己的去处理,例如页面page_load加载时,碰到控件只能调用控件提供的load处理方法,这就是控件的封装,只有自身愿意公开的属性和方法才 能被页面访问,这是标准的封装,这也是由asp.net的页面生命周期决定的。因此,如果控件要使用寄宿页面的一个变量,那么它应该公开一个属性,然后由 页面在代码里主动给它赋值。

同样的,不同的机制决定了不同的行为。从MSDN中:

赋予 File 或 Virtual 特性的值必须用引号 (“”) 括起来。在执行任何动态代码之前处理被包含的文件。

重要的一句,在执行任何动态代码之前处理被包含的文件。这句话什么意思??

一层意思就是说被包含文件在被处理之后内容才会加入到页面中,动态代码执行时,执行的代码根本就区分不出来这块内容是引用的还是自己本来就有的,也 就是说我们可以用来重用的静态文件里加入<% 代码 %> 或者<%= 页面变量 %> 这样的代码,它们会被页面处理为相应的内容。这就是我所谓的“可穿透的封装”。这种方法当然比控件公开属性,页面主动赋值要更灵活。

二层意思如果引用的aspx或ascx,那么会在引用的动态内容执行输出之后,页面代码才会执行,这样就不如用户控件调用将流程在页面内好了,尽量不要使用include引用动态页面。

因此我们应只用include引用静态文件,但是静态文件里可以包含服务端标记代码。

 

知无不言,但怎奈能力有限,词不达意,就当抛砖引玉吧。大家尽量看,不过可以肯定的是可以以静态文件存在的重用就用include吧。可能还会有其它更适合的使用场景和更多的优势,还请有充分经验的补充。

[转载]DedeCMS后台无法加载编辑器,DedeCMS无法加载编辑器,织梦编辑器问题 - dong_jquery的日志 - 网易博客

mikel阅读(925)

转载DedeCMS后台无法加载编辑器,DedeCMS无法加载编辑器,织梦编辑器问题 – dong_jquery的日志 – 网易博客.

DedeCMS后台无法加载编辑器,DedeCMS无法加载编辑器,织梦编辑器问题 转载于  http://blog.7777e.com/?p=424 郑州php网站建设
DedeCMS后台无法加载编辑器,首先建议通过以下这几种方法尝试解决,如果没有成功,那么可能是服务器Apache的问题(如果是在本地测试,那么可以尝试换个Apache)。
1、编辑器位置 404 无法显示网页
解决1:有可能文件出错。重新复制升级包中 “include\FCKeditor”的目录文件
解决2:5.5版本只支持fck编辑器,需做如下设置: 后台-“系统”-“系统基本参数”-“核心设置”-“Html编辑器选项(目前仅支持fck):” 这里填写fck , 已正确的用户无需更改. (很多情况下这种方法即可解决)
2、编辑器位置空白,文字可以输入,但页面代码报错
研究:个人测试是IE浏览器的问题,本人使用的是IE6.0,应是不兼容的问题。
解决1:将之前的老版fck编辑器覆盖过来
解决2:使用非IE核心的浏览器。如:火狐浏览器。
3、从本地传输include文件夹中的FCKeditor文件夹,覆盖到空间的里面的FCKeditor文件夹。因为有可能传输过程中丢失数据,所以覆盖一边可能能解决。
4、修改空间中的\include\FCKeditor\editor\lang中的四个文件,下载到本地然后重新编辑在上传空间。因为有可能这四个文件出现乱码导致编辑器不能正常使用。
5、 更改空间中的 include文件夹中的/htmledit/index.php;找到第六行 if($modetype==”full”){  改为  if($modetype==”full”||$modetype==””||$modetype==”basic”){
注 明:如果以上的都没有解决你的问题,或者你对后台的文件没有碰过(就是没有修改过后台的所有文件,或者新增后台文件),那么,很可能是你的路由器问题,建 议你多换几个不同的路由器来访问后台试验一下,这个问题是楼主亲自遇到的,上面的所有步骤全部都试验了,也重装了系统,重装过dede, 最后的问题,竟然出现在路由器上,悲催了,折腾死了,也没找到答案,辛亏一友友提示!!!!《路由器》

[转载]缓存、动态页面静态化、网站优化(转) - yylp521 - 博客园

mikel阅读(1512)

[转载]缓存、动态页面静态化、网站优化(转) – yylp521 – 博客园.

一、缓存

缓存(Cache)技术在软件开发过程中有着广泛的用途, 它对提升软件性能和改善客户体验有很大帮助. 所谓缓存, 是指将那些经常重复的操作结果暂时存放起来, 在以后的执行过程中, 只要使用前面的暂存结果即可. 缓存技术在日常生活中随处可见, 就拿排队买票来说吧: 买票时需要先排队, 等轮到自己了, 再告诉售票员你需要买那里的票, 售票员查询完后告诉你有还是没有. 若有, 付款走人; 若没有, 离开队列. 例如, 北京到上海是热门线路, 票很快就卖完了, 但是根据购票的规则, 仍然有许多乘客会先排队, 然后问有没有北京到上海的票, 结果只能默默离开队列. 如果每个人都问一遍的话, 售票员估计得烦死. 于是, 售票员在窗口上挂个牌子, 写上”北京到上海(无票)”, 之后再要购买该票的乘客看到这个牌子就不会在队列中继续等待了. 这就是生活中的一个缓存事例.

由此可见, 缓存的思想就是: 尽量减少执行那些费时的、需要经常重复执行的工作的次数. 在计算机的世界中, 执行速度最慢的操作就是IO操作, 也就是读写硬盘的操作. 因此, 构建一个高效的桌面应用程序或网站应用程序时, 很重要的一个因素就是尽可能少的减少IO操作. 比如, 相同的数据没有必要每次都从数据库中读取, 在第一次读取出来后缓存起来即可, 第二次就不需要在做IO操作了.

那么在我们开发Web网站的过程中, 到底有多少工作可以采用用缓存呢? 或者说, 我们可以在哪些地方使用缓存呢? 见下图: 下图是客户端浏览器和Web服务器之间的一次完整的通信过程, 红色圆圈标示了可以采用缓存的地方.

关于上图, 如果一边描述请求过程, 一边描述缓存可能会不便理解. 像做手术一样, 这里首先弄清楚整个请求过程, 然后从整体上对关键节点设置的缓存进行描述.

上图所示的请求过程:

客户端浏览器首先发送HttpRequest请求到IIS服务器, 当然也有一些企业内部网络或者小型机关网络中会设置代理服务器. 代理服务器在请求过程中可以看作是一个转发请求的中继器, 或者干脆在这里忽略代理服务器也行.

IIS服务器接受到请求后, 会根据请求地址进行判断, 例如: *.html、*.jpg等静态内容的资源, 会直接由IIS寻找资源并返回给客户端浏览器; 例如: *.aspx、*.ashx、*.asmx等ASP.NET中的动态内容会交给aspnet_isapi.dll处理.

aspnet_isapi.dll将请求转交给ASP.NET组件处理, 这时asp.net组件会启动一个asp.net的进程(进程名称为w3wp.exe), 该进程会创建一个应用程序域(一个进程可以创建多个应用程序域, 一个应用程序域中可以跑多个线程), 在应用程序域中会创建asp.net执行环境HttpRuntime, 在asp.net执行环境中创建HttpContext对象并启动HttpApplication应用程序管道(HttpRuntime中可以同时创建多 个HttpContext和HttpApplication, 也就是说在HttpRuntime中可以同时进行多套处理流程). 其中, HttpContext其实就是对一堆参数的封装, 如: HttpRequest、HttpResponse、HttpServerUtility等, HttpContext可以在整个HttpApplication管道中使用, 可以取出其中的参数, 并且HttpContext本身也可以当作一个字典来使用. HttpApplication也就是asp.net应用程序管道或者叫asp.net应用程序生命周期, 它是实际处理客户端请求的地方, 我们只能通过Global.asax或者IHttpModule接口来人为控制客户端请求的处理过程.

在HttpApplication启动后, 在IIS6.0中会有17个事件(IIS7.0中会更多), 其中包括: 用户验证、用户授权等等, 这些事件中有两个重要的时间点, 第一个时间点是在第7个事件之后, 也就是PostResolveRequestCache事件之后, 映射请求的处理程序(需查找配置文件); 第二个时间点是在第11个事件之后, 也就是PreRequestHandlerExecute事件之后, 执行处理程序, 像对*.aspx的处理程序System.Web.UI.PageHandlerFactory.Page就是实现了IHttpHandler接口的类. 在这个时间点中执行处理程序并进入页面的生命周期, 其中最重要的是页面处理的11个事件(如: Page_Int、Page_Load、Page_PreRender、Page_SaveState、Page_Unload), 我们在这11个事件中, 完成读取aspx页面模板、控件预读、读取视图状态、控件属性赋值等工作, 并且要求我们程序员调用BLL、DAL、DE以及数据库等完成业务逻辑的处理工作. 最后, 在由页面对象中的Render方法将结果发送给客户端浏览器, 客户端浏览器再呈现给用户.

至此, 完成了一次完整的请求过程. 那么, 我们的缓存工作应该在哪里做呢?

缓存该在哪里做, 取决于我们能在整个请求过程的什么位置动手. 我们依然从客户端浏览器开始:

首先, 最好的情况是客户端不发送任何请求直接就能获得数据, 这种情况下, 用于缓存的数据保存在客户端浏览器的缓存中.

其次, 在具有代理服务器的网络环境中, 代理服务器可以针对那些经常访问的网页制作缓存, 当局域网中第一台主机请求了某个网页并返回结果后, 局域网中的第二台主机再请求同一个网页, 这时代理服务器会直接返回上一次缓存的结果, 并不会向网络中的IIS服务器发送请求, 例如: 现在的连接电信和网通线路的加速器等. 但是代理服务器通常有自己专门的管理软件和管理系统, 作为网站开发人员对代理服务器的控制能力有限.

再次, 前面也说过, 当用户将请求地址发送到IIS服务器时, IIS服务器会根据请求地址选择不同的行为, 如: 对于*.aspx页面会走应用程序管道, 而对*.html、*.jpg等资源会直接返回资源, 那么我们可以把那些频繁访问的页面做成*.html文件, 这样用户请求*.html, 将不用再走应用程序管道, 因此会提升效率. 例如: 网站首页、或某些突发新闻或突发事件等, 可以考虑做成静态网页. 就拿”天气预报的发布页面”打比方: 天气预报的发布页面的访问用户非常多, 我们可以考虑将发布页做成静态的*.html网页, 之后在整个网站程序启动时, 在Gboabl.asax的Application_Start事件处理器中, 创建子线程以实现每3个小时重新获取数据生成新的天气发布页面内容.

之后的asp.net的处理流程, 作为程序员我们是无法干涉的. 直到启动HttpApplication管道后, 我们才可以通过Global.asax或IHttpModule来控制请求处理过程, 在应用程序管道中适合做整页或用户控件的缓存. 如: 缓存热门页面, 我们可以自动缓存整个网站中访问量超过一定数值(阀值)的页面, 其中为了减小IO操作, 将缓存的页面放在内容中.

最后, 我们程序员可以操作的地方就是页面处理管道或者称为页面生命周期, 我们可以页面对象的11个事件中, 采用数据缓存的方式减小访问数据库的次数, 也就是减少IO操作. 还有些真假分页等问题涉及网站的优化, 稍后再讨论.

总之, 我们可以采用缓存的地方有以下5处:

1. 客户端浏览器的缓存(非Cookie, Cookie只能放字符串信息)

2. 如果有代理服务器的话, 可以在代理服务器中缓存整个网页(本篇仅讨论网站开发的相关内容)

3. 将频繁访问的资源做成*.html等的静态内容, (我们可以将静态资源缓存在ramdisk等开辟的内存盘中,  以进一步减少对硬盘的访问).

4. 在应用程序处理管道或应用程序生命周期中, 进行整页缓存或用户控件的局部缓存

5. 在页面处理管道或页面生命周期中, 做数据缓存

下面我们来详细论述这些缓存手段:

1. 客户端浏览器上的缓存(非Cookie, Cookie中的内容为: 键和值均为string类型的键值对)

我们可以通过在Http回应中增加特定的头部说明来指定浏览器的缓存策略; 添加头部说明的手段既可以通过页面指令声明设置, 也可以通过编程方式设置.

对于图片、JavaScript脚本、CSS等资源, 可以在IIS管理器中, 右击图片等资源, 选择”属性” –> HttpHeaders后, 勾选Enable Content Expiration并设置时间即可. 一种值得推荐的手段是, 将需要缓存的资源分类, 如: image/dynamic/、image/static/, 这样我们可以再文件夹上, 选择属性中的HttpHeaders进行设置. 否则, 针对每一个静态资源设置HttpHeaders将是件非常痛苦的事情. 此外, 还可以采用一款名为CacheRight的工具可以提供缓存资源的统一配置.

查看或设置浏览器缓存位置:  IE –> Internet选项 –> 常规 –> 临时Internet文件 –> 设置

 

Html文件的Head中的缓存设置:

<meta http-equiv=”pragma” content=”no-cache” />

<meta http-equiv=”Cache-Control” content=”no-cache” />

<meta http-equiv=”expires” content=”Wed, 26 Feb 1997 08:21:57 GMT” />

浏览器中关于Cache的3属性:

Cache-Control:

设置相对过期时间, max-age指明以秒为单位的缓存时间. 若对静态资源只缓存一次, 可以设置max-age的值为315360000000 (一万年).

Http协议的cache-control的常见取值及其组合释义:

no-cache: 数据内容不能被缓存, 每次请求都重新访问服务器, 若有max-age, 则缓存期间不访问服务器.

no-store: 不仅不能缓存, 连暂存也不可以(即: 临时文件夹中不能暂存该资源)

private(默认): 只能在浏览器中缓存, 只有在第一次请求的时候才访问服务器, 若有max-age, 则缓存期间不访问服务器.

public: 可以被任何缓存区缓存, 如: 浏览器、服务器、代理服务器等

max-age: 相对过期时间, 即以秒为单位的缓存时间.

no-cache, private: 打开新窗口时候重新访问服务器, 若设置max-age, 则缓存期间不访问服务器.

private, 正数的max-age: 后退时候不会访问服务器

no-cache, 正数的max-age: 后退时会访问服务器

点击刷新: 无论如何都会访问服务器.

Expires:

设置以分钟为单位的绝对过期时间, 优先级比Cache-Control低, 同时设置Expires和Cache-Control则后者生效.

Last-Modified:

该资源的最后修改时间, 在浏览器下一次请求资源时, 浏览器将先发送一个请求到服务器上, 并附上If-Unmodified-Since头来说明浏览器所缓存资源的最后修改时间, 如果服务器发现没有修改, 则直接返回304(Not Modified)回应信息给浏览器(内容很少), 如果服务器对比时间发现修改了, 则照常返回所请求的资源.

注意:

Last-Modified属性通常和Expires或Cache-Control属性配合使用, 因为即使浏览器设置缓存, 当用户点击”刷新”按钮时, 浏览器会忽略缓存继续向服务器发送请求, 这时Last-Modified将能够很好的减小回应开销.

ETag将返回给浏览器一个资源ID, 如果有了新版本则正常发送并附上新ID, 否则返回304, 但是在服务器集群情况下, 每个服务器将返回不同的ID, 因此不建议使用ETag.

以上描述的客户端浏览器缓存是指存储位置在客户端浏览器, 但是对客户端浏览器缓存的实际设置工作是在服务器上的资源中完成的. 虽然刚才我们介绍了有关于客户端浏览器缓存的属性, 但是实际上对这些属性的设置工作都需要在服务器的资源中做设置. 我们有两种操作手段对浏览器缓存进行设置, 一个是通过页面指令声明来设置, 另外一个是通过编程方式来设置.

浏览器缓存的设置手段:

第一: 通过页面指令声明来设置HTTP的缓存

页面指令<%@ OutputCache Location=”Any” Duration=”10” VaryByParam=”ProductId” VaryByHeader=”Accept-Language”%>中的Location用来设置缓存的位置, 该属性常见的值为:

Any(默认): 输出缓存可以位于任何地点, 对应于HttpCacheability.Public. 如: 客户端浏览器、代理服务器或服务器本身.

Client: 只能位于发出请求的客户端浏览器, 对应于HttpCacheability.Private.

Downstream: 输出缓存可以位于除服务器本身的其他任何地方, 如: 客户端浏览器、代理服务器.

Server: 输出缓存位于Web服务器本身, 对应于HttpCacheability.Server

ServerAndClient: 输出缓存只能位于服务器本身或客户端浏览器, 对应于HttpCacheability.Private和HttpCacheability.Server

None: 禁用输出缓存, 对应于HttpCacheability.NoCache.

VaryByParam属性: 根据请求参数的不同而缓存不同的版本. 多个值用分号(;)分隔, *号表示为任意参数或参数组合缓存不同版本, “none”表示只缓存一个版本.

VaryByHeader属性: 根据请求头来缓存不同的版本, 如同一页面的不同语言版本.

VaryByCustom属性: 根据自定义参数来缓存不同的版本, 如: VaryByCunstom=”browser”是系统已实现的, 根据浏览器名称和版本号缓存不同的版本. 也可以, 根据自定义参数来缓存, 如: VaryByCustom=”happy”, 此时系统不知道如何解释happy, 因此需要在Global.asax或IHttpModule实现类中重写GetVaryByCustomString()方法, 来完成处理逻辑.

VaryByControl属性: 根据用户控件中的服务器控件ID来缓存不同版本.

更高级的方式, 是通过配置文件来设置HTTP的缓存.

页面指令为<%@ OutputCache CacheProfile=”cacheconfig”%>, 其中cacheconfig是配置文件中的缓存配置节中CacheProfile的名称.

View Code

第二: 通过编程方式设置HTTP的缓存特性

在页面后台文件*.cs文件或*.ashx等其他代码文件中, 我们可以通过HttpResponse类的实例对象上的Cache属性or页面对象的Response属性上的Cache属性来设定缓存特性.

VaryByHeaders[“Accept-Language”] = true: 设置缓存同一页的不同语言版本

SetMaxAge(): 设置活动过期时间

SetExpires()方法: 设置绝对过期时间

SetLastModified(): 设置最后修改时间

SetNoStore(): 设置不要缓存

SetNoServerCaching(): 关闭服务器缓存

SetCacheability(): 设置缓存位置, 其参数类型为HttpCacheability枚举类型, 可选项如下:

NoCache: 将会在Http响应头添加Cache-Control: no-cache标头, 表示禁用缓存.

Private: 只在客户端浏览器缓存(关闭代理服务器缓存).

Public: 可在任意位置缓存.

Server: 只能在服务器上缓存.

ServerAndNoCache: 只能在服务器上缓存, 其余都不能缓存, 将会在Http响应头添加Cache-Control: no-cache标头.

ServerAndPrivate: 只能在服务器和客户端浏览器缓存, 不能再代理服务器上缓存.

注意:

其中, 设置NoCache和ServerAndNoCache, 将会在Http响应头中添加如下内容:

Cache-Control: no-cache

Pragma: no-cache

Expires: -1

以上设置可防止浏览器在自己的历史记录文件夹中缓存该页, 前进和后退按钮都会请求响应新的版本.

SetAllowResponseInBrowserHistory()方法: 允许在浏览器历史记录中响应, 若参数为true, 则可覆盖HttpCacheability的设置, 但如果SetCacheability设置为NoCache或ServerAndNoCache, 则会忽略SetAlloewResponseInBrowerHistory的值.

Cookies和代理缓存不能结合的很好, 当使用cookie时,不要允许代理缓存。设置Location为Private、Server或ServerAndPrivate。

用户控件中的缓存(部分页缓存)不可以使用Location属性, 因为在不同页面中, 同一用户控件缓存的结果将会有多个. 如果希望用户控件缓存同一结果, 可以添加Shared=”true”属性来实现.

页面缓存和用户控件缓存的Duration属性的时间长短问题: 结论是按照时间长的来算. 如: 页面缓存长, 用户控件缓存短, 则用户控件的缓存内容会和页面缓存一起到期; 若用户控件缓存长, 页面缓存短, 则用户控件的缓存内容会一直到用户控件的缓存时间结束.

Http Referer是Request.Header的一部分, 他可以指出是从哪个页面链接过来的, 但是可以伪造. 我们可以通过this.Request.Headers[“Referer”]或this.Request.UrlReferrer获得其内容, 最好通过this.Request.UrlReferrer.OriginalString来避轨编码不一致的问题.

2. 和代理服务器相关的缓存措施

对我们程序员来说, 和代理服务器相关的操作无非也是在服务器上设置的, 参见: 1中的浏览器缓存设置中的操作手段.

3. 将频繁访问的资源做成*.html等的静态内容

像天气预报这样的即时性要求不是很强的页面, 非常适合做成静态的html页面. 我们可以在网站的Global.asax中, 每3个小时重新读取一下数据, 将读取的数据生成新的html页面并覆盖掉老页面. 网站首页也是访问极其频繁的, 做法类似, 只是时间短点. 为简便起见, 直接用txt文件模拟数据库.

//App_Data/WeatherData.txt, 内容随便.

// WeatherReport/Today.html

View Code

注意: Global.asax是继承自HttpApplication类的子类, 如果网站存在Global.asax, 它会在HttpRuntime运行环境创建HttpApplication类的对象时, 用Global类的对象替带HttpApplication类的对象, 并且Application_Start、Application_End事件激发只会在服务器启动时进行, 而Application_BeginRequest等事件处理器则每次请求时都会激发. 本例在Application_Start中为保证主线程不被过分占用, 采用子线程创建timer并更新天气信息.

//Global.asax

View Code

4. 通过Global.asax或IHttpModule在应用程序处理管道中做整页缓存或用户控件局部缓存

第3所述的情况, 可以认为是在IIS级别的缓存, 它的目的是避免或劲量少走应用程序处理流程. 但是这种IIS级别的缓存只适合那些很少的热门资源, 如果所有页面都做成静态页面就又回到了古老的纯html时代, 当然是得不偿失. 那么大部分的资源还需要是动态的, 也就是说必须要走应用程序处理流程, 程序员可以通过Global.asax和IHttpModule来人为的影响应用程序处理管道. 因此, 在HttpApplication级的缓存是正是通过Global.asax或IHttpModule来完成的.

“动态页面静态化”:

在文章的标题中, 提到过一种称为”动态页面静态化”的技术, 暂且不管这个称谓是否贴切(园子有议论这个的帖子). 无论名称是什么, 我觉得那只是个称谓而已, 关键的是它用来解决什么问题. 早期的搜索引擎并不能很好的收录如*.aspx等的动态页面, 但是对*.html等静态页面收录良好, 于是乎产生了一种UrlRewrite(Url重写)的技术. 另外一种”动态页面静态化”的技术就和今天的缓存挂钩了, 那就是把用户对*.aspx页面的请求结果缓存到html文件中.

第一种: UrlRewrite技术:

它的原理是把用户发出的对静态页面(*.html)的请求转化映射为对动态页面(*.aspx)的请求. UrlRewrite技术主要就是用来解决当时搜索引擎收录的问题, 如今的各大搜索引擎中宣称已经不存在这个问题了. 那么这个技术现今就没用了吗? 在一篇讲Yahoo团队的34条网站优化手段的文章中, 看到一处用UrlWriter的地方, 说的是为了减小客户端浏览器在请求回应过程中传递的数据, 在网站真正实施发布时尽量采用”短名称”, 如: 超链接中用p.html代替product.html、在JS中用d代表window.document等. 这篇文章说短文件名有利于减少传输过程中的数据量, 但是客户体验较差, 这时就可以通过UrlRewrite技术来改善用户体验, 具体做法是在html代码中使用p.html做超链接, 用户请求时依然采用product.html, UrlRewrite的工作就是把请求中的product.html转换映射为p.html. 这和”把*.html映射到*.aspx”是一个道理. 再有一个和缓存相关的用处, 就是url中含有类似*.aspx?id=123的查询字符串时, 很多代理是不会缓存这种请求的, 这时我们可以通过UrlRewrite技术将*.aspx?id=123变成/123.aspx, 从而使代理服务器缓存这种请求.

示例: 静态*html映射到动态页面*.aspx, 因为默认对*.html文件的处理是由IIS来完成的, 因此这里必须在IIS中, Web程序的属性中, “主目录” —> “应用程序设置” –> “配置”中分别添加*.htm和*.html到aspnet_isapi.dll的配置. 注意: “检查文件是否存在” 的勾一定要去掉, 这时对*.htm和*.html的处理将会交给asp.net处理.

// UrlRewriteDemo/App_Code/ HtmlToAspx.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
namespace HtmlAspxModel { ///<summary>/// Summary description for HtmlToAspx ///</summary>publicclass HtmlToAspx : IHttpModule { #region IHttpModule Members
publicvoid Dispose() { thrownew NotImplementedException(); }
publicvoid Init(HttpApplication application) { //更通用的做法是采用IHttpModule来操作HttpApplication管道 //注册事件application.BeginRequest +=new EventHandler(application_BeginRequest); }
void application_BeginRequest(object sender, EventArgs e) { HttpApplication ha = sender as HttpApplication; HttpContext hc = ha.Context;
string rawurl = ha.Context.Request.RawUrl;
if (rawurl.EndsWith(.htm,StringComparison.OrdinalIgnoreCase)) //判断以*.htm结尾{ string regnoparam =@”/(\w+)\.htm; //不含参数string regparam =@”/(\w+)\=(\d+); //含参数if (System.Text.RegularExpressions.Regex.Match(rawurl, regparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) //匹配成功{ System.Text.RegularExpressions.MatchCollection mc = System.Text.RegularExpressions.Regex.Matches(rawurl, regparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase); rawurl = rawurl.Replace(.htm, “”); for (int i =0; i < mc.Count; i++) { if (i ==0) { rawurl = rawurl.Replace(mc[i].Value, .aspx?+mc[i].Value.Substring(1,mc[i].Value.Length1)); } else { rawurl = rawurl.Replace(mc[i].Value, &+ mc[i].Value.Substring(1, mc[i].Value.Length 1)); } } } elseif (System.Text.RegularExpressions.Regex.Match(rawurl, regnoparam, System.Text.RegularExpressions.RegexOptions.IgnoreCase).Success) { rawurl = rawurl.Replace(.htm, .aspx); } hc.RewritePath(rawurl); } }
#endregion } }
复制代码

//UrlRewriteDemo/Default.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=Default.aspx.cs Inherits=_Default%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><p></p><a href=”WebFormNoParam.htm”>WebFormNoParam.htm</a></p><br /><p></p><a href=”WebFormWithParam/id=55/name=123.htm”>WebFormWithParam/id=55/name=123.htm</a></p><br /><form id=”form” method=”post” action=”WebFormWithParam.htm”><div><label for=”id”>编号:&nbsp;</label><input type=”text” id=”id” name=”id”/><br /><label for=”name”>姓名:&nbsp;</label><input type=”text” id=”name” name=”name”/><br /><input type=”submit” value=”提交”/></div></form></body></html>

//UrlRewriteDemo/WebFormNoParam.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=WebFormNoParam.aspx.cs Inherits=WebFormNoParam%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><h1>This is a aspx page!</h1></body></html>

//UrlRewriteDemo/WebFormWithParam.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=WebFormWithParam.aspx.cs Inherits=WebFormWithParam%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><label for=”id”>编号:&nbsp;</label><input type=”text” id=”id” runat=”server”/><br /><label for=”name”>姓名:&nbsp;</label><input type=”text” id=”name” runat=”server”/><br /></body></html>

//UrlRewriteDemo/web.config

View Code

复制代码
<?xml version=”1.0″?><!– Note: As an alternative to hand editing this file you can use the web admin tool to configure settings for your application. Use the Website->Asp.Net Configuration option in Visual Studio. A full list of settings and comments can be found in machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
–><configuration><configSections><sectionGroup name=”system.web.extensions” type=”System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><sectionGroup name=”scripting” type=”System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”scriptResourceHandler” type=”System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><sectionGroup name=”webServices” type=”System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”jsonSerialization” type=”System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”Everywhere”/><section name=”profileService” type=”System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”authenticationService” type=”System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”roleService” type=”System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/></sectionGroup></sectionGroup></sectionGroup></configSections><appSettings/><connectionStrings/><system.web><!– Set compilation Debug=”true” to insert Debugging symbols into the compiled page. Because this affects performance, set this value to true only during development. –><compilation debug=”true”><assemblies><add assembly=”System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add assembly=”System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/></assemblies></compilation><!– The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user.
–><authentication mode=”Windows”/><!– The <customErrors> section enables configuration of what to do if/when an unhandled error occurs during the execution of a request. Specifically,
it enables developers to configure html error pages to be displayed in place of a error stack trace.
<customErrors mode=”RemoteOnly” defaultRedirect=”GenericErrorPage.htm”> <error statusCode=”403″ redirect=”NoAccess.htm” /> <error statusCode=”404″ redirect=”FileNotFound.htm” /> </customErrors>
–><pages><controls><add tagPrefix=”asp” namespace=”System.Web.UI” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add tagPrefix=”asp” namespace=”System.Web.UI.WebControls” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></controls></pages><httpHandlers><remove verb=”*” path=”*.asmx”/><add verb=”*” path=”*.asmx” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”*” path=”*_AppService.axd” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ validate=”false”/></httpHandlers><httpModules><add name=”ScriptModule” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”HtmltoAspx” type=”HtmlAspxModel.HtmlToAspx”/></httpModules></system.web><system.codedom><compilers><compiler language=”c#;cs;csharp” extension=”.cs” warningLevel=”4″ type=”Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”WarnAsError” value=”false”/></compiler><compiler language=”vb;vbs;visualbasic;vbscript” extension=”.vb” warningLevel=”4″ type=”Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”OptionInfer” value=”true”/><providerOption name=”WarnAsError” value=”false”/></compiler></compilers></system.codedom><!– The system.webServer section is required for running ASP.NET AJAX under Internet Information Services 7.0. It is not necessary for previous version of IIS. –><system.webServer><validation validateIntegratedModeConfiguration=”false”/><modules><remove name=”ScriptModule”/><add name=”ScriptModule” preCondition=”managedHandler” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></modules><handlers><remove name=”WebServiceHandlerFactory-Integrated”/><remove name=”ScriptHandlerFactory”/><remove name=”ScriptHandlerFactoryAppServices”/><remove name=”ScriptResource”/><add name=”ScriptHandlerFactory” verb=”*” path=”*.asmx” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptHandlerFactoryAppServices” verb=”*” path=”*_AppService.axd” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptResource” preCondition=”integratedMode” verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></handlers></system.webServer><runtime><assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″><dependentAssembly><assemblyIdentity name=”System.Web.Extensions” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly><dependentAssembly><assemblyIdentity name=”System.Web.Extensions.Design” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly></assemblyBinding></runtime></configuration>
复制代码

补充知识 — 重置Form表单的Action属性:

a. 自定义类RawHtmlForm继承自HtmlForm控件

b. 重写RenderAttribute()方法

View Code

publicclass RawHtmlForm : System.Web.UI.HtmlControls.HtmlForm { protectedoverridevoid RenderAttributes(System.Web.UI.HtmlTextWriter writer) { this.Action = Page.Request.RawUrl; base.RenderAttributes(writer); } }

c. 在配置文件中, 指明用新控件RawHtmlForm替代HtmlForm控件, 这样在生成form标签时, 自动使用新控件代替旧控件.

View Code

<system.web><pages><tagMapping><add tagType=”System.Web.UI.HtmlControls.HtmlForm” mappedTagType=”RawHtmlForm”/></tagMapping></pages></system.web>

第二种: 缓存为Html文件的技术(*.aspx页面的请求结果就是个html页面):

它的原理是客户端请求*.aspx文件, 如果在缓存文件夹中有对应名称的*.html文件, 则将请求地址替换为*.html的文件. 这样就不必走完全部的应用程序处理管道, 响应速度会大大提高; 如果缓存文件夹中没有对应的*.html文件, 则该请求会走完整个应用程序管道, 在应用程序管道中的页面处理部分将*.aspx页面的请求结果返回给客户端同时, 再以流的形式保存(另存)*.aspx请求结果到缓存文件中. 这样做的好处是加快网站的响应速度, 而一个快速响应的网站对提升SEO(Searching Engine Optimization)效果也是有一定帮助的. 这种手法是用对文件的IO操作代替费时的应用程序管道的操作, 对于那些访问量极高的页面, 我们甚至可以把*.aspx页面的请求结果直接保存到内存中, 这样连IO操作也省掉了, 但内存的有限性决定了我们不能够保存太多的页面在内存中, 只能保存那些访问量极高的少量页面.

缓存后替换:

以上我们实现了整页的缓存, 但是缓存完的数据是关于整个页面的, 是没办法修改其内容的. 仍然拿新闻页面来打比方: 如果我想在每个新闻页面中显示下当前日期和时间, 或者我想在每个新闻页面的头部和尾部添加广告, 针对这种大部分内容不变仅有少部分内容发生变化的情况, ASP.NET提供了缓存后替换功能.

缓存后替换功能可以将整个页面进行输出缓存, 但是特定的部分标记为不缓存. Substitution控件指定需要动态创建而不进行缓存的部分, 类似于占位符, 动态生成的内容将输出到Substitution控件所在的位置. ASP.NET为我们提供了3种方式的缓存后替换: a. 以声明的方式使用Substitution控件 b. 以编程的方式使用Substitution控件 c. 使用AdRotator控件.

a. 以声明的方式使用Substitution控件时: Substitution控件调用MethodName属性指定的方法, 该方法提供了在Substitution控件处显示的内容, 并且该方法必须返回字符串类型的结果, 而且必须在Page或UserControl类的代码中包含该控件指定的这个静态方法. MethodName指定的方法并需符合HttpResponseSubstitutionCallback委托的约定(以参数为HttpContext 类型), 简言之就是MethodName指的方法, 必须是static、返回string、参数为HttpContext类型的方法.

b. 以编程的方式使用Substitution控件时: 可以将当前页或用户控件后台代码中的静态方法 或 任何对象上的静态方法的方法名, 传递给HttpResponse.WriteSubstitution()方法. 第一次请求该页的时候, WriteSubstitution方法调用HttpResponseSubstitutionCallback委托来产生输出, 之后将替换缓存区中的内容. 该方法会将客户端的缓存能力从public将为server, 从而使浏览器不在缓存而保证能够重新生成静态内容.

c. AdRotator服务器控件: 该服务器控件是在其内部支持缓存后替代功能. 如果将AdRotator控件放在页面上, 则每次请求时都会生成动态的广告. 因此, 包含AdRotator控件的页面仅能在服务器端缓存, 可以通过AdRotator的AdCreated事件对广告内容作高级的处理. 通过AdRotator的AdvertisementFile属性指定储存广告数据的文件, 通常是xml文件. 配置广告信息的xml的格式, 见示例文件XmlAdData.xml.

AdRotator的几个属性释义如下:

ImageUrl: 广告图片地址

NavigateUrl: 点击广告图片的链接地址

AlternateText: 鼠标放在图片上的提示文字

Keyword: 该广告的关键词

Impression: 权重, 广告的显示频率.

示例: 对于新闻类型的网站, 站内的大量文章内容发生改变的频率不是很高, 因此适合将这些文章内容缓存为静态的html页面, 以减少文章内容页重复生成过程的开销.

//CacheHtmlPageDemo/App_code/ CacheHtmlModule.cs    —   若在CacheFile文件夹中, 存在缓存的*.html则返回客户端

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for CacheHtmlModule ///</summary>publicclass CacheHtmlModule : IHttpModule { public CacheHtmlModule() { //// TODO: Add constructor logic here // }
#region IHttpModule Members
publicvoid Dispose() { thrownew NotImplementedException(); }
publicvoid Init(HttpApplication application) { application.BeginRequest +=new EventHandler(application_BeginRequest); }
void application_BeginRequest(object sender, EventArgs e) { HttpApplication ha = sender as HttpApplication; HttpContext hc = ha.Context;
//相对于网站根节点~/的资源地址, 如: ~/default.aspxif (hc.Request.AppRelativeCurrentExecutionFilePath.ToLower().EndsWith(.aspx)) { //处理开头和结尾的”~/”、”.aspx”、深层文件夹的”/”, 并使用context.Request.Url.Query获得Url中的请求参数信息后处理请求参数string filepath =~/CacheFile/+ hc.Request.AppRelativeCurrentExecutionFilePath.ToLower().Replace(.aspx, “”).Replace(~/, “”).Replace(/, _) + hc.Request.Url.Query.Replace(?, _).Replace(&, _) +.html; if (System.IO.File.Exists(hc.Server.MapPath(filepath))) { //在缓存文件夹中能够找到静态的html文件hc.RewritePath(filepath, false); } } }
#endregion }
复制代码

//CacheHtmlPageDemo/App_code/HtmlPageBase.cs — 若不存在缓存的*.html, 则走页面处理管道并将生成的页面保存到CacheFile文件夹中, 我们所要做的只是将页面后台的.cs文件中的类继承成自HtmlPageBase类而非原先的Page类.

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for HtmlPageBase ///</summary>publicclass HtmlPageBase : System.Web.UI.Page { protectedoverridevoid Render(System.Web.UI.HtmlTextWriter writer) { //生成的页面内容不但要保存到文件, 还要返回给浏览器. 所以不能直接用streamwriterSystem.IO.StringWriter sw =new System.IO.StringWriter(); System.Web.UI.HtmlTextWriter htw =new System.Web.UI.HtmlTextWriter(sw);
//调用基类的render方法, 将结果输出到StringWriter中base.Render(htw);
htw.Flush(); htw.Close();
//获得页面的内容string pagecontent = sw.ToString(); sw.Close(); string filepath =this.Server.MapPath(~/CacheFile/) +this.Request.AppRelativeCurrentExecutionFilePath.ToLower().Replace(.aspx, “”).Replace(~/, “”).Replace(/, _) +this.Request.Url.Query.Replace(?, _).Replace(&, _) +.html; //判断CacheFile文件夹是否存在string cachefolder = System.IO.Path.GetDirectoryName(filepath); if (System.IO.Directory.Exists(cachefolder)) { System.IO.Directory.CreateDirectory(cachefolder); }
//保存页面内容到静态文件中System.IO.StreamWriter swriter =new System.IO.StreamWriter(filepath); swriter.Write(pagecontent); swriter.Close();
//将页面内容输出到浏览器中Response.Write(pagecontent); }
#region 不在IHttpModule中做任何处理, 而是在页面的Page_PreInit事件中查看缓存文件是否存在(理论上比IHttpModule要慢)//protected override void OnPreInit(EventArgs e) //{ // string filepath = “~/CacheFile/” + this.Request.AppRelativeCurrentExecutionFilePath.ToLower().Replace(“.aspx”, “”).Replace(“~/”, “”).Replace(“/”, “_”) + this.Request.Url.Query.Replace(“?”, “_”).Replace(“&”, “_”) + “.html”; // if (System.IO.File.Exists(filepath)) // { ////在CacheFile中能够找到缓存的静态页面 // this.Server.Transfer(filepath); // } // base.OnPreInit(e); //}#endregion }
复制代码

//CacheHtmlPageDemo/Default.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=Default.aspx.cs Inherits=_Default%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><p><a href=”News1.aspx”>新闻1</a></p><br /><p><a href=”News2.aspx”>新闻2</a></p><br /><p><a href=”News3.aspx”>新闻3</a></p><br /></body></html>

//CacheHtmlPageDemo/News1.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=News1.aspx.cs Inherits=NewOne%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><h1>This is the 1st page of News Channel.</h1></body></html>

//CacheHtmlPageDemo/News3.aspx , 缓存后替换示例, 该页面添加了@OutputCache页面指令, 且后台代码继承自默认的Page类.

View Code

复制代码
<%@ Page Language=C# AutoEventWireup=true CodeFile=News3.aspx.cs Inherits=News3%>
<%@ OutputCache Duration=10 VaryByParam=None%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><asp:Label id=”time” runat=”server” text=”用于对比的Label显示的时间:”></asp:Label><hr /><h1>This is the 3rd page of News Channel.</h1><hr /><asp:substitution id=”substitution1″ runat=”server” MethodName=”GetTimeNow”></asp:substitution><br /><hr /><asp:AdRotator ID=”AdRotator1″ AdvertisementFile=”~/Data/XmlAdData.xml.xml” runat=”server”/></body></html>
复制代码

//CacheHtmlPageDemo/App_Data/XmlAdData.xml, 为简便而使用xml, 若用数据库也可以, 将XML节点对应表的字段(随便两张图片就行).

View Code

<?xml version=”1.0″ encoding=”utf-8″ ?><Advertisements><Ad><AlternateText>美女1</AlternateText><ImageUrl>~/Data/ad1.jpg</ImageUrl><NavigateUrl>http://www.baidu.com</NavigateUrl><Kayword>1</Kayword><Impressions>50</Impressions></Ad>
<Ad><AlternateText>美女2</AlternateText><ImageUrl>~/Data/ad2.jpg</ImageUrl><NavigateUrl>http://www.baidu.com</NavigateUrl><Kayword>1</Kayword><Impressions>50</Impressions></Ad></Advertisements>

// CacheHtmlPageDemo/web.config

View Code

复制代码
<?xml version=”1.0″?><!– Note: As an alternative to hand editing this file you can use the web admin tool to configure settings for your application. Use the Website->Asp.Net Configuration option in Visual Studio. A full list of settings and comments can be found in machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
–><configuration><configSections><sectionGroup name=”system.web.extensions” type=”System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><sectionGroup name=”scripting” type=”System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”scriptResourceHandler” type=”System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><sectionGroup name=”webServices” type=”System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”jsonSerialization” type=”System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”Everywhere”/><section name=”profileService” type=”System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”authenticationService” type=”System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”roleService” type=”System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/></sectionGroup></sectionGroup></sectionGroup></configSections><appSettings/><connectionStrings/><system.web><!– Set compilation debug=”true” to insert debugging symbols into the compiled page. Because this affects performance, set this value to true only during development. –><compilation debug=”true”><assemblies><add assembly=”System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add assembly=”System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/></assemblies></compilation><!– The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user.
–><authentication mode=”Windows”/><!– The <customErrors> section enables configuration of what to do if/when an unhandled error occurs during the execution of a request. Specifically,
it enables developers to configure html error pages to be displayed in place of a error stack trace.
<customErrors mode=”RemoteOnly” defaultRedirect=”GenericErrorPage.htm”> <error statusCode=”403″ redirect=”NoAccess.htm” /> <error statusCode=”404″ redirect=”FileNotFound.htm” /> </customErrors>
–><pages><controls><add tagPrefix=”asp” namespace=”System.Web.UI” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add tagPrefix=”asp” namespace=”System.Web.UI.WebControls” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></controls></pages><httpHandlers><remove verb=”*” path=”*.asmx”/><add verb=”*” path=”*.asmx” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”*” path=”*_AppService.axd” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ validate=”false”/></httpHandlers><httpModules><add name=”ScriptModule” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”Cachehtml” type=”CacheHtmlModule”/></httpModules></system.web><system.codedom><compilers><compiler language=”c#;cs;csharp” extension=”.cs” warningLevel=”4″ type=”Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”WarnAsError” value=”false”/></compiler><compiler language=”vb;vbs;visualbasic;vbscript” extension=”.vb” warningLevel=”4″ type=”Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”OptionInfer” value=”true”/><providerOption name=”WarnAsError” value=”false”/></compiler></compilers></system.codedom><!– The system.webServer section is required for running ASP.NET AJAX under Internet Information Services 7.0. It is not necessary for previous version of IIS. –><system.webServer><validation validateIntegratedModeConfiguration=”false”/><modules><remove name=”ScriptModule”/><add name=”ScriptModule” preCondition=”managedHandler” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></modules><handlers><remove name=”WebServiceHandlerFactory-Integrated”/><remove name=”ScriptHandlerFactory”/><remove name=”ScriptHandlerFactoryAppServices”/><remove name=”ScriptResource”/><add name=”ScriptHandlerFactory” verb=”*” path=”*.asmx” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptHandlerFactoryAppServices” verb=”*” path=”*_AppService.axd” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptResource” preCondition=”integratedMode” verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></handlers></system.webServer><runtime><assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″><dependentAssembly><assemblyIdentity name=”System.Web.Extensions” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly><dependentAssembly><assemblyIdentity name=”System.Web.Extensions.Design” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly></assemblyBinding></runtime></configuration>
复制代码

使用缓存时, 关于缓存内容的考量:

a. 没有必要缓存用户相关页面, 这会存储很多低点击率的页面

b. 缓存那些频繁请求, 但是内容更新不太频繁或者提供过期内容也影响不大的页面.

c. 生成操作很昂贵的页面, 如: 电子商务网站中的, 分类显示和报表显示页面的生成操作.

d. 内存空间有限, 所以只能缓存那些占用空间小的内容.

e. 对于Postback的响应不应该被缓存, 因为他们依赖于post请求参数.

5. 在页面处理管道或通过IHttpHandler在处理程序中做数据缓存.

说起数据缓存, 肯定跟网站应用程序的数据来源有关了, 需要被缓存的那部分数据当然是放在Cache中. 那么数据本身要么是存放在文件中, 要么是存放在数据库中, 因此对应于这两种数据载体, .net提供了2种依赖机制: 一种是基于文件和目录的CacheDependency, 一种是基于数据库的SQLDependency, SQLDependency是数据库和程序之间的依赖关系, 此外微软还提供了继承自CacheDependency的SQLCacheDependency类, 用来建立数据库和程序缓存之间的依赖关系. 这种缓存依赖解决个什么问题呢, 数据不是已经缓存了吗? 没错, 我们是缓存了数据, 但是如果原始数据发生变化怎么办? 缓存依赖就是当数据发生变化时, 自动清除缓存中数据的这么一种机制, 接下来的下一次请求必然要从新执行读取数据的操作.

缓存依赖执行的操作是删除缓存内容, 这样下一次请求势必会从新获取数据. 这与”缓存后替换”功能是不一样的, 缓存后替换功能是用读取的新数据替换缓存页面的一部分内容.

第一种: 基于文件和目录的CacheDependency是比较简单的, 留意缓存依赖的过期时间、缓存项的优先级、以及删除通知(当被缓存的数据从缓存中移除时激发的事件)即可.

值得注意的地方: 删除通知的回调方法(委托)的回调时机是不可预知的, 很有可能在回调的时候, 页面的生成过程已经完成, 这时候根本没有HttpContext对象, 所以对缓存的操作只能通过HttpRuntime的Cache属性来获得. 而且, 不能在页面上使用实例方法来作为回调方法, 因为回调方法会阻止页面对象的垃圾回收, 内存资源将很快消耗光.

//DataCacheDemo/App_Code/ FileCacheManager.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for FileCacheManager ///</summary>publicclass FileCacheManager { public FileCacheManager() { //// TODO: Add constructor logic here // }
publicstaticstring GetDataFromFile() { string msg =string.Empty; string datapath = HttpContext.Current.Server.MapPath(~/App_Data/TextFileData.txt);
//1. 从缓存中读取数据 //msg = HttpContext.Current.Cache[“message”] as string;msg = HttpRuntime.Cache[message] asstring;
if (msg ==null) { //2. 创建缓存依赖对象System.Web.Caching.CacheDependency cd =new System.Web.Caching.CacheDependency(datapath);
//从文本文件中读取数据using (System.IO.StreamReader sr =new System.IO.StreamReader(datapath)) { msg = sr.ReadToEnd(); } HttpRuntime.Cache.Add(message, msg, cd, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 1, 0), System.Web.Caching.CacheItemPriority.Normal, new System.Web.Caching.CacheItemRemovedCallback(CallbackMethod)); }
return msg; }
//当被缓存的数据从缓存中移除时, 激发的事件. 缓存依赖的回调方法必须是静态方法, 实例方法会阻止垃圾回收.privatestaticvoid CallbackMethod(string key, object value, System.Web.Caching.CacheItemRemovedReason reason) { //回调方法执行时, 可能根本不存在HttpContext对象, 这时应该是用HttpRuntime对象获得缓存的引用} }
复制代码

//DataCacheDemo/App_Data/TextFileData.txt, 文字内容随意

//DataCacheDemo/CacheDependency.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=CacheDependency.aspx.cs Inherits=CacheDependency%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><span>基于文件和目录的缓存依赖: </span><hr /><asp:Label ID=”lbl_msg” runat=”server” Text=”Label”></asp:Label></body></html>

//DataCacheDemo/CacheDependency.aspx.cs

View Code

using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls;
publicpartialclass CacheDependency : System.Web.UI.Page { protectedvoid Page_Load(object sender, EventArgs e) { this.lbl_msg.Text = FileCacheManager.GetDataFromFile(); } }

第二种: 基于Sql的缓存依赖有两种实现: a. 基于轮询的实现 和 b. 基于通知的实现(仅支持Ms Sql 2005 以上的版本).

  1. 基于轮询的实现: 就是由 应用程序 每隔一定时间去访问数据库, 查看数据是否发生变化.

注意: 轮询的实际操作, 不可能每次都去查询整个数据库, 轮询机制实际上是通过触发器来维护一张监控表来实现的.

具体操作如下:

创建一张监控信息表, 表的字段主要有两个(被监控的表名, int型字段用来表示数据是否发生变化).

在被监控表上建立触发器, 当表的内容发生变化时, 修改监控表的int字段, 可以通过表的Insert、Delete、Update触发器实现.

对于除Sql Server以外的数据库, 如Oracle、MySql等可采用类似的办法建立轮询机制或通知机制(当然Sql Server也可以这么做). 而Sql Server数据库已经由微软提供了一套轮询实现机制.

(1) . 工具位置: c:\Windows\Microsoft.Net\Framework\v2.0.50727\aspnet_regsql.exe

该工具的参数信息如下(区分大小写):

-S 数据库服务器的名称或ip地址

-E 使用集成验证方式登录数据库

-U 用户名

-P 密码

-d 数据库名称, 如不提供则使用aspnetdb数据库

-ed 为数据库打开Sql缓存依赖支持

-dd 关闭数据库的Sql缓存依赖支持

-et 指定Sql缓存依赖使用的表, 需要使用-t指定表名

-dt 禁用Sql缓存依赖使用的表, 需要使用-t指定表名

-t 指定表名

-lt 列出启用缓存依赖的表名

示例:

启用数据库缓存依赖: aspnet_regsql –S .\MSSQLServer –E –d northwind –ed

启动对被监控表的监控: aspnet_regsql –S .\MSSQLServer –E –d northwind –t calendar –et

列出启用了缓存依赖的表: aspnet_regsql –S .\MSSQLServer –E –d northwind –lt

(2). 运用aspnet_regsql工具配置完数据库后, 还需要在配置文件中增加轮询的设置. sqlCacheDependency enable属性为”true”表示开启抡起机制, polltime设置以毫秒为单位的轮询间隔(>= 500ms, 默认为1分钟).

View Code

<system.web><caching><sqlCacheDependency enabled=”true”><databases><add name=”northwindCache” connectionStringName=”MsSql” pollTime=”60000″/></databases></sqlCacheDependency></caching></system.web>

SqlCacheDependency表示Sql缓存依赖对象, 即支持轮询机制也支持通知机制. SqlCacheDependency scd = new SqlCacheDependency(“配置文件中的数据库那一项的名称, 如northwindCache”, “被监控的表名”);

当构造缓存依赖对象时, 可以通过AggregateCacheDependency创建多个依赖的组合, 在该组合中任一依赖发生变化时将使缓存失效. 组合的缓存依赖内部为一个集合, 例如: 缓存的数据是依赖于Customers和Orders两张表, 我们可以建立2个缓存依赖对象, 并将其添加到一个AggregateCacheDependency对象中, 这时只要任何缓存依赖发生变化, 缓存数据将会失效.

//涉及Sql的相关操作, MsSqlCommand.txt

View Code

复制代码
###################MS SQL Server 轮询机制########################
ifexists(select*from sysobjects where[name]=SP_GetAllCustomersWithOrdersand[type]=P) dropprocedure SP_GetAllCustomersWithOrders gocreateprocedure SP_GetAllCustomersWithOrders asbeginselect*from customers c innerjoin orders o on c.customerid = o.customerid endgo
调用exec SP_GetAllCustomersWithOrders
插入一条数据insertinto Customers( CustomerID, CompanyName, ContactName, ContactTitle, Address, City, PostalCode, Country, Phone) values(ZHANG,CAIT Beijing,Jalen Zhang,Owner,China Beijing,Beijing,100088,China,82240389) go
deletefrom Customers where CustomerID =ZHANGgo

###################MS SQL Server 通知机制######################## 产看是否开启通知机制select databasepropertyex(MSPetShop4,IsBrokerEnabled)
开启Sql Server 2005数据库通知机制alterdatabase MSPetShop4 set enable_broker go
通知机制只支持最简单的Select语句SELECT ProductId, CategoryId, Name, Descn, ImageFROM dbo.Product
update product set Descn =HelloWorldwhere productid=BD-01and categoryid=BIRDS

复制代码

 

//DataCacheDemo/App_Code/IDALFactory.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for IDALFactory ///</summary>publicinterface IDAL { void ExecuteNonQuery(string sqlstr, System.Data.CommandType comtype, paramsobject[] param); System.Data.DataSet ExecuteDataSet(string sqlstr, System.Data.CommandType comtype, paramsobject[] param); object ExecuteReader(string sqlstr, System.Data.CommandType comtype, paramsobject[] param); }
publicclass IDALFactory { publicstatic IDAL GetDAL() { string dbtype = System.Configuration.ConfigurationManager.AppSettings[DBType]; switch (dbtype) {
caseMsSql: returnnew SqlDAL(); caseOracle: returnnew OracleDAL(); default: returnnull; } }
}
复制代码

//DataCacheDemo/App_Code/SqlDAL.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for SqlDAL ///</summary>publicclass SqlDAL : IDAL { readonlystring constr = System.Configuration.ConfigurationManager.ConnectionStrings[MsSql].ConnectionString; System.Data.SqlClient.SqlConnection connection; System.Data.SqlClient.SqlCommand command;
public SqlDAL() { connection =new System.Data.SqlClient.SqlConnection(constr); command =new System.Data.SqlClient.SqlCommand(); command.Connection = connection; }
#region IDALFactory Members
publicvoid ExecuteNonQuery(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { command.CommandText = sqlstr; command.CommandType = comtype; if (comtype == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as System.Data.SqlClient.SqlParameter); } try { connection.Open(); command.ExecuteNonQuery(); } catch (System.Data.SqlClient.SqlException se) { } finally
{
if (connection.State == System.Data.ConnectionState.Open) { connection.Close(); } } } }
public System.Data.DataSet ExecuteDataSet(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { System.Data.DataSet ds =new System.Data.DataSet(); command.CommandText = sqlstr; command.CommandType = comtype; if (comtype == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as System.Data.SqlClient.SqlParameter); } try { //DataSet是无连接的System.Data.SqlClient.SqlDataAdapter sda =new System.Data.SqlClient.SqlDataAdapter(command); sda.Fill(ds); } catch (System.Data.SqlClient.SqlException se) { } } return ds; }
publicobject ExecuteReader(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { command.CommandText = sqlstr; command.CommandType = comtype; System.Data.SqlClient.SqlDataReader sdr =null; if (comtype == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as System.Data.SqlClient.SqlParameter); } try { connection.Open(); sdr = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection); } catch (System.Data.SqlClient.SqlException se) { } } return sdr; }
#endregion }
复制代码

//DataCacheDemo/App_Code/OracleDAL.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web;
///<summary>/// Summary description for OracleDAL ///</summary>publicclass OracleDAL : IDAL { readonlystring constr = System.Configuration.ConfigurationManager.ConnectionStrings[Oracle].ConnectionString; Oracle.DataAccess.Client.OracleConnection connection; Oracle.DataAccess.Client.OracleCommand command; public OracleDAL() { connection =new Oracle.DataAccess.Client.OracleConnection(constr); command =new Oracle.DataAccess.Client.OracleCommand(); command.Connection = connection; }
#region IDALFactory Members
publicvoid ExecuteNonQuery(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { command.CommandText = sqlstr; command.CommandType = comtype; if (comtype == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as Oracle.DataAccess.Client.OracleParameter); } try { connection.Open(); command.ExecuteNonQuery(); } catch(Oracle.DataAccess.Client.OracleException oex) { } finally { if (connection.State == System.Data.ConnectionState.Open) { connection.Close(); } } } }
public System.Data.DataSet ExecuteDataSet(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { command.CommandText = sqlstr; command.CommandType = comtype; if (command.CommandType == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as Oracle.DataAccess.Client.OracleParameter); } } System.Data.DataSet ds =new System.Data.DataSet(); Oracle.DataAccess.Client.OracleDataAdapter oda =new Oracle.DataAccess.Client.OracleDataAdapter(command);
//DataSet是无连接的try { oda.Fill(ds); } catch (Oracle.DataAccess.Client.OracleException oe) { } return ds; }
publicobject ExecuteReader(string sqlstr, System.Data.CommandType comtype, paramsobject[] param) { Oracle.DataAccess.Client.OracleDataReader odr =null; command.CommandText = sqlstr; command.CommandType = comtype; if (comtype == System.Data.CommandType.StoredProcedure) { for (int i =0; i < param.Length; i++) { command.Parameters.Add(param[i] as Oracle.DataAccess.Client.OracleParameter); } } try { connection.Open(); odr = command.ExecuteReader(System.Data.CommandBehavior.CloseConnection); } catch(Oracle.DataAccess.Client.OracleException oe) { } return odr; }
#endregion }
复制代码

//DataCacheDemo/App_Code/pollingSqlDependency.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=pollingSqlDependency.aspx.cs Inherits=pollingSqlDependency%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><span>基于通知机制的SQL缓存依赖: </span><br /><h3>MS SQL Server 轮询机制</h3><br /><hr /><form id=”form1″ runat=”server”><asp:GridView ID=”grd_customerandorder” runat=”server”></asp:GridView></form></body></html>

//DataCacheDemo/App_Code/pollingSqlDependency.aspx.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls;
publicpartialclass pollingSqlDependency : System.Web.UI.Page { protectedvoid Page_Load(object sender, EventArgs e) { BindData(); }
protectedvoid BindData() { //1. 从缓存中读取数据System.Data.DataTable dt = HttpContext.Current.Cache[CustomerWithOrders] as System.Data.DataTable; if (dt ==null) { IDAL dal = IDALFactory.GetDAL(); dt = dal.ExecuteDataSet(SP_GetAllCustomersWithOrders, System.Data.CommandType.StoredProcedure).Tables[0];
//2. 创建缓存依赖对象System.Web.Caching.SqlCacheDependency scd =new System.Web.Caching.SqlCacheDependency(CustomerCache, Customers);
System.Web.Caching.SqlCacheDependency scd2
=new System.Web.Caching.SqlCacheDependency(CustomerCache,Orders);
System.Web.Caching.AggregateCacheDependency acd
=new System.Web.Caching.AggregateCacheDependency(); acd.Add(scd,scd2);
//3. 保存数据到缓存 //HttpContext.Current.Cache.Add(“CustomerWithOrders”, dt, scd, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0), System.Web.Caching.CacheItemPriority.Normal, new System.Web.Caching.CacheItemRemovedCallback(CallbackMethod));HttpContext.Current.Cache.Add(CustomerWithOrders, dt, acd, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0), System.Web.Caching.CacheItemPriority.Normal, new System.Web.Caching.CacheItemRemovedCallback(CallbackMethod)); } this.grd_customerandorder.DataSource = dt; this.grd_customerandorder.DataBind(); }
//当被缓存的数据从缓存中移除时, 激发的事件. 缓存依赖的回调方法必须是静态方法, 实例方法会阻止垃圾回收.privatestaticvoid CallbackMethod(string key, object value, System.Web.Caching.CacheItemRemovedReason reason) { //回调方法执行时, 可能根本不存在HttpContext对象, 这时应该是用HttpRuntime对象获得缓存的引用} }
复制代码

//DataCacheDemo/App_Code/web.config

View Code

复制代码
<?xml version=”1.0″?><!– Note: As an alternative to hand editing this file you can use the web admin tool to configure settings for your application. Use the Website->Asp.Net Configuration option in Visual Studio. A full list of settings and comments can be found in machine.config.comments usually located in
\Windows\Microsoft.Net\Framework\v2.x\Config
–><configuration><configSections><sectionGroup name=”system.web.extensions” type=”System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><sectionGroup name=”scripting” type=”System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”scriptResourceHandler” type=”System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><sectionGroup name=”webServices” type=”System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″><section name=”jsonSerialization” type=”System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”Everywhere”/><section name=”profileService” type=”System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”authenticationService” type=”System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/><section name=”roleService” type=”System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ requirePermission=”false” allowDefinition=”MachineToApplication”/></sectionGroup></sectionGroup></sectionGroup></configSections><appSettings><add key=”DBType” value=”MsSql”/></appSettings><connectionStrings><add name=”MsSql” connectionString=”data source=.;database=northwind;user id=sa;password=sa”/><add name=”Oracle” connectionString=”data source=orcl;user id=hr;password=hr”/><!–使用Northwind演示Northwind不成功, 估计数据库可能有问题–><add name=”MsSqlNotification” connectionString=”data source=.;database=MSPetShop4;user id=sa;password=sa”/></connectionStrings><system.web><caching><sqlCacheDependency enabled=”true”><databases><add name=”CustomerCache” connectionStringName=”MsSql” pollTime=”60000″/></databases></sqlCacheDependency></caching><!– Set compilation debug=”true” to insert debugging symbols into the compiled page. Because this affects performance, set this value to true only during development. –><compilation debug=”true”><assemblies><add assembly=”System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add assembly=”System.Data.DataSetExtensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089″/><add assembly=”Oracle.DataAccess, Version=10.2.0.100, Culture=neutral, PublicKeyToken=89B483F429C47342″/></assemblies></compilation><!– The <authentication> section enables configuration of the security authentication mode used by ASP.NET to identify an incoming user.
–><authentication mode=”Windows”/><!– The <customErrors> section enables configuration of what to do if/when an unhandled error occurs during the execution of a request. Specifically,
it enables developers to configure html error pages to be displayed in place of a error stack trace.
<customErrors mode=”RemoteOnly” defaultRedirect=”GenericErrorPage.htm”> <error statusCode=”403″ redirect=”NoAccess.htm” /> <error statusCode=”404″ redirect=”FileNotFound.htm” /> </customErrors>
–><pages><controls><add tagPrefix=”asp” namespace=”System.Web.UI” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add tagPrefix=”asp” namespace=”System.Web.UI.WebControls” assembly=”System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></controls></pages><httpHandlers><remove verb=”*” path=”*.asmx”/><add verb=”*” path=”*.asmx” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”*” path=”*_AppService.axd” validate=”false” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″ validate=”false”/></httpHandlers><httpModules><add name=”ScriptModule” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></httpModules></system.web><system.codedom><compilers><compiler language=”c#;cs;csharp” extension=”.cs” warningLevel=”4″ type=”Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”WarnAsError” value=”false”/></compiler><compiler language=”vb;vbs;visualbasic;vbscript” extension=”.vb” warningLevel=”4″ type=”Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″><providerOption name=”CompilerVersion” value=”v3.5″/><providerOption name=”OptionInfer” value=”true”/><providerOption name=”WarnAsError” value=”false”/></compiler></compilers></system.codedom><!– The system.webServer section is required for running ASP.NET AJAX under Internet Information Services 7.0. It is not necessary for previous version of IIS. –><system.webServer><validation validateIntegratedModeConfiguration=”false”/><modules><remove name=”ScriptModule”/><add name=”ScriptModule” preCondition=”managedHandler” type=”System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></modules><handlers><remove name=”WebServiceHandlerFactory-Integrated”/><remove name=”ScriptHandlerFactory”/><remove name=”ScriptHandlerFactoryAppServices”/><remove name=”ScriptResource”/><add name=”ScriptHandlerFactory” verb=”*” path=”*.asmx” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptHandlerFactoryAppServices” verb=”*” path=”*_AppService.axd” preCondition=”integratedMode” type=”System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/><add name=”ScriptResource” preCondition=”integratedMode” verb=”GET,HEAD” path=”ScriptResource.axd” type=”System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35″/></handlers></system.webServer><runtime><assemblyBinding xmlns=”urn:schemas-microsoft-com:asm.v1″><dependentAssembly><assemblyIdentity name=”System.Web.Extensions” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly><dependentAssembly><assemblyIdentity name=”System.Web.Extensions.Design” publicKeyToken=”31bf3856ad364e35″/><bindingRedirect oldVersion=”1.0.0.0-1.1.0.0″ newVersion=”3.5.0.0″/></dependentAssembly></assemblyBinding></runtime></configuration>
复制代码

基于Oracle实现轮询操作, 关键点就是对监控表的操作及触发器的编写. 这部分代码我没有写, 但它和Oracle通知机制的缓存依赖很像(见基于通知的缓存依赖Oracle示例). 我说个思路: 首先仍是创建监控表, 之后在被监控表上创建触发器, 对该表做的Insert、Update、Delete操作都会修改监控表(在我的基于通知的缓存依赖Oracle示例中, 我制作了个函数完成添加监控表记录、添加被监控表触发器、以及创建本地文件的操作). 最后, 我们可以再Galobal.asax的Application_Start事件中开辟子线程, 在子线程中读取配置文件中的事件设置, 定期去读取监控表的数据.

再具体点就是, 我们应该事先缓存监控表, 在子线程读取监控表数据后, 比较两个DataTable的内容, 找出发生变化的表的名字. 然后利用索引器查看缓存中是否有以这个表名作key的缓存项, 如果有的话清空这个缓存项, 没有则什么也不做. 大体思路是这样, 代码可以参考Orale的通知机制的实现, 这篇已经写了很久了, 不想再写了.

2. 基于通知的实现: 当数据发生变化时, 由数据库直接通知应用程序. 虽然, 通知机制不需要使用工具做数据库准备工作, 也不需要在配置文件中做任何修改, 但是通知机制只能应用在Ms Sql Server 2005以上的版本中.

具体操作:

使用命令启动数据库通知机制(默认是开启状态, 请务必先检查): alter database 数据库名称 set enable_broker

在Global.asax文件中的Application_Start和Application_End事件中分别设置开启和结束SqlDependency的监听.

通过一个SqlCommand对象来获取数据库查询通知, 该SqlCommand对象必须满足条件, 第一是数据库的表名必须是完全限定名(如: dbo.Region), 第二是Select语句必须显示执行列名, 不能用*、top函数等, 即只能是最简单的Select语句.

构造缓存依赖对象, SqlCacheDependency scd = new SqlCacheDependency(command);

示例:

//涉及Sql的相关操作, OracleCommand.txt.txt

View Code

复制代码
select username form all_users
alteruser hr identified by hr account unlock
exit后, 以hr登陆
select table_name from user_tables
select*from regions
createtable table_monitor( table_name varchar2(50), constraint pk_table_monitor primarykey(table_name), countnumber(8) default(0), create_date date default(sysdate) )
//Triger的写法, 后边用一个函数完成添加监控记录、触发器及创建文件的操作. createorreplacetrigger tg_monitor_REGIONS after insertordeleteorupdateon REGIONS declare file_handle utl_file.file_type; v_count number(8); beginupdate table_monitor setcount=count+1where table_name =REGIONS; selectcountinto v_count from table_monitor where table_name=REGIONS; file_handle := utl_file.fopen(PIC_FILE_DIR,REGIONS.txt,w); utl_file.putf(file_handle,to_char(v_count)); utl_file.fclose(file_handle); end tg_table_monitor;
//用函数添加表名到监事表的同时, 也添加了触发器和创建了记录表信息的文件, 用于在插入数据时, 维护本地的一个文本文件, 配合CacheDependency实现通知机制 //注意: PL/SQL调试, 需要执行grant debug connect Session, createtrigger, createany directory to hr; //注意: 添加PRAGMA AUTONOMOUS_TRANSACTION自治事务是为了在select句子中使用insert、delete、update语句 //注意: 必须确保服务器目录上有orcltmp这个目录 createorreplacefunction fn_addToMonitor( p_tablename varchar2 ) returnvarchar2is PRAGMA AUTONOMOUS_TRANSACTION; v_file varchar2(500); v_sql varchar2(500); begininsertinto table_monitor(table_name) values(p_tablename); v_file :=create or replace directory PID_FILE_DIR as c:\orcltmp”’; execute immediate v_file; v_sql :=create or replace trigger tg_monitor_|| p_tablename || chr(10) || after insert or delete or update on || p_tablename || chr(10) ||declare|| chr(10) || file_handle utl_file.file_type;|| chr(10) || v_count number(8);|| chr(10) ||begin|| chr(10) || update table_monitor set count = count + 1 where table_name = ”’|| p_tablename ||”’;|| chr(10) || select count into v_count from table_monitor where table_name=”’|| p_tablename ||”’;|| chr(10) || file_handle := utl_file.fopen(PIC_FILE_DIR,”’|| p_tablename ||.txt,w);|| chr(10) ||utl_file.putf(file_handle,to_char(v_count));|| chr(10) ||utl_file.fclose(file_handle);||chr(10) ||end tg_table_monitor;; execute immediate v_sql; commit; returnSuccess!; exception when others thenrollback; returnFailure!; end; //执行 select fn_addToMonitor(REGIONS) from dual
//测试时, 对Regions的修改需要使用事务, 否则DML操作在Oracle中不会提交, 导致页面结果不变 declarebegininsertinto regions(region_id,region_name) values(6,WelCome); commit; exception when others thenrollback; end;
复制代码

//DataCacheDemo/Global.asax

View Code

复制代码
<%@ Application Language=C#%>
<script runat=server>
void Application_Start(object sender, EventArgs e) { // Code that runs on application startup //开启SqlDependency的监听System.Data.SqlClient.SqlDependency.Start(System.Configuration.ConfigurationManager.ConnectionStrings[MsSqlNotification].ConnectionString); }
void Application_End(object sender, EventArgs e) { // Code that runs on application shutdownSystem.Data.SqlClient.SqlDependency.Stop(System.Configuration.ConfigurationManager.ConnectionStrings[MsSqlNotification].ConnectionString); }
void Application_Error(object sender, EventArgs e) { // Code that runs when an unhandled error occurs }
void Session_Start(object sender, EventArgs e) { // Code that runs when a new session is started }
void Session_End(object sender, EventArgs e) { // Code that runs when a session ends. // Note: The Session_End event is raised only when the sessionstate mode // is set to InProc in the Web.config file. If session mode is set to StateServer // or SQLServer, the event is not raised. }
</script>
复制代码

//DataCacheDemo/App_Code/noticeSqlDependency.aspx.aspx

View Code

<%@ Page Language=C# AutoEventWireup=true CodeFile=noticeSqlDependency.aspx.cs Inherits=noticeSqlDependency%>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”><head runat=”server”><title></title></head><body><form id=”form1″ runat=”server”><span>基于通知机制的SQL缓存依赖(Ms Sql Server): </span><hr /><asp:GridView ID=”grd_product” runat=”server”></asp:GridView><hr /><span>基于通知机制的SQL缓存依赖(Oracle): </span><hr /><asp:GridView ID=”grd_region” runat=”server”></asp:GridView></form></body></html>

//DataCacheDemo/App_Code/noticeSqlDependency.aspx.aspx.cs

View Code

复制代码
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls;
publicpartialclass noticeSqlDependency : System.Web.UI.Page { protectedvoid Page_Load(object sender, EventArgs e) { GetSqlData(); GetOracleData(); }
protectedvoid GetSqlData() { //1. 从缓存中读取数据System.Data.DataTable dt = HttpContext.Current.Cache[product] as System.Data.DataTable; if (dt ==null) { //2. 创建Sql缓存依赖对象string constr = System.Configuration.ConfigurationManager.ConnectionStrings[MsSqlNotification].ConnectionString; string sqlstr =SELECT ProductId, CategoryId, Name, Descn, Image FROM dbo.Product; //通知机制只支持最简单的Select语句System.Data.SqlClient.SqlConnection connection =new System.Data.SqlClient.SqlConnection(constr); System.Data.SqlClient.SqlCommand command =new System.Data.SqlClient.SqlCommand(sqlstr, connection); command.CommandType = System.Data.CommandType.Text; System.Web.Caching.SqlCacheDependency scd =new System.Web.Caching.SqlCacheDependency(command); //别忘了在Global.asax中开启SqlDependency侦听 System.Data.SqlClient.SqlDataAdapter sda =new System.Data.SqlClient.SqlDataAdapter(command); System.Data.DataSet ds =new System.Data.DataSet();
//3. 从数据库中读取try { sda.Fill(ds); dt = ds.Tables[0]; //4. 保存到缓存中HttpContext.Current.Cache.Add(product, dt, scd, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, new System.Web.Caching.CacheItemRemovedCallback(CallbackMethod)); } catch (System.Data.SqlClient.SqlException se) { } } this.grd_product.DataSource = dt; this.grd_product.DataBind(); }
//当被缓存的数据从缓存中移除时, 激发的事件. 缓存依赖的回调方法必须是静态方法, 实例方法会阻止垃圾回收.privatestaticvoid CallbackMethod(string key, object value, System.Web.Caching.CacheItemRemovedReason reason) { //回调方法执行时, 可能根本不存在HttpContext对象, 这时应该是用HttpRuntime对象获得缓存的引用 }
protectedvoid GetOracleData() { //1. 从缓存读数据System.Data.DataTable dt = HttpContext.Current.Cache[regions] as System.Data.DataTable; if (dt ==null) { //2. 创建缓存依赖对象System.Web.Caching.CacheDependency cd =new System.Web.Caching.CacheDependency(c:\\orcltmp\\REGIONS.txt);
//3. 从数据库读数据string constr = System.Configuration.ConfigurationManager.ConnectionStrings[Oracle].ConnectionString; string sqlstr =select * from regions; Oracle.DataAccess.Client.OracleConnection connection =new Oracle.DataAccess.Client.OracleConnection(constr); Oracle.DataAccess.Client.OracleCommand command =new Oracle.DataAccess.Client.OracleCommand(sqlstr, connection); command.CommandType = System.Data.CommandType.Text;
try { Oracle.DataAccess.Client.OracleDataAdapter oda =new Oracle.DataAccess.Client.OracleDataAdapter(command); System.Data.DataSet ds =new System.Data.DataSet(); oda.Fill(ds); dt = ds.Tables[0]; HttpContext.Current.Cache.Add(regions, dt, cd, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0), System.Web.Caching.CacheItemPriority.Normal, new System.Web.Caching.CacheItemRemovedCallback(OracleCallback)); } catch(Oracle.DataAccess.Client.OracleException oe) {} } this.grd_region.DataSource = dt; this.grd_region.DataBind(); }
protectedstaticvoid OracleCallback(string key, object value, System.Web.Caching.CacheItemRemovedReason reason) { //清空缓存 //HttpContext.Current.Cache.Remove(“regions”); —X, 此时不一定有HttpContext对象HttpRuntime.Cache.Remove(regions); } }
复制代码

差点忘了, 还有网站优化:

通常我们面对一个站时, 可以从以下几个方面考虑优化的问题:

a.Ajax, 使用Ajax技术提升客户体验

b. 浏览器缓存(客户端缓存, 缓存后替换)

c. 应用程序处理管道级别的缓存(整页级缓存, 含IIS位置的整页缓存)

d. 控件级缓存(用户控件中使用缓存技术)

e. 数据集缓存(效果较明显)

f. 代码级的优化

[转载]网站页面静态化方案 - 野男人 - 博客园

mikel阅读(843)

转载网站页面静态化方案 – 野男人 – 博客园.

 

       在 大型网站中,访问者看到的页面基本上是静态页面。为什么都要把页面静态化呢?把页面静态化,好处有很多。例如:访问速度快,更有利于搜索引擎收录等。目前 主流的静态化主要有两种:一种是通过程序将动态页面抓取并保存为静态页面,这样的页面的实际存在于服务器的硬盘中,另外一种是通过WEB服务器的 URL Rewrite的方式,他的原理是通过web服务器内部模块按一定规则将外部的URL请求转化为内部的文件地址,一句话来说就是把外部请求的静态地址转化为实际的动态页面地址,而静态页面实际是不存在的。这两种方法都达到了实现URL静态化的效果,但是也各有各自的特点。

 

将动态页面转化为实际存在的静态页面这种方法,由于静态页面的存在,少了动态解析过程,所以提高了页面的访问速度和稳定性,使得优化效果非常明显。所以这种方法被广泛采用。但是它的局限性同样存在。对于大型网站而言,这种方法将带来不可忽视的问题。

 

一、由于生成的文件数量较多,存储需要考虑文件、文件夹的数量问题和磁盘空间容量的问题;

 

二、页面维护的复杂性和大工作量,及带来的页面维护及时性问题,需要一整套站点更新制度。

 

URL Rewrite方式特点同样鲜明,由于是服务器内部解析的地址,所以内容是实时更新的,也不存在文件管理和硬件问题,维护比较方便。在服务器级URL Rewrite重写技术并不影响页面的执行速度。但是URL Rewrite的门槛比较高,国内虚拟主机大多不支持,而且虚拟主机是目录级的URL Rewrite,通过遍历目录读物URL转发规则的方式将大大降低页面的执行速度。

 

除了抓取动态页面和URL Rewrite的方法外,在这里我们再看一下另外的一种方法。此方法的核心思想就是:把页面划分成子数据块,每个数据块可能是一个inc文件,也可能多个数据块包含在一个inc文件中。具体的数据块划分根据页面的业务结构来处理。比如:网站头尾等公共数据块可以独立成一个文件。这种方法需要考虑以下几个方面:

 

1、用什么方式生成页面及里面的数据块

 

2、页面的更新机制;

 

3、大量的页面文件的维护工作;

 

4、页面数据块的及时性。

 

这种方式的话,通常可以在后台增加一个服务程序,专门生成某个频道或栏目的页面。这样虽然可行,按照频道分的话,逻辑结构也清晰。

 

 



【单服务模式】

 

这样会带来一些问题。例如:当频道修改后,相应的服务程序都要重新翻一遍。如果频道栏目很多,对应的服务程序也会很多,导致程序的维护工作量大。前台开发人员不仅要去做页面,也要考虑后台的服务程序结构,给他们增加了不必要的开发难度,降低了开发效率。

 

 

 

 


【多服务模式】

 

而在多服务模式下,会出现多台服务去争抢指令数据的情况。动作指令的状态必须在多个服务之间同步。服务升级了,也要一个一个去更新,出现错误了也要一个一个去排查。。。。。。

 

那 么有没有一种方法能把生成页面的功能独立抽象成一个平台,同时提供一个程序接口,前台开发人员只需要按照这个接口,开发业务组件即可。现在前台开发人员只 需要把写好的业务组件,部署到指定的地方即可。剩下的事情交给这个平台去做,这样就简化了系统发布,维护工作,减轻了前台开发人员的工作量,提高了他们的 开发效率。

 

 


【平台集中处理模式】

 

       动作指令是指页面更新的动作,当页面数据有变化时,会根据业务规则从某个地方发出一个动作。它的来源大致可以分为三种:前台页面触发,后台内容管理系统触发,后台自动定时触发。

 

静态数据生成系统与业务组件的接口设计。通过反射的方式调用业务组件,接口的参数在指令结构的基础上扩展即可。比如增加一些错误描述,数据库链接对象等。

 

数据分发是一个独立的数据传输系统,它负责根据预先设定好的配置,把生成的页面数据传输到指定的web服务器上。

 

为了使系统在随着网站访问量的上升的同时做到水平扩展,加快指令的处理速度。所以需要把系统部署到多台服务器上,这样以来各个子系统就要统一通信协调。可以用MQ消息作为子系统之间的通信手段。子系统的部署模式变为Master-Slave的形式。Master主机上的系统负责读指令,然后把指令发送到MQ。各个Slave主机系统负责接收MQ消息指令,调用业务组件并更新某条指令的状态,这样就把处理业务逻辑的压力平均的分配到了各台slave主机。

 

 

    对于一个大型网站来说,生成的页面数据会非常多,管理这些页面文件又是一个问题。例如有的页面被删除了,而已经生成的页面数据还会存在各个web服务器上。这时就需要通过后台系统记录这些页面文件的部署位置,以便今后统一管理。同时业务组件的量也可能会比较多,特别是存在多版本的情况下,所以也需要把业务组件的配置情况记录到数据库中,便于统一管理。

[转载]OAuth2.0 - Snow〃冰激凌 - 博客园

mikel阅读(1128)

[转载]OAuth2.0 – Snow〃冰激凌 – 博客园.

oAuth认证是当下比较公认与应用较广泛的一种标准。使第三方应用可以安全的访问与 操作其它应用程序。2.0相对于1.0来说,认证过程简化了许多,但不兼容1.0。2.0增加一个refreshToken的概念,每个返回的 accessToken 都有一个有效期,过了有效期,需要refreshToken向服务器重新获得一个新的accessToken。Token相当于一个凭证一样,是该第三方 应用访问其它应用的通行证。(也可以不设refreshToken, 比如说新浪微博就没有)

 

处理过程中,访问的url用Android的WebView来显示,在WebViewClient中 shouldOverrideUrlLoading(WebView view, String url)方法中处理重定向的url,并可以自定义显示自己本地asset中的网页。操作微博都是GET与POST请求。

 

认证过程:

以QQ微博为例:

1.  用一个QQ号在QQ微博网站上注册一个自己的应用,得到两个值:APP_KEY  APP_SECRET。

2.  在自己开发的第三方应用上授权

授权即得到QQ微博给你自己开发的应用一个权力,可以操作QQ微博。2.0目前有两种授权模式,区别在于是否有自己的服务器。

  1. Authorization code grant,适合于有server端的应用授权(此认证方式分为两步,第一步是get请求,第二步是post请求)
  2. Implicit grant,适合于通过客户端访问的应用授权

    Authorization cod模式和Implicit模式仅在授权的时候有所区别,后续调用API的方式是一致的。

1.Authorization code grant

使用场景:
Authorization code授权适用于PC,无线客户端等需要和第三方server进行交互的应用场景。使用Authorization code授权,第三方能够集中处理用户的授权请求和授权结果,适用于有server端的应用。

授权流程:
Authorization code授权模式分为两步,首先获取code,然后用code换取accesstoken。

第一步:请求code

请求方法:
GET

请求地址:

https://open.t.qq.com/cgi-bin/oauth2/authorize?client_id=APP_KEY&response_type=code&redirect_uri=http://www.myurl.com/example

请求参数:

字段 必须 说明
client_id true 申请应用时分配的app_key
redirect_uri true 授权成功后的回调地址,即第三方应用的url
response_type true 授权类型,为code
wap false 主要用于指定手机授权页的版本,无此参数默认显示pc授权页面

wap=1时,跳转到wap1.0的授权页
wap=2时,跳转到wap2.0的授权页
不带本参数时,手机访问默认跳到wap2.0的授权页

state false 用于保持请求和回调的状态,授权请求成功后原样带回给第三方。该参数用于防止csrf攻击(跨站请求伪造攻击),强烈建议第三方带上该参数。参数设置建议为简单随机数+session的方式
forcelogin false 针对pc授权页
forcelogin=true,强制弹出登录授权页面 forcelogin=false,用户已经登录并且已经授权第三方应用,则不再弹出授权页面
默认为forcelogin=true

返回结果:
如果授权成功,授权服务器会将用户的浏览器重定向到redirect_uri,并带上code,openid和openkey等参数,重定向的url如下:

http://www.myurl.com/example?code=CODE&openid=OPENID&openkey=OPENKEY

参数说明:

字段 说明
code 用来换取accesstoken的授权码,有效期为10分钟
openid 用户统一标识,可以唯一标识一个用户
openkey 与openid对应的用户key,是验证openid身份的验证密钥

第二步:请求accesstoken

请求地址:

https://open.t.qq.com/cgi-bin/oauth2/access_token?client_id=APP_KEY&client_secret=APP_SECRET&redirect_uri=http://www.myurl.com/example&grant_type=authorization_code&code=CODE

请求参数:

字段 必须 说明
client_id true 申请应用时分配的app_key
client_secret true 申请应用时分配的app_secret
grant_type true authorization_code
code true 调用authorize时返回的code
redirect_uri true 回调地址,必须和请求code时的redirect_uri一致
state false 用于保持请求和回调的状态,授权请求成功后原样带回给第三方。该参数用于防止csrf攻击(跨站请求伪造攻击),强烈建议第三方带上该参数。参数设置建议为简单随机数+session的方式

返回结果:

access_token=ACCESS_TOKEN&expires_in=60&refresh_token=REFRESH_TOKEN

参数说明:

字段 说明
access_token 访问第三方资源的凭证
expires_in accesstoken过期时间,以返回的时间的准,单位为秒,注意过期时提醒用户重新授权
refresh_token 刷新token

2.Implicit grant

使用场景:
Implicit grant授权适用于没有server端的客户端应用,由客户端发起授权请求,保存和处理accesstoken。

请求地址:

https://open.t.qq.com/cgi-bin/oauth2/authorize?client_id=APP_KEY&response_type=token&redirect_uri=http://www.myurl.com/example

请求参数:

字段 必须 说明
client_id true 申请应用时分配的app_key
redirect_uri true 授权回调地址,必须和应用注册的地址一致
response_type true 授权类型,为token
wap false 主要用于指定手机授权页的版本,无此参数默认显示pc授权页面

wap=1时,跳转到wap1.0的授权页
wap=2时,跳转到wap2.0的授权页
不带本参数时,手机访问默认跳到wap2.0的授权页

state false 用于保持请求和回调的状态,授权请求成功后原样带回给第三方。该参数用于防止csrf攻击(跨站请求伪造攻击),强烈建议第三方带上该参数。参数设置建议为简单随机数+session的方式
forcelogin false 针对pc授权页
forcelogin=true,强制弹出登录授权页面 forcelogin=false,用户已经登录并且已经授权第三方应用,则不再弹出授权页面
默认为forcelogin=true

返回结果:

http://www.myurl.com/example#access_token=ACCESS_TOKEN&expires_in=60&openid=OPENID&openkey=OPENKEY

参数说明:

字段 说明
access_token accesstoken,访问api资源的凭证
expires_in accesstoken过期时间,以系统返回的过期时间为准,注意过期时提醒用户重新授权
openid 用户统一标识,可以唯一标识一个用户
openkey 与openid对应的用户key,是验证openid身份的验证密钥

刷新accesstoken

Oauth2中,accesstoken的有效期不是无限的,当第三方应用使用的acesstoken时间超过了其生命周期时,可以通过刷新机制来获取新的accesstoken。

请求地址

https://open.t.qq.com/cgi-bin/oauth2/access_token?client_id=APP_KEY&grant_type=refresh_token&refresh_token=REFRESH_TOKEN

请求参数

字段 必须 说明
client_id true 申请应用时分配的app_key
grant_type true 固定为“refresh_token”
refresh_token true 上次授权或者刷新时获取的refresh_token

返回结果

返回字符串:

access_token=ACCESS_TOKEN&expires_in=60&refresh_token=REFRESH_TOKEN&name=NAME

参数说明

字段 说明
access_token accesstoken,访问api资源的凭证
expires_in accesstoken过期时间,以系统返回的过期时间为准,注意过期时提醒用户重新授权
refresh_token 刷新token
name 授权用户微博帐号

注:
1)通过刷新机制可以延长access_token有效期,每次刷新延长的access_token有效期与授权时access_tken的有效期一致,多次刷新总的有效期为3个月;
2)使用implicit模式获取refresh_token时,为保证AppKey的安全,不要把app_secret放在客户端。