[转载]SqlServer性能优化——Partition(创建分区)

mikel阅读(1224)

[转载]SqlServer性能优化——Partition(创建分区) – smjack – 博客园.

和压缩(Compression)相比,数据库分区(Partition)的操作更为复杂繁琐。而且与Compression一次操作,终身保持不 同,分区是一项需要长期维护周期变更的操作。

分区的意义在于将大数据从物理上切割为几个相互独立的小部分,从而在查询时只取出其中一个 或几个分区,减少影响的数据;另外对于置于不同文件组的分区,并行查询的性能也要高于对整个表的查询性能。

事实上,在SQL Server 2005中就已经包含了分区功能,甚至在2005之前,还存在一个叫做“Partitioned Views”的功能,能通过将同样结构的表Union在一个View中,实现类似现在分区表的效果。而在SQL Server 2008中,分区功能得到了显著加强,使得我们不仅能够对表和索引做分区,而且允许对分区上锁,而不是之前的全表上锁

指定分 区列

和Compression一样,在SQL Server 2008中也提供了分区的向导界面。在企业管理器中,需要分区的表上右键选择Storage-》Create Partition:

SqlServer性能优化——Partition(创建分区)

这里会列出该表所有的字 段,包括字段类型、长度、精度及小数位数的信息,可以选择其中的任意一一列作为分区列(Patitioning Column),不仅仅是数字或者日期类型,即使是字符串类型的列,也可以按照字母顺序进行分区。而以下类型的列不可用于分区:text、 ntext、image、xml、timestamp、varchar(max)、nvarchar(max)、varbinary(max)、别名、 hierarchyid、空间索引或 CLR 用户定义的数据类型。此外,如果使用计算列作为分区列,则必须将该列设 为持久化列(Persisit)。

在列表下方,提供了两个选项:

  1. 分 配到可用分区表
    这要求在同一数据库下有另一张已分好区的表,同时该表的分区列和当前选中的列的类型完 全一致
    这样的好处是当两张表在查询中有关联时,并且其关联列就是分区列时,使用同样的分区策略会更有效率。
  2. 将非唯一索引和唯一索引的存储空间调整为与索引分区列一致
    这样会将表中的所 有索引也一同分区,实现“对齐”。这是一个重要而麻烦的选项,具体需求请参阅MSDN(已分区索引的特殊指导原则)。
    这样的好处是表和索引的分区一致,一方面查询时利用索引更 为高效,而且在下文提到的移入移出分区也会更为高效。

注意:这里建议使 用聚集索引列作为分区列。一方面索引结构本身就应与查询相关,那么分区列与索引一致会保证查询的最大效率;另一方面,保证索引对齐而且 是聚集索引对齐是保证分区的移入移出操作顺畅的前提,否则可能会出现无法移入移出的情况,而分区的移入移出又是管理大数据的重要策略——滑 动窗口(SlideWindow)策略的基础操作。

分区函数与分区方案

选好分区列后,如果没 有应用“分配到可用分区表”选项,接下来则会进入选择\创建分区函数以及分区方案的 界面。其中分区函数会指定分区边界,而分区方案则规划了每个分区所存储的文件组。

向导操作界面如下:

SqlServer性能优化——Partition(创建分区)

其中Left boundary说明每个分区的边界值被包含在边界值左侧的分区中,也就是每个分区内的数据约束是<=指定的边界值, 相应的,Right boundary则说明每个分区的边界值被包含在边界值右侧的分区中,每个分区内的数据约束是<指定的边界值

在下方的列表中,列出了当前分区方案下现有的分区。其中文件组(Filegroup)指定了每个分区存放的位置,如果将分区放置于位于不同磁盘 中的不同文件组中,由于不同磁盘的读写互不干扰,这将提高分区表并行处理的效率。一般情况下,将所有分区放置在同一个文件组是比较稳妥的做法。关于文件组 的展开阅读可以参阅:SQL Server Filegroups

注意,在这里最后一个分区是 没有指定边界的,用于保存所有>(Left Boundary)或>=(Right boundary)最后一个分区边界的数据。

如果选择时间类型的字段作为分区列,可以通过Set按钮实现按条件分组:

SqlServer性能优化——Partition(创建分区)

这样可以很方便得通过设置起止时间将表按照指 定时间段自动分区,但之后依然需要手动指定每个分区的文件组。

制定好分区方案之后可以通过Estimate sotrage预估每个分区的行数、空间占用情况,不过除非需要以占用空间或行数来规划你的分区策略,一般不建议在这里进行预估, 因为如果对空表来说,预估的结果当然都是0,而如果表中已经包含大量数据,预估则会花费比较长的时间。

创建分区

通 过以上设置,分区已经基本完毕,在向导的最后,可以选择是创建脚本还是立即执行分区操作。

我们可以查看在不同情况下创建分区的脚本的情 况:

1.在表没有索引的情况下:

BEGIN TRANSACTION
CREATE PARTITION FUNCTION [TestFunction](datetime) AS RANGE LEFT FOR VALUES (N'2010-01-01T00:00:00', N'2010-02-01T00:00:00', 
N'2010-03-01T00:00:00', N'2010-04-01T00:00:00', N'2010-05-01T00:00:00', N'2010-06-01T00:00:00')

CREATE PARTITION SCHEME [TestScheme] AS PARTITION [TestFunction] TO ([PRIMARY], [PRIMARY], [PRIMARY], 
[PRIMARY], [PRIMARY], [PRIMARY], [PRIMARY])

CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] 
(
    [birthday]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday])

DROP INDEX [ClusteredIndex_on_TestScheme_634025264502439124] ON [dbo].[Account] WITH ( ONLINE = OFF )
COMMIT TRANSACTION

这里先创建Partition Function以及Partition Scheme,之后在分区列上创建聚集索引并按照分区方案分区,最后删除了这一索引。</>

2.在表有索引的情况下:

如果原先没有聚集索引:

CREATE CLUSTERED INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] 
(
    [birthday]
)WITH (SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF) ON [TestScheme]([birthday])

DROP INDEX [ClusteredIndex_on_TestScheme_634025229911990663] ON [dbo].[Account] WITH ( ONLINE = OFF )

这和没有索引的情况一样,如果表原先存在聚集索引,则脚本变为:

CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])

可以看到原有的聚集索引(IX_id)在分区方案上被重建了。

如果选择了“对齐索引”选项,则会对所有索引都应用分区:

CREATE CLUSTERED INDEX [IX_id] ON [dbo].[Account] 
(
    [id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])

CREATE NONCLUSTERED INDEX [UIX_birthday] ON [dbo].[Account] 
(
    [birthday] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [TestScheme]([birthday])
CREATE NONCLUSTERED INDEX [UIX_name] ON [dbo].[Account] 
(
    [name] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, 
ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON)

这里不仅对聚集索引IX_id进行了分区,也对非聚集索引UIX_name和UIX_birthday进行了分区。

注意事项

  1. 对一张表分好区后不可以进行再次分区,同时也没有直接取消表分区的方法
  2. 如果要查看已分区表的分区状态以及每个分区中的行数和占用空间,可以通过Storage-》Management Compression查看。同时可以在这里为每个分区指定压缩方式。
  3. 如果分区表索引没有对齐,则不可以对该表进行切入切出(Switch in/out)操作,同样也不能执行滑动窗口操作
  4. 分区实际上是在每个分区表都添加了约束,相应的插入操作的性能也会受到影响。
  5. 即使进行了分区,如果查询的条件字段和分区列并没有关联,性能也未必会得到提升。

附:对分区并行查询的说明

由于我在实际操作中主要考虑并行查询方面的效率,所以文章里只是略略带过,但评论中有人提到,所以摘录整理一些资料在下面:

  1. 并行查询肯定需要多核支持,单核下并行是不可能的。
  2. 在2005中,如果有两个以上的Partition,一个线程对应一个Partition,所以如果有10个线程,却只有3个分区的话,就会 有7个线程被浪费。
  3. 在2008中,这一问题被改进,所有的线程都被投入到所有的Partition中。具体可以参看Partitioning enhancements in SQL Server 2008

[转载]SqlServer性能优化——Partition(管理分区)

mikel阅读(1028)

[转载]SqlServer性能优化——Partition(管理分区) – smjack – 博客园.

正如上一篇文章SqlServer 性能优化——Partition(创建分区)中所述,分区并不是一个一劳永逸的操作,对一张表做好分区仅仅是开始,接下来可能需要频繁的变更分 区,管理分区。

在企业管理器中,虽然有“管理分区”的菜单,里面的内容却可能与你的预想不同,这里并没有提供直接对分区进行操作的方 法,所以一些普通的操作,比如“增加分区”、“删除分区”之类的操作就需要通过脚 本实现了。

增加分区(Split Partition)

增加分区”事实上 就是将现有的分区分割开,基于此,在SQL Server中应用的是Split操作。在分离分区的时候,不仅仅要在 Partition Function上指定分割的分界点,同样需要在Partition Scheme上指定新分区应用的文件组:

--指定下一个分区应用文件组PRIMARY
ALTER PARTITION SCHEME [MyPartitionSchema] 
NEXT USED [PRIMARY]
--指定分区分界点为5000
ALTER PARTITION FUNCTION MyPartitionFunction()
SPLIT RANGE (5000)

需要注意的一点是,新增的分区中非聚簇索引的压缩模式会被置为None

删除分区(Merge Partition)

删除分区”同样可以认为是将原来分离的分区合并在一起,所以对应的是Merge操作,而且由于并没有新增的分 区,Partition Scheme并不需要改变:

ALTER PARTITION FUNCTION MyPartitionFunction ()
MERGE RANGE (5000)

切换分区(Switch Partition)

切换分区”可能是一个比看上去会应用的更频繁的操作,它的意义在于将一个分区的数据从一张表切换到另一张表 中。这里定义被切换分区的表为“源表”,被切换到的表为“目标表”,则执行切换操作的前提是:

  1. 源表和目标表拥有同样的表结构,即同样的字段、字段类型,同样的索引结构(聚簇和非聚簇),同样的压缩格式。 但不要求默认值约束一致(Default Constaint),也不要求目标表设置了和源表一样的自增长列。
  2. 源表如果有索引且分区,则其索引必须对齐
  3. 源表中被切换的分区范围必须包含于目标表或者目标表将要被切换到的分区范围。这里有如下几种情况:
    1. 将源表的源分区切换到目标表的目标分区中,则目标分区范围>=源分区;
    2. 将源表的源分区切换到目标表中(目标表未分区),则目标表没有设约束,或约束范围>=源分区;
    3. 将源表切换到目标表中(源表、目标表都未分区),则只要目标表没有设约束就可以了(虽然Switch是分区提出的操作,但一个没有分 区的表同样可以被看做一个大分区,所以可以对没有分区的表进行Switch操作)。
  4. 目标表或目标分区不能含有数据

下面的操作将源表的第二个分区切换到目标表的第二个分区中。

ALTER TABLE [STable] SWITCH PARTITION 2 TO [DTable] PARTITION 2

分区管理操作的性能

分割、合并以及切换分区是元数据上的操作而不是对数据的移动,所以操作的效率要比直接操作数据高很多。

  1. 对于分割分区,操作时间和被分割分区的数据量相关,数据越大则分割花费的时间会越长。
  2. 对于合并分区,如果将两个空的分区合并,自然不会耗什么时间;如果两个分区都有数据,则和分割分区一样,数据越大花费的时间越长;如果两个分 区中有一个没有数据,笔者的经验是如果有大数据量的分区在右(>分界值),则消耗的时间较短,如果有大数据量的分区在左(<分界值),则会消 耗较多的时间。
  3. 对于切换分区,即使是上千万级别的数据,也可以在不到1秒的时间完成分区的切换。所以虽然从表面上看,切 换分区和调用Select或者Insert语句移动数据的结果是一样的,但效率却是不可同日而语的。

查看分区信息

除了利用上文提到的通过“管理压缩”的方式查看某张表的分区信息之外,SQL Server还提供了一张系统表查看数据库中的分区情况:

  1. SYS.PARTITION_SCHEMES,数据库中所有分区方案的信息,包括对应的分区函数的ID。
  2. SYS.PARTITION_FUNCTIONS,数据库中所有分区函数的信息,包括分区数等信息。
  3. SYS.PARTITION_RANGE_VALUES,每个分区范围的信息,可以和SYS.PARTITION_FUNCTIONS联查。

比如可以通过如下的脚本,查出分区函数MyPartitionFunc的第一个分区的右边界:

SELECT value FROM sys.partition_range_values, sys.partition_functions 
WHERE sys.partition_functions.function_id = sys.partition_range_values.function_id 
AND sys.partition_functions.name = 'MyPartitionFunc' AND boundary_id = 1

还可以通过如下脚本,获取分区表中各分区的数据情况(行数,最大值,最小值):

SELECT 
    partition = $PARTITION.MyParitionFunc([ParitionDate])
    ,rows      = COUNT(*)
    ,min    = MIN([ParitionDate])
    ,max    = MAX([ParitionDate])
FROM [MyTable]
GROUP BY $PARTITION.MyParitionFunc([ParitionDate])
ORDER BY PARTITION

具体可以参照MSDN:从已分区表和索引中查询 数据和元数据

[转载]ASP.NET MVC + ADO.NET EF 项目实战(三):引入jQuery

mikel阅读(1024)

[转载]ASP.NET MVC + ADO.NET EF 项目实战(三):引入jQuery – Kanas.Net Blog – 博客园.

jQuery是一个重要的客户端框架,ASP.NET MVC默认的项目模板中就带了这个框架。掌握这个框架对于更好地编写ASP.NET MVC应用是非常重要的。事实上,网上有很多文章讲述如何在ASP.NET MVC项目中使用JQuery。例如以下文章就是讲关于jqGrid的:

Using jQuery Grid With ASP.NET MVC

Using jqGrid with ASP.NET MVC

另外,在CodePlex上就有很多项目帮助你更方便地应用JQuery,例如:

jQuery UI Extensions for ASP.NET MVC
jQuery Grid for ASP.NET MVC

但是,要更加方便地使用JQuery,仅仅知道以上还是不够的。

一:了解ASP.NET MVC的局限性

ASP.NET MVC有几处是我不太喜欢的,但是似乎也没有太好的解决方案。

第一件事情是冗余的Action。在一个Controller中每个Action的地位是平等的。但是很遗憾,一个带Form的View总是需要两 个Action,一个用来GET;另一个用来POST。而我觉得这两个Action是不平等的。用于Post的Action是依赖用于Get的 Action的。我告诉你,这两个Action其实是可以压缩成一个的。另一个完全可以放到System.Web.Mvc中,大家公用,通过传递一个或多 个delegate来处理。也就是说,形式上是一个,实际上仍然是两个。说这个问题的意图是想说明,我们可以把一个Ajax服务通过传递一个 delegate来实现单入口。

第二件事情是Script的Render。我希望任何TagBuilder都可以引用自己的Script,在Html的Head段最后写入。可惜 WebFormViewEngine的Render过程中按次序进行的。这意味着,直到整个页面写入Response你都没有机会去更正对Script的 引用。其实我以前写过关 于ASP.NET MVC中Script的管理,不过实话说非常丑陋。其实我也可以告诉你,这个是可以实现的。在View中可以定义一个特殊的标 签,当遇到这个标签的时候,将已经生成的Html写入缓存,清空HtmlWriter,再Render剩余的部分;最后再Render Script部分,最后交这三者合并,最后写入Response。说这个问题的意图是将客户端复杂化以后给应用带来的困难暴露出来。

任何一件事情,如果单独在服务端完成,这个比较好处理。如果单独在客户端完成,这个也好处理。如果需要客户端和服务端配合,就会弄出一些麻烦来。如 果你要在一个页面中显示一个jqGrid,你一共至少需要做四件事情:

1.在你的View中加入相关的引用,包括JavaScript和CSS;包括jQueryjQuery UI、jqGrid;包括相关的主题。

2.在你的View中适当的地方添加一个table标签,并加上id,用于表格的容器;再添加一个div标签,也加上id,用于分页器的容器。

3.加入相关的脚本,将网格与给定的标签进行绑定。其中需要定义网格的列信息。

4.加入一个数据获取的Action,或者XML格式数据或者JSON格式数据。

对于第1件事情,似乎不算麻烦。如果不是过于挑剔的话甚至没有什么问题。

对于第2件事情和第3件事情,就会有冲突了。可以假设这两件事情是两个人做的,但是他们必须确保他们所使用的id是一样的。一旦不一致,到底算谁的 责任?

对于第4件事情,就更麻烦了。因为那已经是服务端的事情了。不仅获取数据的Url要正确,连Column表和数据表也必须一致。一旦不一致,到底算 谁的责任?

通常对于要求一致的事情,由一个人做比较好。所以,无比聪明的程序员会通过一个 HtmlHelper来根据单一的定义统一生成。这的确是一个好的思路。所以,使用jQuery是需要进行专门包装的。

二:封装jQuery及其插件

如果要一个基于jQuery的Total Solution,还真有比较大的困难。jQuery本身的功能是相当有限的,其丰富的功能要依赖一大堆的插件。几乎每个插件要用在ASP.NET MVC中都需要包装一下。好在jQuery插件有一些约定,所以封装起来相对比较简单。在封装的时候有一点通常容易被忽略的是:作为独立于应用的封 装模块中是可以带自己的Controller的。举个例子来说:如果你封装了一个jQuery UI的主题管理器,主题管理的ModalDialog的Action,包括Get和Post你都是可以包装在一个单独的项目中的。如果你的控制器叫 jQueryController,两个Action分别是ShowThemes和SetTheme,那你可以通过以下代码来实现:

var currTheme = "";
$(document).ready(function() {
var doClick = function() {
$("#themeGallery td img.currentTheme").removeClass();
this.setAttribute("class", "currentTheme");
currTheme = this.getAttribute("title");
}
var doOK = function() {
$.post('/jQuery/SetTheme', { theme: currTheme }, function(data) {
$("#themeDialog").dialog("close");
eval(data);
});
return false;
}
var doCancel = function() {
$("#themeDialog").dialog("close");
}
$("#themeDialog").load("/jQuery/ShowThemes");
$("#ThemeButton").click(function() {
$("#themeGallery td img").click(doClick);
var dialogOpts = {
modal: true,
width: "662px",
height: "420px",
resizable: false,
buttons: { "OK": doOK, "Cancel": doCancel }
};
$("#themeDialog").dialog(dialogOpts);
});
});

这样,在任何需要设置主题的地方放一个<a>元素,id定义为ThemeButton即可。基于同样的原理,我们在封装jqGrid的时候, 可以使用如下定义的委托:

这样,单一的入口就是:

代码

public ActionResult Employees() { var model = new GridModel { Caption = "Employees", Loader = (ctx, page, rows, sidx, sord) => { ...... 实现数据访问 return new GridDataModel { Total = q0.Count(), Page = page, Records = q.Count(), Rows = q.ToArray() }; } }; model.Columns.Add(new ColumnModel { Name = "Id", Width = "80px", Align = ColumnAlign.Right, Caption = "Id", Index = "Id", IsKey = true, Sortable = true, Hidden = true }); ...... 加入其他列 return View(model); }

这样,在View中使用的扩展,生成的JavaScript代码中,Url是由应用无关的模块提供的,以View名称+View中Table的id为标 识,存贮在服务端。获取数据时提交该标识,找出数据加载器,从而实现异步数据加载。

jQuery的插件,除了jqGrid是必须封装的以外,jQuery UI也是必须要封装的。虽然很多人包括我在内,对jQuery UI有诸多不满,主要的不满都是基于功能,项目进度严重拖后于Roadmap上的承诺。另外,其代码质量也不如jQuery。不过,毕竟jQuery UI是jQuery官方的产品,受到很多插件依赖,所以,暂时没有可替代的。

此外,还有以下插件需要封装:

  • 带CheckBox的TreeView;
  • 导航Menu和上下文菜单;
  • 封装Google Maps API的gMap;
  • tinyMCE的jQuery插件;
  • 用于布局的Panel插件;
  • 动态Form插件。

三:如何避免拼凑脚本

在ASP.NET MVC中需要经常拼凑脚本,这一点也非常令人讨厌。讨厌的并不是“拼凑脚本”,而是“经常”。需要“经常”拼凑脚本的原因是服务端的内容是动态输出到客户 端的。如果通过某种固定的机制,智能地将服务端的单一代码自动生成为客户端内容那将是非常令人高兴的事情。换句话说,你在服务端直接写C#代码,然后有一 个专门的工具将这些代码翻译成客户端代码。

我研究过JavaScript#, 发现这条路完全行不通。首先,要么翻译源码、要么翻译编译后的代码,但必须是整个文件地翻译,而不能是某个代码段。其次,JavaScript#必须依赖 一整套程序集,而这些程序集是特有的。

我建立了一个新的项目,叫jQuery#,基于一套专门的库,然后在运行时以较小的代价来实现反编译,并翻译成JavaScript代码。如果仅仅 只是生成jQuery所需要的客户端代码是非常简单的,jQuery API毕竟比较少,每个插件的API也都相当有限。麻烦的是需要插入大量的客户端Event Handler。这才是难点所在。按我的计划,今年6月初会发布jQuery#的第一个版本。 目标是可以这样写View:

代码

[转载]asp.net mvc视图篇(二)

mikel阅读(969)

[转载]asp.net mvc(二) – ASP.NET2.0 – 博客园.

MVC中的ViewData
View在MVC模式中与用户进行最直接的接触,负责数据的呈现。注意:view只是负责数据的呈现,我们要尽量让view中不涉及业务逻辑的处理。既然 View与后台代码是相分离的,但View和Controller是如何联系在一起的呢,答案就是ViewData。

ASP.NET MVC默认使用WebForm来作为view。新建的aspx页面继承自ViewPage,所有的aspx页面都必须继承自ViewPage。我们再看一 下ViewPage的部分代码:

public class ViewPage : Page, IViewDataContainer

我们使用传统的ASP.NET开发时,经常会为了开发的需要,会写一个类似PageBase类,例如会把部分比较通用的方法写入基类。同样在MVC中,我 们也可以这样做。

第一:创建一个ViewPage<T>类:这个类主要是完成一个继承功能,对 MvcContrib.FluentHtml.ModelViewPage,MvcContrib.FluentHtml.ModelViewUserControl 的继承,实现System.Web.Mvc.ViewPage的功能。还有一个非常重要的作用就是把所有的扩展方法都体现在这个类中。类中using GuestBook.Common.HtmlHelpers;可以说明。

using GuestBook.Common.HtmlHelpers;
namespace GuestBook.Web
{
public class ViewPage<T> : MvcContrib.FluentHtml.ModelViewPage<T> where T : class
{
public ViewPage()
{

}
}

public class ViewUserControl<T> : MvcContrib.FluentHtml.ModelViewUserControl<T> where T : class
{
public ViewUserControl()
{

}
}
}

第二:对MVC进行扩展。例如对Html的扩展,我们在做增删改查类似操作时,当用户提 交后一般都会根据系统处理结果显示一段提示文字给用户。

1:创建扩展类:HtmlHelperExtensions,主要包含两个方法,一个是操作成功后的处理方法,另一个则是失败后的处理结果。

public static class HtmlHelperExtensions
{
public static string ErrorBox(this HtmlHelper htmlHelper, ViewDataBase  errorViewData)
{
if (errorViewData.ErrorMessage == null) return string.Empty;

HtmlTextWriter writer = new HtmlTextWriter(new StringWriter());

writer.AddAttribute(class, error);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.Write(errorViewData.ErrorMessage);
writer.RenderEndTag();
return writer.InnerWriter.ToString();
}

public static string MessageBox(this HtmlHelper htmlHelper, ViewDataBase messageViewData)
{
if (messageViewData.Message == null) return string.Empty;

HtmlTextWriter writer = new HtmlTextWriter(new StringWriter());

writer.AddAttribute(class, message);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.Write(messageViewData.Message);
writer.RenderEndTag();
return writer.InnerWriter.ToString();
}
}

2:我们看到上面代码中有一个类:ViewDataBase,这个类是所有ViewData的基类,里面主要包含两个字段:1,一般提示内容;2,当发生 异常时的提示内容。

public class ViewDataBase
{
public string Message { get; set; }
public string ErrorMessage { get; set; }

public ViewDataBase WithErrorMessage(string errorMessage)
{
this.ErrorMessage = errorMessage;
return this;
}
public ViewDataBase WithMessage(string message)
{
this.Message = message;
return this;
}
}

说明:我们在留言所使用的ViewData类需要继承ViewDataBase

public class GuestBookInfo : ViewDataBase
{
public string sTitle
{
get; set; }
public string sContent
{
get; set; }
public int ID
{
get; set; }
}

3:在View中调用扩展方法:

1>:页面引用代码的更改:
默认的:Inherits=”System.Web.Mvc.ViewPage<GuestBook.Common .Models.GuestBookInfo>”
扩展后的:Inherits=”GuestBook.Web.ViewPage< GuestBook.Common.Models.GuestBookInfo>”

2>:页面调用代码。

<% =Html.MessageBox (Model)%>

总结:通过扩展ViewPage和ViewData实现了对Html的扩展, 使得ViewData的功能更加灵活,好用。
注:本文参考Suteki项目。

[转载]asp.net mvc教程基础篇(一) - ASP.NET2.0

mikel阅读(1048)

[转载]asp.net mvc(一) – ASP.NET2.0 – 博客园.

这些天开始学习ASP.NET mvc,用传统的ASP.NET已经快四的年了,刚开始接触asp.net mvc确认感觉有点不适应,主要体现在ASP.NET MVC的实现上。

问题一:要想学习asp.net mvc,我个人觉的最重要的一步是知道mvc路由机制,传统的asp.net程序要想访问一个页面,都是根据页面路径来访问,但MVC并不能直接访问 aspx页面。

问题二:理解MVC三部分的含义和用法。当我们创建一个asp.net mvc应用程序时,系统会默认生成三个文件夹:
1:Controllers,对应MVC中的C,主要是处理所有请求与做出对应的响应;
2:Models,对应MVC中的M,相当时我们平时创建工程中的实体工程,只不过在MVC中它充当了存放数据模型的作用;
3:Views,对应MVC中的V,这里就是存放用户访问的页面文件,但是这个文件不能在浏览器中根据路径访问。
对于系统生成的 ASP.NET MVC项目,我对其做了如下扩展:

扩展点一:系统之所以在web工程中直接创建了三个文件夹,是为了更加直观的体现MVC模式,真正项目中我们需要把它们分开。

扩展点二:MVC中重要的路由处理,默认情况是在Global.asax文件中,我们也可以把这块内容独立出来。

扩展点三:把Controller类和业务逻辑分离,这里可以采用Repository模式。

案例DEMO:创建一个简单的留言簿的项目,数据存储采用SQL,本想用linq to entity,但总觉的这部分还相关不完善,且性能存在问题,故使用传统ado.net实现数据存储。下面是这个项目的分层。

1:GuestBook.Web,页面表示层  ,MVC中的V。

2:GuestBook.MVC.Controller,存放项目所有的Controller,MVC中的C。我们知道Controller有两个作用: 第一,处理请求;第二,做出对应的响应。第二点就是我们平时理解的后台功能实现,例如数据的增删改查等。我们可以把这部分功能与Controller分 离,即所有的业务逻辑都写在业务逻辑层,不直接依赖Controller,我们可以进一步把这些功能点抽象出来,让Controller依赖一个公共的接 口。这个思想我之前的一篇文章有点异曲同工之处:对增删改查用面向对象进行包装

首先:创建一个Repository接口:IRepository.cs,里面包含些常见数据处理 操作方法:这个接口是一个泛型接口,以实现所有实体类的通用性。

public interface IRepository<T>
{
List
<T> FindAllInfo();
T GetInfo(T model);
bool Add(T model);
bool Delete(T model);
bool Edit(T model);
}

然后:实现一条留言的数据处理:

public List<GuestBookInfo> FindAllInfo()
{
string SQL = select * from GuestBook;

List<GuestBookInfo> list = new List<GuestBookInfo>();
using(SQLDataReader dr=SqlHelper .ExecuteReader (conn ,CommandType .Text ,sql ))
{
while (dr.Read())
{
GuestBookInfo model
= new GuestBookInfo();
model.ID
= int.Parse (dr[ID].ToString());
model.sTitle
= dr[sTitle].ToString();
model.sContent
= dr[sContent].ToString();
list.Add(model);
}

}
return list  ;
}
public GuestBookInfo GetInfo(GuestBookInfo model)
{
string sql = select * from GuestBook where ID=+model.ID .ToString ();
using (SqlDataReader dr = SqlHelper.ExecuteReader(conn, CommandType.Text, sql))
{
if (dr.Read())
{
model.ID
= int.Parse(dr[ID].ToString());
model.sTitle
= dr[sTitle].ToString();
model.sContent
= dr[sContent].ToString();

}

}
return model ;
}
public bool Add(GuestBookInfo model)
{
string sql = insert into GuestBook (sTitle,sContent) values (‘ + model.sTitle + ‘,’ + model.sContent + ‘);
int i = SqlHelper.ExecuteNonQuery(conn, CommandType.Text, sql);
if (i > 0)
{
return true; }
return false ;
}
public bool Delete(GuestBookInfo model)
{
string sql = delete GuestBook where ID= + model.ID.ToString();
int i = SqlHelper.ExecuteNonQuery(conn, CommandType.Text, sql);
if (i > 0)
{
return true; }
return false;
}
public bool Edit(GuestBookInfo model)
{
string sql = update GuestBook set sTitle=’ + model.sTitle + ‘,sContent=’ + model.sContent + ‘ where ID= + model.ID.ToString();
int i = SqlHelper.ExecuteNonQuery(conn, CommandType.Text, sql);
if (i > 0)
{
return true; }
return false;
}

其实:Controller依赖IRepository接口。

public class GuestBookController : System.Web.Mvc.Controller
{
IRepository
<GuestBookInfo> inter = new BLL_GuestBook();
public ActionResult Index()
{
var models
= inter.FindAllInfo();
return View(Index, models);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(GuestBookInfo model)
{

inter.Add(model );
return RedirectToAction(Index);
}
public ActionResult Create()
{
GuestBookInfo model
= new GuestBookInfo();
return View(model );
}
public ActionResult Details(int id)
{

GuestBookInfo model=new GuestBookInfo ();
model .ID
=id;
model
=inter.GetInfo (model );
if (string .IsNullOrEmpty (model.sTitle ))
{
return View(NotFound); }
else
{
return View(Details,model );
}
}
public ActionResult Edit(int id)
{
GuestBookInfo model
= new GuestBookInfo();
model.ID
= id;
model
= inter.GetInfo(model);
if (string.IsNullOrEmpty(model.sTitle))
{
return View(NotFound); }
else
{
return View(Edit, model);
}
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
GuestBookInfo model
= new GuestBookInfo();
model.ID
= id;
model
= inter.GetInfo(model);
UpdateModel(model );
inter.Edit(model);
return RedirectToAction(Index);
}
public ActionResult Delete(int id)
{
GuestBookInfo model
= new GuestBookInfo();
model.ID
= id;
model
= inter.GetInfo(model);
if (model == null)
return View(NotFound);
inter.Delete(model);
return RedirectToAction(Index);
}

}

3:GuestBook.Model,MVC中的M。

4:GuestBook.RouteManager,路由管理项目,把路由处理从Global.asax中分离开。我们创建一个新 类:MyMvcAppliation.cs

public class MyMvcAppliation:HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute(
{resource}.axd/{*pathInfo});

routes.MapRoute(
Default, // Route name
{controller}/{action}/{id}, // URL with parameters
new { controller = Home, action = Index, id = “” }, // Parameter defaults
new string[] { GuestBook.MVC.Controller }
);

}

protected void Application_Start()
{
ControllerBuilder.Current.DefaultNamespaces.Add(
GuestBook.MVC.Controller);
RegisterRoutes(RouteTable.Routes);
}
}

5:GuestBook.Data,数据处理工具类,例如SqlHelp等等。

6:GuestBook.DAL,数据处理层。

7:GuestBook.BLL,业务逻辑层。

8:GuestBook.MyInterface,相关接口,本项目中包含Repository模式中的接口类。

这篇文章主要是探讨了MVC项目的分层以及部分扩展,欢迎大家提出更好的想法,在后面的文章中,我会总结点MVC与传统asp.net不同点,这样理解 asp.net mvc就不那么麻烦了。

[原创]ASP.NET MVC实现伪静态

mikel阅读(1265)

最近在研究搜索引擎优化(SEO),使用工具分析了网站的发现ASP.NET MVC的网站的URL格式:Http://www.mikel.cn/Home/Index,该工具抓取不了?于是想到伪静态成:Http://www.mikel.cn/Home/Index.html,于是修改了Global.asax如下(注意:一定要将Default路由配置放在最后,否则伪静态的代码不能显示,因为MVC是从上到下遍历routeDictionary的,遇到匹配的url就行解析):


public class GlobalApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Html",                                              // Route name
"{controller}/{action}.html",                           // URL with parameters
new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);
routes.MapRoute(
"Default",                                              // Route name
"{controller}/{action}/{id}",                           // URL with parameters
new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
);


}

protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}

[转载]ASP.NET MVC Tip #39 – 在ASP.NET MVC中使用分布式缓存

mikel阅读(1179)

[转载][翻译]ASP.NET MVC Tip #39 – 在ASP.NET MVC中使用分布式缓存 – 紫色永恒 – 博客园.

原文地址:ASP.NET MVC Tip #39 – Use the Velocity Distributed Cache
原文作者:Stephen Walther

本文地址:[翻 译]ASP.NET MVC Tip #39 – 在ASP.NET MVC中使用分布式缓存
译者:紫色永恒

在这篇文章中,我将带您体验如何使用Velocity分布式缓存提高ASP.NET MVC应用程序性能。同时我也会向您说明如何使用Velocity作为会话状态提供者。

使用缓存是提高ASP.NET MVC应用程序性能的最好方式。我们知道,在ASP.NET MVC中最耗时的操作非数据库的读取莫数,那么,尽量的避免读取数据库显然成为了提升性能最好的办法。缓存可以将经常访问的数据保持在内存中从而大幅减少 数据库的读取。

由于ASP.NET MVC是ASP.NET框架的一部分,这使得我们在ASP.NET MVC中仍然可以使用标准的System.Web.Caching.Cache对象。标准的ASP.NET缓存对象很好很强大,我们可以通过它指定某些数 据在特定的时间内被缓存,建立缓存项与文件系统或者数据库表之间的依赖,甚至可以在不同的缓存向之间建立复杂的依赖链。也就是说,我们可以通过 ASP.NET缓存对象作很多很多很多…的事情。

但是标准的ASP.NET缓存对象的也有一些限制,比如它只能和我们的web应用程序运行在同一个进程,也就是说它并不是分布式的。不同 应用程序间的缓存并不能共享,我们只能为每个应用程序构造相同的缓存。

在单一服务器架构下,传统的ASP.NET缓存在web应用程序中工作得非常好。但是当我们想使用服务器组等集群架构时,问题就出现了。 比如我们有一个需要处理成十亿上百亿用户访问的web应用程序(译者按:这有点夸张吧…),或者当我们的服务器出现故障时我们并不想重新从缓存加载数据。 当我们欲在多组web服务器上共享缓存是,分布式缓存应运而生。

在这篇文章中我将向您解释如何在ASP.NET MVC应用程序中使用代号为Velocity的微软分布式缓存策略。建立和使用Velocity十分的简单,从标准的ASP.NET缓存转换至 Velocity分布式缓存也不会使您痛苦万分。

同时我也会说明如何将Velocity作为会话状态提供者(session state provider).Velocity允许您在MVC应用程序服务器群组中使用ASP.NET会话状态。您可以通过Velocity在分布式缓存中存储会 话状态。

Velocity的安装和配置

当你看到这篇文章的时候,Velocity和ASP.NET MVC一样还没有正式发布,仍然处于Preview的状态。不过你现在就可以下载Velocity来一个提前体验。

下面的地址中包括所有有关Velocity相关信息。

http://msdn.microsoft.com/en-us/data/cc655792.aspx

您可以通过这个页面的链接下载Velocity。

安装Velocity十分简单,您需要将它安装在您想使用分布式缓存的服务器上(缓存服务器),缓存会从这些服务器上自动的组织起来。

在将Velocity安装在缓存服务器上之前,您需要在您的网络中创建一个共享以便所有的缓存服务器都能够访问它。这个文件共享将包括 缓存配置文件(ClusterConfig.xml)。这个文件是一个标准的XML文件,它用来对分布式缓存进行相关配置。

在Beta版中,您必须在这个文件共享上给予Everyone帐号读写权限。右键单击文件夹,选择属性菜单然后选择安全选项单。点击编辑 按钮,选择添加,输入Everyone然后点击OK即可。这里要确认Everyone帐号具有读写权限。

当您运行Velocity(图1)的安装程序时,您将被要求填入如下信息:

· Cluster Configuration Share – 这里的路径指向您刚刚建立的文件共享.

· Cluster Name – 组名称. 按照您的意愿起个名字吧 (比如, MyCacheCluster).

· Cluster Size – 您期望在这个组中使用的缓存服务器的数量.

· Service Port Number – 应用程序和缓存服务器通信的端口(注意如果您有防火墙,您需要在防火墙中解除对该端口的屏蔽)

· Cluster Port Number — 缓存服务器之间通信的端口。Velocity将使用这个端口号。(同样需要在防火墙中解除屏蔽)

· Max Server Memory – 允许Velocity在这台服务器上使用的内存最大值。

图 1 – 安装 Velocity

clip_image002

您必须在每台安装Velocity后的服务器上配置防火墙的例外规则,否则Velocity的通讯将被阻止。您可以为 DsitributedCache.exe进程或者为每个端口号(22233端口,22234端口和22235端口等)建立一个防火墙例外规则。

使用 Velocity 管理工具

您可以通过Velocity Administration Tool命令行工具管理Velocity(图2)

图2 2 – Velocity 管理工具

clip_image004

Velocity管理工具支持如下命令(这些命令十分有用):

· start cluster – 为组中的每一台缓存服务器启动Velocity。

· stop cluster – 与上一条正相反。

· create cache – 建立并命名一个新缓存。

· delete cache – 删除一个已存在的缓存。

· list host – 列出组中的所有缓存服务器

· list cache – 类出组中的所有缓存配置。

· show hoststats <cache server>:<cache port>– 列出指定缓存服务器上的统计信息。

安装Velocity后您需要做的第一件事就是使用如下命令启动缓存组:

start cluster

ASP.NET MVC应用程序中使用Velocity

完成Velocity的建立和启动后,您可以建立一个使用Velocity缓存的ASP.NET MVC应用程序。为了在项目中使用Velocity,您需要添加对如下程序集的引用:

· CacheBaseLibrary.dll

· ClientLibrary.dll

您可以在Program Files\Microsoft Distributed Cache文件夹或任意一台安装了Velocity的机器上找到这些dll.您也可以将这些dll从缓存服务器上拷贝到您的开发环境中。

同时,您需要修改您的ASP.NET MVC应用程序的web.config文件。在<configSections>节点中添加如下子节点:

  1. <section name=“dcacheClient”
  2. type=“System.Configuration.IgnoreSectionHandler”
  3. allowLocation=“true” allowDefinition=“Everywhere”/>

接下来,在配置文件的任意位置添加如下节点:

  1. <dcacheClient deployment=“simple” localCache=“false”>
  2. <hosts>
  3. <!–List of hosts –>
  4. <host name=“localhost”
  5. cachePort=“22233”
  6. cacheHostName=“DistributedCacheService” />
  7. </hosts>
  8. </dcacheClient>

在这里您可能需要改动一下host节点下name为“localhost”的值。换句话说,MVC应用将在本地使用缓存服务。如果缓存服 务器不在您的本地环境中,您需要更改这个值,以使它指向正确的服务器。

增加缓存项及从缓存中检索指定项

分布式缓存的使用方式和普通的ASP.NET缓存使用方式十分相似。您可以使用如下的方法在分布式缓存中增加,获取和移除缓存项:

· Add() – 向分布式缓存中添加一项.如果此项的键已经存在,那么将会抛出一个异常。

· Get() – 按照指定的键从分布式缓存中获取缓存项。

· Put () – 向分布式缓存中添加一项.如果此项的键已经存在,那么它将会被替换。

· Remove() – 从分布式缓存中移除一个存在的缓存项。

让我们看看示例,代码段1中的controller使用分布式缓存来缓存影视数据库中的记录。

代码段 1 – HomeController.cs

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Web;
  5. using System.Web.Mvc;
  6. using System.Data.Caching;
  7. using Tip39.Models;
  8. using System.Diagnostics;
  9. using System.Web.Configuration;
  10. using System.Web.Hosting;
  11. using System.Data.Linq.Mapping;
  12. using System.Data.Linq;
  13. namespace Tip39.Controllers
  14. {
  15. [HandleError]
  16. public class HomeController : Controller
  17. {
  18. private DataContext _dataContext;
  19. private Table<Movie> _table;
  20. public HomeController()
  21. {
  22. // Get connection string
  23. var conString = WebConfigurationManager.ConnectionStrings[“Movies”].ConnectionString;
  24. // Get XML mapping source
  25. var url = HostingEnvironment.MapPath(“~/Models/Movie.xml”);
  26. var xmlMap = XmlMappingSource.FromUrl(url);
  27. // Create data context
  28. _dataContext = new DataContext(conString, xmlMap);
  29. _table = _dataContext.GetTable<Movie>();
  30. }
  31. public ActionResult Index()
  32. {
  33. // Try to get movies from cache
  34. var factory = new CacheFactory();
  35. var cache = factory.GetCache(“default”);
  36. var movies = (List<Movie>)cache.Get(“movies”);
  37. // If fail, get movies from db
  38. if (movies != null)
  39. {
  40. Debug.WriteLine(“Got movies from cache”);
  41. }
  42. else
  43. {
  44. movies = (from m in _table select m).ToList();
  45. cache.Put(“movies”, movies);
  46. Debug.WriteLine(“Got movies from db”);
  47. }
  48. // Display movies in view
  49. return View(“Index”, movies);
  50. }
  51. }
  52. }

Index()方法返回数据库中所有的行。首先,这个方法试图从缓存中获取数据,如果获取失败,则从数据库中获取数据并在缓存中添加一个 缓存项。

Index()方法点用Debug.WriteLine()来向Visual Studio控制台窗口显示消息。无论数据是从缓存还是从数据库取出的,控制台窗口都会忠实的体现出来。(图2)

图 2 – 使用Visual Studio 控制台跟踪缓存行为

clip_image006

您也可以使用Velocity管理工具监视分布式缓存。执行如下命令来显示分布式缓存的统计信息:

show hoststats server name:22233

将“server name”替换成您的缓存服务器的名称(很可惜,在这里不能使用“localhost”)。执行这条命令后,您将得到有关分布式缓存非常全面的统计信息, 如缓存项的数量,缓存大小,缓存被调用的次数等等(图3)。

图 3 – 缓存统计

clip_image008

代码段1中的Index()方法首先实例化了一个CacheFactory类。这个类用来从分布式缓存中提取特定的缓存项。

Velocity可以管理不同名称的缓存。您可以将不同的数据通过不同的缓存组织起来。想象一下,每个具有特定名字的缓存就像一个分布式 数据库。如果您是第一次安装Velocity,您会得到一个默认的叫做“default”的缓存项。在代码段1中,CacheFactory就是用来从获 取这个被命名为“default”的缓存项的。

接着,Cache.Get()方法用来从从缓存中获取电影数据库中的记录。当Index()方法第一次被执行时,Get()方法不会返回 任何值。因为分布式缓存中并不存在任何数据。

如果Get()方法从缓存中获取数据记录失败,则会真正的从数据库中获取记录。然后这些记录在Put()方法的帮助下添加到分布式缓存 中。

这里要注意一下,利用缓存存储或者获取的数据都是无类型的对象。在您使用这些对象之前,您必须将它们转换为特定的类型。典型的情况是比如 您将分布式缓存应用于产品信息,您需要建立一个强类型的产品信息类。

另外您还需要注意的是,当Visual Studio项目重新启动或编译后分布式缓存仍然有效。如果您需要在开发时随时清除缓存,请在Velocity管理工具中使用这些命令

stop cluster

start cluster

我们可以缓存哪些数据?

您可以将任何可序列化的类型添加到缓存中。也就是说您可以将所有标记了[Serializable]特性的类型添加到缓存中去。(这个类 所有依赖的类型也必须是可序列化的) 代码段1中的Home controller使用LINQ to SQL来从数据库中获取数据记录。当在分布式缓存中应用LINQ to SQL时您必须注意,取决于您创建LINQ to SQL类的方式,这列类有可能不允许被序列化。

如果您使用对象关系设计器建立您的LINQ to SQL类则将导致所有的类型不可以被序列化。为了解决这个问题,我选择手动创建LINQ to SQL类。请看代码段2中的Movie.cs类。

代码段 2 – Models\Movie.cs

  1. using System;
  2. namespace Tip39.Models
  3. {
  4. [Serializable]
  5. public class Movie
  6. {
  7. public int Id { getset; }
  8. public string Title { getset; }
  9. public string Director { getset; }
  10. public DateTime DateReleased { getset; }
  11. }
  12. }

注意,Movie类被包含[Serializable]特性

代码段3中的XML mapping文件被用于HomeController被构造时初始化LINQ to SQL数据上下文。这个XML mapping文件将Movie类和它的属性映射到Movies表和字段。

代码段 3 – Models\Movie.xml

  1. <?xml version=“1.0” encoding=“utf-8” ?>
  2. <Database Name=“MoviesDB” xmlns=“http://schemas.microsoft.com/linqtosql/mapping/2007”>
  3. <Table Name=“Movies” Member=“Tip39.Models.Movie”>
  4. <Type Name=“Tip39.Models.Movie”>
  5. <Column Name=“Id” Member=“Id” IsPrimaryKey=“true” IsDbGenerated=“true”/>
  6. <Column Name=“Title” Member=“Title” />
  7. <Column Name=“Director” Member=“Director” />
  8. <Column Name=“DateReleased” Member=“DateReleased” />
  9. </Type>
  10. </Table>
  11. </Database>

在分布式缓存中使用LINQ to SQL还有另外一点需要注意。您应该清楚一个LINQ to SQL请求直到您真正使用其查询结果时才会真正的执行。确认您向缓存中添加的是LINQ to SQL请求的结果而不是请求本身。

代码段1中的Index()方法将电影记录添加到缓存中,如下

  1. movies = (from m in _table select m).ToList();
  2. cache.Put(“movies”, movies);

注意LINQ to SQL中调用的ToList()方法,它使得请求真正被执行并且将数据添加到电影实体中。否则,它只是个LINQ to SQL请求表达式而已。

将Velocity作为会话状态提供者

默认情况下,ASP.NET框架将会话状态储存在与您的ASP.NET MVC应用程序相同的进程中,这种方式提供了最卓越的性能。然而这种方式有一个很大的缺点:您无法再服务器集群中应用会话状态。

ASP.NET框架提供两种改变在进程中存储会话状态的方式。您可以将会话状态信息储存在您网络中的状态服务器上或者将它们储存在SQL Server数据库中。这两种方式都可以让您实现建立一个会话状态服务中心。得益于此,您可以很好的在服务器集群中控制ASP.NET MVC应用。

不过无论是将会话状态储存在状态服务器上还是SQL Server数据库中,这里都存在一些缺点。其一,使用状态服务器的方式容错性不是很好;其二,使用SQL Server的话,则速度不会很快(您的MVC应用中用户的每个请求都将导致执行一个从数据库中读取会话状态的操作)。
Velocity为您提供了一种更好的进程外存储会话状态的方法。您可以将会话状态信息存储在Velocity分布式缓存当中。而无论是容错性还是相应速 度,都要比以上说的两种方法好的多。

假如您想计算并保持一个特定用户访问页面的次数(图4),您可以将访问次数存储在会话状态中。代码段4中的Counter controller将访问次数保存在以“count”命名的会话状态中。图 4 — 计算页面访问次数

image

代码段 4 – CounterController.cs

  1. using System.Web.Mvc;
  2. namespace Tip39.Controllers
  3. {
  4. public class CounterController : Controller
  5. {
  6. public ActionResult Index()
  7. {
  8. var count = (int?)Session[“count”] ?? 0;
  9. count++;
  10. Session[“count”] = count;
  11. return View(“Index”, count);
  12. }
  13. }
  14. }

默认情况下,Counter controller会将会话状态存储在进程内。如果您希望使用Velocity来存储会话状态的话,您需要修改web.config文件。 将<sessionState>信息添加到<system.web>节点中,如代码段5

代码段 5 – Session Configuration for Velocity

  1. <sessionState mode=“Custom” customProvider=“dcache”>
  2. <providers>
  3. <add
  4. name=“dcache”
  5. type=“System.Data.Caching.SessionStoreProvider”/>
  6. </providers>
  7. </sessionState>

这样您的会话状态就会被储存在分布式缓存当中了。您可以通过Velocity管理工具确认Counter controller是否使用了分布式缓存。执行下面的命令,结果会显示出分布式缓存被访问的次数:

show hoststats server name:22233

Counter controller的Index()方法每执行一次,总的请求统计将会增加两次:一次是读取会话状态、一次是写入会话状态。看图5

图 5 – 查看总请求数

clip_image010
需要注意的是,您根本不用改动现有程序中的任何代码就可以将会话状态存储在Velocity分布式缓存中。您可以通过修改web.config 来更改会话状态的存储方式。这意味着您可以随时根据需要在几种存储方式中自由转换。

总结

在这篇文章中,我简短的向您介绍了Velocity分布式缓存,并示范了如何在ASP.NET MVC应用程序中通过分布式缓存在多请求的情况下存储和获取数据。另外我还向您展示了如何通过Velocity分布式缓存存储会话状态的种种优点。
当然,在这里我仅仅是为您说明了Velocity的最基本的应用。Velocity还有很多的特点我在这篇文章并没有谈及。比如Velocity提供了一 个锁定机制。您可以选择使用乐观或悲观锁定来防止并发攻击。您还可以使用Velocity来标记和搜索缓存中的缓存项。
尽管这只是一个简短的介绍,但我仍希望我已经向您提供了足够的细节来帮助您尝试在ASP.NET MVC应用程序中使用Velocity。

作者:紫色永恒

出处:http://024hi.cnblogs.com/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利

[转载].NET应用访问数据库之数据库的开销问题

mikel阅读(1066)

[转载].NET应用访问数据库之数据库的开销问题 – 软件设计开发 – 博客园.

拿今天的一个例子说话吧,那就表中存放的是全国的地域信息,表结构如下:

首先用代码生成器和存储过程生成器生成基本表的操作,推荐两个工具:动软.Net代码生成器- 全功能的三层架构.Net代码生成器和codeplex上 面的一个存储过程生成工具Stored Procedure Generator (for SQL Server 2000/2005

现在提供了一个方法,可以获取顶级和二级地域的信息,最开始的做法是先获取顶级的地域信息,然后循环顶级地域信息,获取它的子节点。

代码

上面的做法,可以实现功能,最后测试获取一级和二级的地域信息,花费时间5秒左右,不说能接受吧,起码可以忍受。HttpWatch: An HTTP Viewer and HTTP Sniffer for IE and Firefox 这个工具可以查看浏览器获取数据的时间。

后面又写了一个方法,可以根据地域编号和想要获取的层级数目,获取指定地域下面的N层地域。和 上面差不多,完成后一次是,一次获取上海下面的二级花费10秒,获取三级50秒。这好像就不能忍受了吧。

然后进行优化,代码如下,变成一次获取二级的数据,然后用C#代码来生成层级关系。

代码

List<KB.DSN.Entity.District> districtList = new List<KB.DSN.Entity.District>();
KB.DSN.BusinessAccess.District dictrictBll
= new KB.DSN.BusinessAccess.District();
districtList
= dictrictBll.GetEntityList(string.Format( {0} {1}, Settings.Get_Top_And_Second_District_Where,
Settings.District_Order_By));
var top
= from c in districtList
where c.DisFatherCode.Trim() == 0
select c;
var second
= from c in districtList
where c.DisFatherCode.Trim() != 0
select c;

foreach (KB.DSN.Entity.District dis in top)
{
var se
= from c in second
where c.DisFatherCode == dis.DisCode
select c;
dis.ChildrenDis
= se as List<KB.DSN.Entity.District>;
}
return top as List<KB.DSN.Entity.District>;

组合的时候用到了LINQ的技术,好东西啊。推荐教程:LINQ体验系列文章导航

效率大增,变成了0.5和2秒,从无可救药变成可以忍受,甚至是可以接受了,很好。

结论

不应该多次往返数据库进行操作,每次都要连接数据库,然后关闭,造成很大的性能消耗,应该一次获取足量的数据,然后用C#代码 来处理,这样会提升很多。当然了,获取多少也是很需求有关系的,不是一个固定规律。

[转载]Adobe的AJAX框架--Spry

mikel阅读(1071)

[转载]Adobe的AJAX框架–Spry – AJAX – web – JavaEye论坛.

最近看完了Adobe的AJAX框架Spry的所有文档和Demo,觉得这东西挺有意思的,在这里介绍给大家。
Spry框架的开发人员是来自于DreamWeaver开发组,他们把Spry框架做为DreamWeaver的一个完美补充为设计者和开发 者提供对AJAX技术的支持。Spry框架是一个轻量级的AJAX框架,它的代码和标签十分的简洁和优雅,以保证让用户能便捷的使用,并不会为过繁杂的标 签所惑。

Spry框架的官方网址:
http://labs.adobe.com/technologies/spry
在这里你能找到最新的文档和下载最新的Spry版本,目前版本是预览版1.3_08-11。
大家可以先在下面的看到Spry的示例和Demo:
http://labs.adobe.com/technologies/spry/samples/
http://labs.adobe.com/technologies/spry/demos/

Spry框架其实就是一个客户端的JavaScript类库,包含了一组JavaScript文件,CSS,图片文件,通过官方的框架结构 图,我们能看出Spry框架的核心是四部分:XML数据器(XML Data Sets),动态区域(Dynamic Regions),装饰器库(Widgets)和变化效果库(Transition Effects)。

我们可以看出,Spry框架接收的数据格式只是XML数据格式。

一,XML数据器(XML Data Sets)
XML数据器是一个提供了从XML文档中载入和管理数据的JavaScript对象。它是Spry框架中处理XML格式数据的一个 JavaScript功能实现。通过它,我们可以从XML中直接得到转换成表格数据格式的行和列的值,其实就是数组。它封装了获取 XMLhttpRequest的方法,和发送并接收数据等一系列获取数据的方法。
要创建一个XML数据器,你必须在你的HTML文件中加入两行引入JavaScript文件的代码:

<script type=”text/javascript” src=”../../includes/xpath.js”></script>
<script type=”text/javascript” src=”../../includes/SpryData.js”></script>

上面引入的是Spry框架的核心js文件之一。”xpath.js”是Google基于XPath 1.0标准的 JavaScript功能实现。你如果想获得更多的关于它的信息,可以访问Google的开源项目 google-ajaxslt project page .
“SpryData.js”则包含了定义XML数据器和动态区域的代码。
构造XML数据器就好像新建一个类一样,用”new”关键字即可:
<script type=”text/javascript”>
var dsPhotos = new Spry.Data.XMLDataSet(“/photos.php?galleryid=2000”, “/gallery/photos/photo”);
</script>
Spry框架为XML数据器提供了一些有特色的功能:如数据排序,数据过滤,按指定时间间隔自动更新,并引入了观察者通知模式以支持事件的触 发。
关于XML数据器更多资料,大家可以参考http://labs.adobe.com/technologies/spry/articles/data_set_overview/, 这一部分笔者已经翻译完成了,但英文原文其实写的通俗易懂,如果对一些名词翻译的不准确反倒会误导了大家,所以还是不贴出来了,大家可以参考官方英文文 档。

二,动态区域(Dynamic Regions)
一旦你建立了XML数据器,你就可以在动态区域中去显示这个数据器的数据了。
创建动态区域块很简单,只要在html标签的相应位置加上”spry:region”属性就可以了,Spry框架就会知道这一块是被标识成动 态区域了。
在动态区域,你可以有条件的选择要输出的数据,也可用循环输出。
动态区域另一个特点就是,它分为master和detail两个区域类型。
如下面的Demo截图:

master区域的数据改变会使detail区域的数据相应发生改变

两者都注册成XML数据器的观察者

当master区域的数据变化时,触发detail区域的响应事件,从面达到更新相应数据。
大家可参考http://labs.adobe.com/technologies/spry/samples/DataSetMasterDetailSample.html所 示的功能。
这只是动态区域简单的示例,复杂的情况可能会有多个XML数据器与多个动态区域相互关联触发。

三,装饰器库(Widgets)
一个装饰器是由一组HTML,CSS,JavaScript封装成的高级UI。最常见的装饰器有可折叠的菜单,树型菜单和选项table面板 等。这些对象都比较难于创建,需要一些更高级的编程经验。Spry的开发组在创建装饰器这一概念就是希望开发者们能相互协作,共享各自的设计,把这些高级 界面元素用在自己的页面上。
Spry框架下的装饰器是易于编辑的。这种模型非常适合于设计者和编辑人员:要改变外观,只要改变CSS就可以了,要增加一个可折叠菜单只要 copy和paste一个代码块就够了。
如,你能看懂的这段代码是创建了一个可折叠菜单吗?
<div id=”Acc1″  class=”Accordion”>
<div class=”Panel”>
<div class=”Header”>Panel Header  1</div>
<div class=”Content”>Panel  1 Content </div>
</div>
<div class=”Panel”>
<div class=”Header”>Panel Header  2</div>
<div class=”Content”>Panel  2 Content</div>
</div>
<div class=”Panel”>
<div class=”Header”>Panel Header  3</div>
<div class=”Content”>Panel  3 Content</div>
</div>
</div>
<script>
var acc1 = new  Hanzo.Widget.Accordion(“Acc1”);
</script>
这段代码是非常简洁清晰的,没有什么繁杂的标签,这样设计是为了易于阅读。
关于这个例子,可以参考http://labs.adobe.com/technologies/spry/samples/accordion/AccordionSample.html

四,变化效果库(Transition Effects)
Spry框架的变化效果库都存于SpryEffects.js文件中,是基于JavaScript的一些动态变化效果,如,淡出,改变形状 等。
Spry框架在设计时,曾考虑直接用第三方的效果库,如Script.aculo.us,但后来开发小组觉得要保证框架代码和标签的一致性, 还是选择了自已开发,但是也基本上是以Script.aculo.us为原型进行设计,因为Script.aculo.us 本身就是一个非常优秀的变化效果库框架。
由于Spry框架现在只是预览版,所以目前只支持七种变换:
Appear/Fade Makes an element appear or fade away
Highlight Flashes a color as the background of an element
BlindUp/BlindDown Simulates a window blind, up or down, where the contents of the affected elements stay in place
SlideUp/SlideDown Simulates a window blind, where the contents of the affected element scroll accordingly
Grow/Shrink Increases/reduces the size of the element
Shake Moves the element slightly to the left, then to the right, repeatedly
Squish Reduces the element to its top-left corner and disappears

可能到这大家对Spry框架已经有了大致的了解,其实这个东西已经足够我们的大多数应用的开发了,笔者也十分期待着正式版能早日放出,并打算 在自己现在的项目中试一试了。

[转载]关于json template一点思考

mikel阅读(992)

[转载]关于json template一点思考 – AJAX – web – JavaEye论坛.

如果ajax调用从server返回的是json,那么client如何把json render成html呢?

我看到一个叫json-template的 js库,比如如下的代码,有点像xslt转换xml

{# This is a comment and will be removed from the output.}

{.section songs}
  <h2>Songs in '{playlist-name}'</h2>

  <table width="100%">
  {.repeated section @}
    <tr>
      <td><a href="{url-base|htmltag}{url|htmltag}">Play</a>
      <td><i>{title}</i></td>
      <td>{artist}</td>
    </tr>
  {.end}
  </table>
{.or}
  <p><em>(No page content matches)</em></p>
{.end}

不过这样感觉有点把事情复杂化了,我本意只是想把json转换成html。

难道应该从server端直接传回html么?

从server端直接传回HTML有个缺点,就是html只是用来显示(view)的,如果我们用html来传递所有的数据,势必有些数据要放到 hidden的element中,这样html看起来很混乱。如果不放到hidden input中,也可以放到element attribute或者class中,少量的数据可以,多了就显得有点怪异了,影响了HTML语义semantical,这里有解 释。HTML5中有些改进,不过我看 也不是很优雅的解决方法。

放到json中的话作为一个统一的model来交给ajax(control)来处理。

感觉我需要的是一个mvc的JavaScript框架,我知道dojo中也有templete的东东,不过我不大熟悉,不知道是否解决了这个问 题。(这 里有篇如何创建widget的文章,感觉正是我需要的)。dojo包装的比较深,我不大明白其底层是用何种机制来保存这些数据的。其实数据保存在 那里无关紧要(在firebug中可以看到,还是保存在DOM中),更重要的是要建立对象(widget)和标准HTML的bind关系。

Using template in Django ? 这篇文章也同意我的观点,他用到了一个叫jBind的emplate engine

google也有个tool叫closure-templates, 跟一般的模板不同的是它可以把模板文件编译成JavaScript,这样性能就更好些。

关于dojo的东西后续讨论我放到了我的博客这里:

从 “在HTML中嵌入数 据”到Dojo的组件模型

回复:

楼主可以看看这个jTemplates,这是一个JQuery模板插件,功能非常将大,就像客户端的FreeMarker,我用过感觉不错,网 址:http://jtemplates.tpython.com/

回复:

对于json数据刷新到模版,我建议楼主参考一下adobe的spry框架,完全符合楼主的需求。而且不大的一个框架。完全可以和其他js库混用。