[转载]SQL存储过程解密研究

mikel阅读(924)

[转载]SQL存储过程解密研究 – flysoul – 博客园.

从网上搜索SQL存储过程解密,可以看到一大堆的资料,其内容都基本上都一致,这是先放上一篇:
解密存储过程
本文将以此为基础进行研究,虽能解密成功,但其中解密那一段究其原理是到底是什么,一直也弄不明白,望了解内情的朋友告知。
先谈问题:
网上得来的存储过和经试验存在几个问题:
1、并没有删除原存储重建,仅仅在控制台做了一个输出,拷贝出来很不方便。
2、对于长度大点的加密数据会解密失败。
带着这两个问题来改造此存储过程。

先做准备工作,首先需要知道DAC这么个东西, 指的是数据库专用管理员连接,为管理员提供的一种特殊的诊断连接。
知道了后得先打开它,以SQL2008为例:
右击“对象浏览器”,找到“Facets”,点击,如图:

找到“Sruface Area Configuration”,选择“RemoteDacEnabled”,设为True:

然后进行DAC登录,CMD模式下敲如下命令,不清楚原理的可以自行研究:
SQLcmd -A -S 192.168.1.101 -U sa -P 123456
命令提示行下打开需处理的数据库:
>USE TEST
>GO

准 备就绪,复制搜索得到的存储过程(这里有些语法问题,做些小调整即可),生成解密存储程,然后我们准备两个加密后的存储过程,其中一个长度较大(可自行循 环创建一个500行的存储程,本文不再提供),验证得出结论,短小的存储过程很快即解密成功,并输出,但长度较大的却解密失败。
接下来看看其如何解密的:
先看这句
select @maxColID = max(subobjid),@intEncrypted = imageval FROM sys.sysobjvalues WHERE objid = object_id(@procedure)
指的是加密后的数据存放在“sys.sysobjvalues”表中,其内容存放于“imageval”字段。

知道了加密后的数据,就得进行解密,它定义了4个关键字段:
DECLARE @real_01 nvarchar(max)
DECLARE @fake_01 nvarchar(max)
DECLARE @fake_encrypt_01 nvarchar(max)
DECLARE @real_decrypt_01 nvarchar(max)
分别指的原始加密数据内容、原始加密存储过程的CREATE语句、自己构造的假的存储过程加密后的数据、最终解密后的存储过程。
其方法是按位将@real_01、@fake_encrypt_01、@real_decrypt_01进行异或运算,此处为何如此处理,原理不明!!!
WHILE @intProcSpace<=(datalength(@real_01)/2)
BEGIN
–xor real & fake & fake encrypted
SET @real_decrypt_01 = stuff(@real_decrypt_01, @intProcSpace, 1, NCHAR(UNICODE(substring(@real_01, @intProcSpace, 1)) ^ (UNICODE(substring(@fake_01, @intProcSpace, 1)) ^ UNICODE(substring(@fake_encrypt_01, @intProcSpace, 1)))))
SET @intProcSpace=@intProcSpace+1
END
实际上到此为止,加密后的存储过程已解密出来了。

其下面还有一大段语句没有仔细研究,但基本上是利用sp_helptext将内容输出,方法比较繁索,而且没有达到我们要的效果,我们将换一种方法进行输出。
基本上就这么简单,除了原理不清楚外,基本上已达到要求,接下来要解决开始提出的两个问题。
首先是长度问题,为什么长度一大就解密失败,来看看@real_decrypt_01的定义并进行初始化@real_decrypt_01的语句:
DECLARE @real_decrypt_01 nvarchar(max)
SET @real_decrypt_01 = replicate(N’A’, (datalength(@real_01) /2 ))
乍 一看没什么问题,但我们使用LEN(@real_decrypt_01)输出看看,最大输出长度为4000,可能问题就出现NVARCHAR的长度上了, 理论上NVARCHAR(MAX)支持2G的大小。为什么会出现这种情况没有研究过,但有人给出了解决方法,进行显示转换:
SET @real_decrypt_01 = replicate(CONVERT(NVARCHAR(MAX), N’A’), (datalength(@real_01) /2 ))
其它的几还有几处也是该原因,改正后进行重新运行,问题解决,长度较大的存储过程也解密成功。

第一个问题解决了,如何能方便的输出呢,试验了删除重建,但未成功,那么就用最简单的方法吧,利用xp_cmdshell将内容输出到文本。
先建立一个物理表,用于存储解密后的数据:

CREATE TABLE [dbo].[SQL_DECODE](
[ID] [int] IDENTITY(1,1) NOT NULL,
[SQLTEXT] [nvarchar](max) NOT NULL,
CONSTRAINT [ID] PRIMARY KEY CLUSTERED
(
[ID] ASC
)
) ON [PRIMARY]

GO

然后建立一个存储过程,遍历所有加密过的存储过程,调用解密存储过程进行解密,解密后输出:

CREATE PROCEDURE [dbo].[DECODE_DATABASE]
AS
SET NOCOUNT ON
BEGIN

DECLARE @PROC_NAME VARCHAR(256)
SET @PROC_NAME = ''
DECLARE @ROWS INT
DECLARE @TEMP TABLE(
NAME VARCHAR(256)
)
INSERT INTO @TEMP SELECT NAME FROM sysobjects WHERE TYPE = 'P'
AND NAME NOT IN (
'DECODE_DATABASE', 'DECODE_PROC'
)
SET @ROWS = @@ROWCOUNT
WHILE @ROWS &gt; 0
BEGIN
SELECT @PROC_NAME = NAME FROM (
SELECT ROW_NUMBER() OVER (ORDER by NAME) AS ROW, NAME FROM @TEMP
) T
WHERE ROW = @ROWS
EXEC [DECODE_PROC] @PROC_NAME
PRINT @PROC_NAME
SET @ROWS = @ROWS - 1
END

RETURN

EXEC master..xp_cmdshell 'bcp "SELECT [SQLTEXT] FROM TEST.dbo.[SQL_DECODE]" queryout C:\decode.txt -c -T -S PC2011043012JUJ'
END

GO

最后是改造后的存储过程:

CREATE PROCEDURE [dbo].[DECODE_PROC](
@PROC_NAME SYSNAME = NULL
)
AS
SET NOCOUNT ON

DECLARE @PROC_NAME_LEN INT --存储过程名长度
DECLARE @MAX_COL_ID SMALLINT --最大列ID
SELECT @MAX_COL_ID = MAX(subobjid) FROM sys.sysobjvalues WHERE objid = OBJECT_ID(@PROC_NAME) GROUP BY imageval

SELECT @PROC_NAME_LEN = DATALENGTH(@PROC_NAME) + 29
DECLARE @REAL_01 NVARCHAR(MAX) --真实加密存储过程数据
DECLARE @FACK_01 NVARCHAR(MAX) --修改为假的存储过程,长度(40003 - 存在过程名长度),原理不明?
DECLARE @FACK_ENCRYPT_01 NVARCHAR(MAX) --伪加密存储过街程数据
DECLARE @REAL_DECRYPT_01 NVARCHAR(MAX) --最终解密后的数据,初始化为原始加密长度的一半的“A”,原理不明?

SET @REAL_01 = (
SELECT imageval FROM sys.sysobjvalues WHERE objid = object_id(@PROC_NAME) AND valclass = 1 AND subobjid = 1
)

DECLARE @REAL_DATA_LEN BIGINT
SET @REAL_DATA_LEN = DATALENGTH(@REAL_01)
--PRINT @REAL_DATA_LEN

DECLARE @FACK_LEN BIGINT
SET @FACK_LEN = @REAL_DATA_LEN * 10 --改造:假的长度在原真实数据长度上放大10倍

--此处需将NVARCHAR显示转换成NVARCHAR(MAX),不然将只能产生4K长度
SET @FACK_01 = 'ALTER PROCEDURE ' + @PROC_NAME + ' WITH ENCRYPTION AS ' + REPLICATE(CONVERT(NVARCHAR(MAX), '-'), @FACK_LEN - @PROC_NAME_LEN)
--PRINT '@FACK_01 = ' + STR(LEN(@FACK_01))
EXECUTE (@FACK_01)
SET @FACK_ENCRYPT_01 = (
SELECT imageval FROM sys.sysobjvalues WHERE objid = object_id(@PROC_NAME) AND valclass = 1 AND subobjid = 1
)

SET @FACK_01 = 'CREATE PROCEDURE ' + @PROC_NAME + ' WITH ENCRYPTION AS ' + REPLICATE(CONVERT(VARCHAR(MAX), '-'), @FACK_LEN - @PROC_NAME_LEN)
SET @REAL_DECRYPT_01 = REPLICATE(CONVERT(NVARCHAR(MAX), N'A'), (DATALENGTH(@REAL_01) /2))
--PRINT 'LEN(@REAL_DECRYPT_01) = ' + STR(LEN(@REAL_DECRYPT_01))

--按位对 @REAL_01、 @FACK_01、 @REAL_DECRYPT_01 进行异或操作。
DECLARE @INT_PROC_SPACE BIGINT
SET @INT_PROC_SPACE = 1
WHILE @INT_PROC_SPACE &lt;= (DATALENGTH(@REAL_01) /2 )
BEGIN
SET @REAL_DECRYPT_01 = STUFF(
@REAL_DECRYPT_01,
@INT_PROC_SPACE,
1,
NCHAR(UNICODE(SUBSTRING(@REAL_01, @INT_PROC_SPACE, 1)) ^ (UNICODE(SUBSTRING(@FACK_01, @INT_PROC_SPACE, 1)) ^ UNICODE(SUBSTRING(@FACK_ENCRYPT_01, @INT_PROC_SPACE, 1))))
)
SET @INT_PROC_SPACE = @INT_PROC_SPACE + 1
END

--移除WITH ENCRYPTION
SET @REAL_DECRYPT_01 = REPLACE(@REAL_DECRYPT_01, 'WITH ENCRYPTION', '')
INSERT INTO [SQL_DECODE] VALUES (@REAL_DECRYPT_01)

--PRINT '@REAL_DECRYPT_01 = ' + @REAL_DECRYPT_01
--PRINT 'LEN(@REAL_DECRYPT_01) = ' + STR(LEN(@REAL_DECRYPT_01))

--删除原存储过程
SET @FACK_01 = 'DROP PROCEDURE ' + @PROC_NAME
EXEC(@FACK_01)

GO

至此,解密全过程大功告成,命令行模式下运行:
>EXEC [DECODE_DATABASE]
>GO
在C盘根目录下,解密后的存储过程文本生成成功。
运行前别忘记打开xp_cmdshell使用权限,同打开DAC一样:

或者命令行模式下敲如下命令:
>sp_configure ‘show advanced options’,1
>reconfigure
>go
>sp_configure ‘xp_cmdshell’,1
>reconfigure
>go

[转载]推荐19个很有用的 JavaScript 库

mikel阅读(962)

[转载]推荐19个很有用的 JavaScript 库 – 梦想天空(山边小溪) – 博客园.

流行的 JavaScript 库有JQuery,MooTools,Prototype,Dojo和YUI等,这些 JavaScript 库功能丰富,加上它们众多的插件,几乎能实现任何你需要的功能,然而需要实现一些特定的功能,则可以选择功能更专一的轻量库,今天这篇文章与大家分享16 个很有用的 JavaScript 库。

Blackbird: Open Source JavaScript Logging Utility
Blackbird 是一款非常酷的 JavaScript 调试工具,带有一个漂亮的界面显示和过滤调试信息。

Javascript-199 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Treesaver.js
Treesaver 是一个用于创建杂志布局的 JavaScript 框架。

Js-001 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Bibliotype
Bibliotype 是一个简单的基于 HTML、CSS 和 JS ,用于快速原型制作的的框架。

Useful-tool-127 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Highcharts – Interactive JavaScript charts
Highcharts 是一个纯 JavaScript 编写,用于生产交互式图表的 JavaScript 库。

Javascript-264 in Useful JavaScript and jQuery Tools, Libraries, Plugins

jStat: a JavaScript statistical library
Stat是一个采用JavaScript开发的统计工具包,可以让你执行高级统计操作,而不需要专门的统计语言。

Js-007 in Useful JavaScript and jQuery Tools, Libraries, Plugins

highlight.js
Highlight.js 是一个用于高亮显示程序代码的 JavaScript 库,简单易用。

Javascript-268 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Respond.js: Fast CSS3 Media Queries for Internet Explorer 6-8 and More
这个快速、轻量的库用于让IE6-8支持 CSS3 Media Queries,是一种让网页支持小屏幕设备显示的特性。

Javascript-154 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Underscore.js
Underscore 是一个实用的JavaScript工具库,提供了类似 Prototype 功能的编程支持,但没有对 JavaScript 内置的对象进行扩展。

Javascript-279 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Popcorn.js
Popcorn.js 是一个HTML5 Video框架,它提供了易于使用的API来同步交互式内容。

Javascript-315 in Useful JavaScript and jQuery Tools, Libraries, Plugins

SlickGrid!
SlickGrid 是一个用于构建简单的、快速、灵活的表格的库。

Js-004 in Useful JavaScript and jQuery Tools, Libraries, Plugins

JavaScript InfoVis Toolkit
The JavaScript InfoVis Toolkit 用于创建交互的可视化数据。

Javascript-237 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Handlebars.js: Minimal Templating on Steroids
Handlebars 是一个 JavaScript 页面模板库,帮助你轻松的构建语义化模板。可参考ThinkVitamin的文章 Getting Started with Handlebars.js

Javascript-280 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Backbone.js
Backbone.js 是一个 JavaScript MVC 应用框架,通过Models进行key-value绑定及自定义事件处理。

Javascript-313 in Useful JavaScript and jQuery Tools, Libraries, Plugins

OpenFaces
OpenFaces 是一个包含AJAX-powered JSF组件库,一个Ajax框架和一个客户端校验框架的开源框架,它源于QuipuKit并增加了一些新的组件和特性。

Js-009 in Useful JavaScript and jQuery Tools, Libraries, Plugins

d3.js
D3 可以让你绑定任何数据到文档对象模型(DOM),然后再应用数据驱动传递给文档。

Javascript-238 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Visual Event JS Library
Javascript 是事件驱动的语言,这个可视化事件库用于帮助你追踪事件。

Js-005 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Tempo: JSON rendering engine
Tempo 节奏是一个小的JSON渲染引擎,让你使用纯HTML就可以构建数据模板。

Js-002 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Art Deco – Selectable Text
实现字母分离效果的库,保留了文本能被选择的特性。

Javascript-153 in Useful JavaScript and jQuery Tools, Libraries, Plugins

Using Less.js to Simplify Your CSS3
LESS 是一个扩展CSS,让其拥有动态语言特性的库。

Javascript-175 in Useful JavaScript and jQuery Tools, Libraries, Plugins

[转载]android 打包自己的自定义组件成JAR包

mikel阅读(794)

[转载]android 打包自己的自定义组件成JAR包 – Terry_龙 – 博客园.

在项目开发过程中,我们难免会用到自己去制作自定义的VIEW控件,之后我们别的项目如果需要的话就直接将其复制到对应的项目中使用,虽说这么做是一个解决问题的方法,但毕竟不是很好。

原因是,当我们项目 积累越来越多,会发现自定义的控件越来越多,而且这些自定义的控件都是可以重复利用的,这时我们可以想想,如果把这些自定义控件都封装成一个JAR包,然 后用一个项目积累起来,之后我们以后开发项目只要在原来JAR包的基础上做扩展或者直接使用,可以大大减少自己的工作重复性。

首先Android 工程的基本面貌是这样的:

当然对应的Activity 文件被我删除了,因为当编译成jar 包我们并不需要Activity 文件。

以上面这个工程为例,我们将它打包成JAR包步骤为:

右键工程选择导出:

选择导出目标为:java->JAR文件:

把一些不必要的文件勾选掉,如下图:

到了这一步,己经基本完成,浏览选择jar 文件导出路径即可。

导出完成后,我们就可以像使用其他JAR文件一样使用我们自己的自定义控件包了。下面给出一个小DEMO介绍如何使用这个JAR包。

步骤一:

新建文件夹lib,将jar 包放入。

步骤二:

关联JAR包,如下图:

步骤三,使用JAR包:

如下代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:gif="http://schemas.android.com/apk/res/com.terry.jarTest" 
    android:id="@+id/layout" android:orientation="vertical"
    android:layout_width="fill_parent" android:layout_height="fill_parent">



    <Button android:text="停止" android:id="@+id/Button01"
        android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
    <Button android:text="开始" android:id="@+id/Button02"
        android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>



    <com.terry.gif.TypegifView
        android:layout_width="fill_parent" android:id="@+id/gifView1"
        gif:stop="true" android:layout_height="wrap_content" 
        gif:delay="1"></com.terry.gif.TypegifView>



</LinearLayout>

有一个不好的就是如果你项目中存在使用属性,必须也把属性一起复制到你要使用的项目中,要不然会识别不了。

[转载].NET简谈平台大局观

mikel阅读(955)

[转载].NET简谈平台大局观 – 南京.王清培 – 博客园.

在很久以前,我刚开始接触.NET的时候,总是搞不清楚.NET与 VisualStudio之间的关系,我开发的东西是怎么样层层流转最终形成可执行文件的;这样的疑惑一直困扰着我,我想也困扰着许许多多刚接触.NET 的开人员;尽管在学校的学生学过编译原理、操作系统等等计算机原理方面的理论知识;但是就是无法将这股力量用到实实在在的理解上;下面本人就简谈一 下.NET中的主要核心东西:高级语言(C#,VB.NET,J#等)、编译器、CLR引擎、VisualStudio系列工具等等之间是如何协调工作 的,这样的理解有利于我们自己在开发软件项目的时候可以借鉴一下好的思想(如:文档对象模型、脚本引擎等等),好的实现方式,这里不存在具体的设计模式, 这篇文章所讲的类容以具体的模块划分如何协调工作为主,针对初学者而言;

.NetFramwork是一个技术框架的统称,在这个框架里面是一系列为了实现软件开发而具有的所有支撑;

1:

这个图很简单没有给出复杂的部件,说明意思就行;在最上层就是我们直接使用的高级语言,有C#、VB等,但是这些语言各不相同,要想让CLR引擎去 执行,必须遵守一致的预定,就是微软中间语言,微软中间语言已经成为ISO公开标准,以后会有越来越多的第三方的语言可以在.NET平台开发软件了;各个 语言是有不同厂商开发的,所以必须提供语言的编译器,通过编译器将高级代码编译成标准的微软中间代码,CLR只认识中间代码(更确切的说应该是JIT认 识);CLR加载中间代码并且调用及时编译器将其编译成本地代码,这样才能做到跨平台的作用;每个电脑的配置上不同的,CPU结构、内存结构等等都不一 样,所以用IL语言传递,最终运行的时候再编译成本地代码;在具体的东西已经超出本章的范围;

整个的实现过程大致是这样的,我们来介绍VisualStudio工具是干嘛的,在没有开发工具之前程序员都是在黑屏上编码的,工具是一些前辈门用心血编写出来的软件,让我们能在这个工具上开发;那工具在整个过程中扮演着什么角色呢?

2:

工具本身的作用就是减轻我们程序员的编码工作,大大提高了开发效率;以可视化的方式展现复杂的处理过程,这就是IDE集成开发环境;初学者在刚开始 学习.NET的时候,可能会被工具迷惑,以为VisualStudio工具就是装了.NET框架,希望通过本文章能帮助你理清头绪;

[转载]一篇讲述ASP.NET运行原理的很好的文章

mikel阅读(1226)

[转贴]原贴地址http://www.cnblogs.com/Heroman/archive/2005/05/12/153975.aspx

首先先感谢 菩提本非树

这一章是全书基础和精神所在,其后的例子章节是为了验证这章的讲述和实践讲述的内容
其 中第一节是讲述ASP.NET运行模式,这一节着眼于整个ASP.NET应用程序的运作模式,实际上,并不是在讲组件,但是却很重要,因为写组件的人必须 清楚的知道ASP.NET应用程序是如何启动.如何处理请求,如何处理SESSION等这些细节问题的,但这一节对于一般读者来讲,可能十分晦涩.下面的 讲解可能有助于你理解这一切.
一个ASP.NET的应用程序是开始于IIS的.
当你请求一个包含ASP.NET应用的网址时,IIS接受到请求(IIS是WEB服务守候进程),IIS收到请求后,会根据请求者请求的主机头或者IP或者端口号来找到对应的站点.
当 找到站点后,如果你请求的资源是以ASPX为结尾的WEBFORM,时,IIS会将控制权交给一个ISAPI扩展.,名叫 AspNet_ISAIP.DLL.这时,控制权由IIS交到ASPNET的ISAPI扩展上.,需要说明的是,ISAPI扩展的级别低于IIS,但高于 用户站点,它独立于站点之外
ISAPI收到处理请求后,会启动一个ASP.NET工作进程.然后将请求者 的请求信息转交给ASP.NET工作进程(名为ASPNET_WP.EXE).接下来,控制权由ASPNET_WP掌握.ASPNET_WP首先解出请求 者的信息,如果请求者请求的ASP.NET应用程序(站点或虚拟目录,通俗一点)尚未拥有APPDOMAIN,ASPNET_WP就会建立一个 APPDOMAIN,并且将被请求的ASP.NET应用所需的Assembly(就是那些DLL,例如System.Web.DLL等)载入到 APPDOMAIN中
以上的步骤可以看到一个结论和规律:控制权是以流水式在各个请求处理者间传递,并且,前一个处理请求者必须负责传递后一个处理请求者所需的信息.而且要负责装载或初始化后一个处理者,这很像我们生活中的接力赛.
问 题是,可能有许多人会问:干嘛要如此繁琐呢?直接由IIS把请求转交给ASPNET_WP如何呢?不是不可以,而是如此一来,这个处理过程的可扩展性就变 低了.ASPNET ISAPI是IIS和ASPNET_WP之间的桥梁.虽然看起来它仅仅负责转交请求等工作.可是这样一来,就大大扩展延展性.
另 外一个疑问是关于APPDOMAIN的,包括我,对于APPDOMAIN一开始的理解就曾陷入误区,APPDOMAIN这东东微软讲的也比较含糊,有人说 跟进程一样,但我一开始理解成了IIS里的应用程序池,所以,走了不少弯路,实际上,APPDOMAIN既不是进程,也不是IIS里的应用程序池概 念..NET下的所有应用程序都运行于APPDOMAIN之中(我自己的理解),每一个APPDOMAIN是一个执行的容器,每执行一个应用程序或者 ASP.NET应用,.NET执行环境就会建立一个APPDOMAIN,然后把应用程序需要的一些DLL载入.APPDOMAIN的功能很像进程,但绝不 是进程.你可以这样理解,APPDOMAIN就是ASP.NET应用程序的执行环境吧.
AspNet_WP 不光负责建立APPDOMAIN(当然,如果已经存在的话,就直接使用这个DOMAIN了),另外,它在APPDOMAIN建立后,还会将请求转发至对应 的APPDOMAIN中的ISAPIRuntime对象。(Isapiruntime对象是APPDOMAIN的一部分)。ISAPIRUNTIME专门 负责解出请求的必要信息。它将信息和请求转交给HttpRuntime。在这里,需要说明的是IsapiRuntime是一个类,它的全称是 System.Web.Hosting.ISAPIRuntime,而HttpRuntime也是一个类,它的全称是 System.Web.HttpRuntime。因此,可以说,这两个对象是APPDOMAIN运行环境的一部分,在ASPNET_WP建立 APPDOMAIN的同时,也会作为运行环境来建立这两个对象.
由于接二连三的讲述了几个对象,所以,当 我第一遍看这本书特别是看到这部分时,觉得特别晕,因为第一对.NET FRAMEWORK的类库不甚了解,第二,对ASP.NET的运行原理初次接触.摸不着头脑,总想把这些对象名称与某个DLL或者某个实际上的文件来对 应.其实不然,不管是ISAPIRuntime也好,还是HttpRuntime,它们在APPDOMAIN建立时,作为APPDOMAIN的一部分被实 例化.所以它们代表的是内存中的一个类的实例,也就是对象.并且,这上面的一部分运作原理,似乎跟ASP.NET应用程序没有直接联系.似乎不入正题,很 容易让初看者不知所云.实际上,可以说,由IIS到ISAPI是完成了请求的第一个部,也就是接纳客户请求.由ISAPI到APPDOMAIN,是第二部 分,也就是初始化部分,旨在建立处理请求的大环境,为下面处理请求和运行ASP.NET应用程序作好准备.
接 下来,当APPDOMAIN初始化完成后,接下来就需要建立会话了吧,因此,请求由HttpRuntime来接受,HttpRunTime主要的工作便是 为每一个提出请求的客户建立一个HttpContext对象.这个东东又管理着HttpSession对象.每一个访问者有各自的HttpContext 对象和HttpSession对象,这些对象,你可以在.NET FRAMEWORK库中找到对应的类名,像System.Web.HttpContext,System.Web.HttpSessionState等.
可以看出,请求的处理过程非常类似于.NET中事件模型的处理过程.若干个处理模块被串接到一个事件上.在ASP.NET运行原理里,也是,若干个模块依次轮流处理一个请求,像流水线操作一样.
另外,作为组件开发者,还要明确一个HttpRuntime,HttpContext,HttpSession这些对象的层次关系和调用创建关系.细节部分无需了解,只要知道谁创建了谁,谁被谁调用即可
HttpRuntime负责创建HttpContext和HttpSession,httpContext负责管理httpSession
到HttpRuntime创建完httpContext为止,实际上,你的应用程序仍然没有运行,或者说,请求者的请求实际上并未真正的被处理,前面的工作都是些准备性或者辅助性的工作.
HttpRuntime除了创建上面的对象外,还要创建HttpApplication.至于创建Application对象的过程,是比较复杂的.你可以把其作为一个分支流程涉略一下
接下来,HttpApplication调用ProcessRequest方法来处理用户请求,此方法会调用对应的HttpHandler来处理用户请求,HttpHandler根据用户请求的文件的扩展名处理请求,并把请求的结果,也就是HTML发送到客户浏览器
另 外,过程的复杂性远远超出了上面的描述,基本上,黄先生这本书的第三章第一节用了十几页文本在描述ASP.NET运行过程及原理,以及处理请求时用的一些 手法,但总体上的过程如上面的描述那样,只不过,我没有将建立各种对象时的细节剥离出来展示给大家.黄先生原著上的这节内容实际上非常详细.但为何大家看 起来均言吃力呢?一方面是因为原理部分一向比较麻烦,另外一方面,是因为黄先生在讲述时,没有先向大家概要的描述过程和纲领,然后再描述细节,再是直接把 细节和纲领融合在一起.这样,如果看的时候,没有去将这节的各个小标题和内容串联起来并先总结出纲领来的话.看完后,就会头晕.实际上,整个讲述就是在描 述ASP.NET处理请求的过程.如果隐藏所有技术性的细节,而只讲流程的话,大家可能很快理解.然后再将流程中的每一部分的技术细节展现出来,我想,容 易理解的多.这好比讲故事,先将故事梗概说一下比较好吧.
当然,我不是说黄先生写的不好,实际上,这一节写的很透彻,看懂了,就很受用.流程是很重要的,它的重要性在于你知道了在何时发生何事,就可以在指定的时间点做一些处理.这一点,在黄先生本书以后的章节中讲述ASP.NET PAGE对象执行流程中更显重要.
下面的图对整个ASP.NET应用运行过程中的各个对象的职能以及流程做了图解.当然,图解抛弃了技术性的细节,例如,像HttpApplication如何建立等

[转载]模拟谷歌今日使用的css动画

mikel阅读(951)

[转载]模拟谷歌今日使用的css动画 – 胡尐睿丶 – 博客园.

不知道大家有没有注意到谷歌今天官网上的logo,刚开始一看还以为是gif,在仔细一看,发现并非如此,原来是使用CSS Sprite技术,利用一个初始图片和一张画满各个动作的拼接图片,从而实现了动画效果。

本人一时手痒,就想把这个扒下来,但发现谷歌的js写的太牛逼了,无奈,只能自己用自己的思维去模拟一个了。首先,需要两张图,分别是:

当有这两张图后,我们就可以开始模拟了。

我先通过firebug观察,发现google首页在运行的时候会循环加载以下html代码:

<div id="hplogo0" style="left:307px;top:48px;width:88px;height:89px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll 0px 0px transparent;"></div>
<div id="hplogo1" style="left:307px;top:48px;width:89px;height:89px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -88px 0px transparent;"></div>
<div id="hplogo2" style="left:307px;top:48px;width:91px;height:89px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -177px 0px transparent;"></div>
<div id="hplogo3" style="left:305px;top:49px;width:93px;height:89px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -268px 0px transparent;"></div>
<div id="hplogo4" style="left:305px;top:50px;width:93px;height:88px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -361px 0px transparent;"></div>
<div id="hplogo5" style="left:305px;top:50px;width:93px;height:88px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -454px 0px transparent;"></div>
<div id="hplogo6" style="left:306px;top:52px;width:92px;height:86px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -547px 0px transparent;"></div>
<div id="hplogo7" style="left:305px;top:53px;width:93px;height:84px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -639px 0px transparent;"></div>
<div id="hplogo8" style="left:305px;top:54px;width:94px;height:83px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -732px 0px transparent;"></div>
<div id="hplogo9" style="left:306px;top:54px;width:93px;height:83px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -826px 0px transparent;"></div>
<div id="hplogo10" style="left:307px;top:54px;width:92px;height:83px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -919px 0px transparent;"></div>
<div id="hplogo11" style="left:307px;top:54px;width:92px;height:83px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -1011px 0px transparent;"></div>
<div id="hplogo12" style="left:308px;top:54px;width:90px;height:83px;background:url(http://www.google.com.hk/logos/2011/graham11-hp-sprite.png) no-repeat scroll -1103px 0px transparent;"></div>
...
...

  实际上这就是实现动画效果的因素,但我发现,我可以循环生成,但是我无法循环生成每个div里的样式,因为样式的宽高、偏移像素都是无规律的,所以我的做法就是,把谷歌生成好的代码复制过来,然后默认全部隐藏,然后循环让其显示出来。

  原理就是这样,js的实现也更为简单,所以我就没用JQuery,以下是js实现代码:

var i=0;
window.setInterval(google,83);
function google(){
if(i<=154){ var logo = document.getElementById("hplogo"+i); logo.style.display = 'block'; } i++; } [/js]   大功告成,看下demo吧。 现代舞先驱玛莎·葛兰姆 117 周年诞辰   顺便把源码也附上吧,没太多技术含量,如果有问题,希望赐教。源码下载   附1:后来我发现google原来是把坐标等信息存在js数组里,然后循环添加div元素的时候,把值一并写进去,相关阅读《喜欢今天的Google LOGO 玛莎·葛兰姆》   附2:补充知识   Google今日涂鸦是为了纪念现代舞先驱玛莎·葛兰姆(Martha Graham,1894年5月11日-1991年4月1日)117周年诞辰,他是美国舞蹈家和编舞家,也是现代舞蹈史的创始人之一。其作品多以美国人文或是希腊古典神话为主题,代表作有《原始的神秘》(Primitive Mysteries,1936年)、《给世界的信》(Letter to the World,1940年)、《阿帕拉契山脈之春》 (Appalachian Spring,1944年)、《夜旅》(Night Journey,1947年)。

[转载]android使用Activity

mikel阅读(961)

[转载]android使用Activity – skyme – 博客园.

第一个例子,显示网址

首先创建工程

按照提示填入

我使用的是2.3版本,所以Min SDK Version填10

修改/res/layout/下main.xml文件

加入按钮

对应的程序文件如下:

<button></button>

这样就在页面上绘制了一个按钮,然后给按钮添加事件,就是点击后做什么

我的类信息是ActivityUse,这个类继承自Activity

文件中程序如下:

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

submit_data_tonewactivity();

}

private void submit_data_tonewactivity() {
Button button_start_browser = (Button) findViewById(R.id.submit_to_net);

button_start_browser.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
Uri myUri = Uri.parse("http://www.baidu.com");
Intent openBrowseIntent = new Intent(Intent.ACTION_VIEW, myUri);
startActivity(openBrowseIntent);
}
});

}

看这几句

Uri myUri = Uri.parse(“http://www.baidu.com“);
Intent openBrowseIntent = new Intent(Intent.ACTION_VIEW, myUri);
startActivity(openBrowseIntent);

Intent是用于多个Activity之间进行跳转的,Activity可以理解成web开发中的form.

程序调用浏览器,显示网址。

第二个例子,跳转页面并提交数据

用刚才建好的工程

复制一个main.xml并且更名为welcome.xml

配置界面如下,并且在main.xml中加入文本框和登陆按钮

welcome.xml中设置如下,需要对应修改配置属性 并在main.xml中加入如下设置


<!--?xml version="1.0" encoding="utf-8"?-->
<button></button>


<button></button>
<button></button>
<button></button>

Activity,需要在AndroidManifest.xml中添加设置


Welcome.java类

public class Welcome extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
Bundle myBundleForGetName = this.getIntent().getExtras();
String name = myBundleForGetName.getString("key_name");
final EditText resultName = (EditText) findViewById(R.id.logintext);
resultName.setText("欢迎你" + name);
click_button();
}

private void click_button() {
final Button btnExit = (Button) findViewById(R.id.btnexit);
btnExit.setOnClickListener(btnexit_listener);
}
//返回到main页
private Button.OnClickListener btnexit_listener = new Button.OnClickListener() {
public void onClick(View v) {
Intent main = new Intent();
main.setClass(Welcome.this, ActivityUse.class);
startActivity(main);
}
};

}
private void submit_data_tonewactivity() {
final EditText inName = (EditText) findViewById(R.id.name);
final TextView result = (TextView) findViewById(R.id.result);
Button button_start_browser = (Button) findViewById(R.id.submit_to_net);
Button button_login = (Button) findViewById(R.id.show_login);
Button button_showLoginName = (Button) findViewById(R.id.submit_to_showloginname);

button_start_browser.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
Uri myUri = Uri.parse("http://www.baidu.com");
Intent openBrowseIntent = new Intent(Intent.ACTION_VIEW, myUri);
startActivity(openBrowseIntent);
}
});

button_login.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
// 接受数据
Intent openWelcomeActivityIntent = new Intent();
Bundle myBundelForName = new Bundle();
myBundelForName.putString("key_name", inName.getText()
.toString());
openWelcomeActivityIntent.putExtras(myBundelForName);
openWelcomeActivityIntent.setClass(ActivityUse.this,
Welcome.class);
startActivity(openWelcomeActivityIntent);
}
});

button_showLoginName.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
result.setText(inName.getText() + "欢迎您进入......");
}
});
}

注意这几句

// 接受数据
Intent openWelcomeActivityIntent = new Intent();
Bundle myBundelForName = new Bundle();
myBundelForName.putString(“key_name”, inName.getText()
.toString());
openWelcomeActivityIntent.putExtras(myBundelForName);
openWelcomeActivityIntent.setClass(ActivityUse.this,
Welcome.class);
startActivity(openWelcomeActivityIntent);

新用到了Bundle,这个是在对个Activity之间传递数据用的,这个例子中将信息放入的方法是putExtras

在接受端,即Welcome.java中

  Bundle myBundleForGetName = this.getIntent().getExtras();
String name = myBundleForGetName.getString(“key_name”);
final EditText resultName = (EditText) findViewById(R.id.logintext);
resultName.setText(“欢迎你” + name);

接收数据并显示,同样的方法可以传递多个值

页面样例如下:

输入111,点击登陆



跳转后的页面如下:



点击退出可以返回原页面



第三个例子,跳转页面并且得到返回值

还是用刚才的工程

加入login.xml,和Login.java文件

并在AndroidManifest.xml指定



添加的登陆页面效果

使用的是TableLayout



login.xml中信息

<!--?xml version="1.0" encoding="utf-8"?-->



<button></button>
<button></button>

Login.java中信息

public class Login extends Activity {

/*
* (non-Javadoc)
*
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.login);

Button btnLogin = (Button) findViewById(R.id.btnLogin);
Button btnExit = (Button) findViewById(R.id.btnExit);

// 取值
final EditText etName = (EditText) this.findViewById(R.id.tname);
final EditText etPass = (EditText) this.findViewById(R.id.tpass);

btnLogin.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
Intent backIntent = new Intent();
Bundle stringBundle = new Bundle();
stringBundle.putString("loginName", etName.getText().toString());
stringBundle.putString("logPass", etPass.getText().toString());
backIntent.putExtras(stringBundle);
setResult(RESULT_OK, backIntent);
finish();
}
});

btnExit.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
Intent backIntent = new Intent();
setResult(RESULT_CANCELED, backIntent);
finish();
}
});
}

}

修改main.xml,增加 同时修改ActivityUse.java,并且加入get_returnvalue();函数 接受返回值通过重写

<button></button>
private void get_returnvalue() {
Button btnReturn = (Button) findViewById(R.id.btnReturn);
tv = (TextView) this.findViewById(R.id.textViewReturn);

btnReturn.setOnClickListener(new OnClickListener() {

public void onClick(View v) {
Intent toNextInt = new Intent();
toNextInt.setClass(ActivityUse.this, Login.class);
startActivityForResult(toNextInt, REQUESR_ASK);
}
});
}

/*
* 通过重载这个方法,得到返回的结果 requestCode 开启请求Intent时对应的请求码 resultCode 返回的结果验证码 data
* 返回的Intent
*
* @see android.app.Activity#onActivityResult(int, int,
* android.content.Intent)
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUESR_ASK) {
if (resultCode == RESULT_CANCELED) {
setTitle("cancel......");
} else if (resultCode == RESULT_OK) {
showBundle = data.getExtras();// 得到返回的包
name = showBundle.getString("loginName");
pass = showBundle.getString("logPass");
tv.setText("您的用户名是 " + name + " 您的密码是 " + pass);
}
}
}

需要在ActivityUse中加入,这个是设置请求,REQUESR_ASK可以设定任何值

  Intent toNextInt = new Intent();
toNextInt.setClass(ActivityUse.this, Login.class);
startActivityForResult(toNextInt, REQUESR_ASK);

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)

在login.java端可以取值并返回

Intent backIntent = new Intent();
Bundle stringBundle = new Bundle();
stringBundle.putString(“loginName”, etName.getText().toString());
stringBundle.putString(“logPass”, etPass.getText().toString());
backIntent.putExtras(stringBundle);
setResult(RESULT_OK, backIntent);

Run一下看下结果

点击“得到返回的数据”按钮



输入信息并点击登陆



返回的结果为刚才输入的结果。

[转载]用SQL SERVER 2005新提供的命令实现行列转换

mikel阅读(994)

[转载]用SQL SERVER 2005新提供的命令实现行列转换 – 空空儿 – 博客园.

昨天一朋友问我一个问题,需要将一张表里指定的编号(2-4个),按照名称统计编号的数量,仅有一个编号数据的不显示。
模拟表结构如下:

CREATE TABLE TEMP
(
    ID        INT IDENTITY(1,1),
    T_ID    INT,
    T_NAME    NVARCHAR(5)
)
INSERT TEMP    SELECT 1,'A'
UNION ALL    SELECT 2,'B'
UNION ALL    SELECT 3,'C'
UNION ALL    SELECT 4,'D'
UNION ALL    SELECT 1,'C'
UNION ALL    SELECT 4,'B'
UNION ALL    SELECT 4,'C'
UNION ALL    SELECT 2,'A'

实际应得到的数据为:

T_ID1       T_ID2       T_ID3       T_ID4       T_NAME
----------- ----------- ----------- ----------- ------
1           1           0           0           A
0           1           0           1           B
1           0           1           1           C

因为编号是给出的,那么得想办法得到符合条件的T_NAME,这样的话需要根据 T_ID和T_NAME分组数据然后将有多个编号的数据过滤出来,这里需要将结果集自连一下,这里使用SQL SERVER 2005 提供了一个新的语法 CTE(COMMON TABLE EXPRESSION)公共表表达式,不用创建临时表,并且可以进行JOIN操作,遗憾的是必须紧跟着使用,在后面的语句就不能用了.用CTE实现这个功能:

WITH _TEMP AS
(
    SELECT T_ID,T_NAME FROM TEMP WHERE T_ID IN (1,2,3,4) GROUP BY T_ID,T_NAME
) 
SELECT DISTINCT _TEMP.T_NAME FROM _TEMP JOIN _TEMP TEMP_TEMP
    ON _TEMP.T_NAME=TEMP_TEMP.T_NAME WHERE _TEMP.T_ID<>TEMP_TEMP.T_ID

查询的数据为:

T_NAME
------
A
B
C

这样符合条件的T_NAME数据就取出来了,现在定义一个变量,将这个数据组合起来作为条件去取出符合条件的数据:

DECLARE @NAMES NVARCHAR(20) 
SET @NAMES = '';
WITH _TEMP AS
(
    SELECT T_ID,T_NAME FROM TEMP WHERE T_ID IN (1,2,3,4) GROUP BY T_ID,T_NAME
) 
SELECT @NAMES = @NAMES + '''' + _TEMP.T_NAME + ''',' FROM _TEMP JOIN _TEMP TEMP_TEMP
    ON _TEMP.T_NAME=TEMP_TEMP.T_NAME WHERE _TEMP.T_ID<>TEMP_TEMP.T_ID
SET @NAMES = LEFT(@NAMES,LEN(@NAMES)-1)
EXEC('SELECT T_ID,T_NAME FROM TEMP WHERE T_ID IN (1,2,3,4) AND T_NAME IN ('+@NAMES+')')
--查询出的数据为:
T_ID        T_NAME
----------- ------
1           A
2           B
3           C
1           C
4           B
4           C
2           A

现在需要做的事情就是按T_NAME统计T_ID并实现行列转换,在SQL 2005之前的版本可能需要些CASE语块,如果列数不确定的话就更麻烦了,现在SQL 2005提供了 PIVOT 运算符来实现行列转换,完整的SQL语句:

DECLARE @NAMES NVARCHAR(20) 
SET @NAMES = '';
WITH _TEMP AS
(
    SELECT T_ID,T_NAME FROM TEMP WHERE T_ID IN (1,2,3,4) GROUP BY T_ID,T_NAME
) 
SELECT @NAMES = @NAMES + '''' + _TEMP.T_NAME + ''',' FROM _TEMP JOIN _TEMP TEMP_TEMP
    ON _TEMP.T_NAME=TEMP_TEMP.T_NAME WHERE _TEMP.T_ID<>TEMP_TEMP.T_ID
SET @NAMES = LEFT(@NAMES,LEN(@NAMES)-1)
DECLARE @EXEC_SQL VARCHAR(1000)
SET @EXEC_SQL =('SELECT [1][T_ID1],[2][T_ID2],[3][T_ID3],[4][T_ID4],T_NAME FROM
(SELECT T_ID,T_NAME FROM TEMP WHERE T_ID IN (1,2,3,4) AND T_NAME IN ('+@NAMES+'))G
PIVOT
(
    COUNT(T_ID) 
    FOR T_ID IN ([1],[2],[3],[4])
)P')
EXEC( @EXEC_SQL)

因为符合条件的T_NAME不会很多,所以这里使用 IN 运算符,并用SQL 2005 的一些新特性实现要求。
另一种实现方法不使用CTE,使用表自连抓取数据然后再使用PIVOT 运算符来实现行列转换:

SELECT [1][T_ID1],[2][T_ID2],[3][T_ID3],[4][T_ID4],T_NAME FROM 
(SELECT MAIN.T_ID,TEMP.T_NAME FROM TEMP MAIN JOIN TEMP ON MAIN.T_NAME=TEMP.T_NAME
WHERE MAIN.T_ID IN (1,2,3,4) AND TEMP.T_ID IN (1,2,3,4) AND MAIN.T_ID<>TEMP.T_ID
GROUP BY MAIN.ID,MAIN.T_ID,TEMP.T_NAME)G
PIVOT
(
    COUNT(T_ID) 
    FOR T_ID IN ([1],[2],[3],[4])
)P

发送过去后,在真实环境(一百二十多万条数据)执行了下,速度还是很快的。

[转载]SQL Server2008:FOR XML PATH 语句的应用

mikel阅读(898)

[转载]DianPing IT » Blog Archive » FOR XML PATH 语句的应用.

大家都知道在SQL Server中利用 FOR XML PATH 语句能够把查询的数据生成XML数据,下面是它的一些应用示例。

DECLARE @TempTable table(UserID int , UserName nvarchar(50));
insert into @TempTable (UserID,UserName) values (1,'a')
insert into @TempTable (UserID,UserName) values (2,'b')
 
select UserID,UserName from @TempTable FOR XML PATH

运行这段脚本,将生成如下结果:

<row>
  <UserID>1</UserID>
  <UserName>a</UserName>
</row>
<row>
  <UserID>2</UserID>
  <UserName>b</UserName>
</row>

大家可以看到两行数据生成了两个节点,修改一下PATH的参数:

select UserID,UserName from @TempTable FOR XML PATH('lzy')

再次运行上述脚本,将生成如下的结果:

<lzy>
  <UserID>1</UserID>
  <UserName>a</UserName>
</lzy>
<lzy>
  <UserID>2</UserID>
  <UserName>b</UserName>
</lzy>

可以看到节点变成,其实PATH() 括号内的参数是控制节点名称的,这样的话大家可以看一下如果是空字符串(不是没有参数)会是什么结果?

select UserID,UserName from @TempTable FOR XML PATH('')

执行上面这段脚本将生成结果:

<UserID>1</UserID>
<UserName>a</UserName>
<UserID>2</UserID>
<UserName>b</UserName>

这样就不显示上级节点了,大家知道在 PATH 模式中,列名或列别名被作为 XPath 表达式来处理,也就是说,是列的名字,这样大胆试验一下不给指定列名和别名会是怎么样?

select CAST(UserID AS varchar) + '',UserName + '' from @TempTable FOR XML PATH('')

运行上面这句将生成结果

1a2b
大家现在明白了吧,可以通过控制参数来生成自己想要的结果,例如:

select '{' + CAST(UserID AS varchar) + ',','"' +UserName + '"','}' from @TempTable FOR XML PATH('')

生成结果

{1,”a”}{2,”b”}

还可以生成其他格式,大家可以根据自己需要的格式进行组合。

下面是一个数据统计的应用,希望大家可以通过下面的实例想到更多的应用

DECLARE @T1 table(UserID int , UserName nvarchar(50),CityName nvarchar(50));
insert into @T1 (UserID,UserName,CityName) values (1,'a','上海')
insert into @T1 (UserID,UserName,CityName) values (2,'b','北京')
insert into @T1 (UserID,UserName,CityName) values (3,'c','上海')
insert into @T1 (UserID,UserName,CityName) values (4,'d','北京')
insert into @T1 (UserID,UserName,CityName) values (5,'e','上海')
 
SELECT B.CityName,LEFT(UserList,LEN(UserList)-1) FROM (
SELECT CityName,
    (SELECT UserName+',' FROM @T1 WHERE CityName=A.CityName  FOR XML PATH('')) AS UserList
FROM @T1 A
GROUP BY CityName
) B

生成结果(每个城市的用户名)

北京 b,d
上海 a,c,e

[转载]ASP.NET MVC3.0 入门指南 5 从控制器访问模型数据

mikel阅读(873)

[转载]ASP.NET MVC3.0 入门指南 5 从控制器访问模型数据 – cn_大斌哥 – 博客园.

从控制器访问模型数据

本节,您将创建一个新的MoviesController类并编写代码读取电影数据

并用视图模板在浏览器中显示他们。在继续前,请确保您的应用程序被编

译。

右键Controllers文件夹新建一个MoviesController控制器。选择下面的

选项:

  • Controller name: MoviesController. (默认. )
  • Template: Controller with read/write actions and views, using Entity Framework.
  • Model class: Movie (MvcMovie.Models).
  • Data context class: MovieDBContext (MvcMovie.Models).
  • Views: Razor (CSHTML). (默认.)

单击“Add”。Visual Studio创建了下面的文件夹和文件:

  • 在项目的Controller文件夹下MoviesController.cs文件
  • 在项目的View文件夹下Movies文件夹
  • 在新创建的文件夹Views\MoviesCreate.cshtml, Delete.cshtml,
    Details.cshtml, Edit.cshtml, and Index.cshtml。

ASP.NET MVC 3架构机制自动创建CRUD(create, read, update, and delete)

响应方法和视图。您现在拥有了全部的web应用程序的功能,支持增加、显示、

编辑、删除电影作品。

运行应用程序并通过在浏览器地址栏中URL后追加/Movies来浏览Movies控制器。

因为应用程序依托默认的路由(在Global.asax文件中定义),浏览请求

http://localhost:xxxxx/Movies被路由到Movies控制器的默认方法Index。

换句话说,http://localhost:xxxxx/Movies实际上和http://localhost:xxxxx/Movies/Index

是一样的。因为您还没有添加任何东西,所以电影列表是空的。

创建电影

选择“Create New”链接。输入一个电影的详细信息然后单击“Create”按钮。

单击“Create”按钮使页面回发到服务器端(那里的电影信息

保存在数据库中)。您别重定向到/Movies URL,在列表里您

能看到刚被添加的电影信息。

创建一些电影作品,测试全部的功能,编辑、明细、删除。

审视代码

打开Controllers\MoviesController.cs文件并审视生成的Index方法

代码。部分控制器Index方法的代码如下所示:

public class MoviesController : Controller
{
    private MovieDBContext db = new MovieDBContext();

    //
    // GET: /Movies/

    public ViewResult Index()
    {
        return View(db.Movies.ToList());
    }
}

如前面所述,下面的行在MoviesController类中实例化了一个电影

数据库的内容。

private MovieDBContext db = new MovieDBContext();

Movies控制器返回数据库中的所有电影资料实体并把结果传递给Index

视图。

强类型模型和@model关键字

在教程的前面部分,您了解了如何使用ViewBag对象把数据通过控制器

传递给视图。ViewBag是一个动态对象,提供了方便的迟绑定方式将信

息传递给视图。

ASP.NET MVC也支持传递强类型数据给视图模板。这种强类型的方式支

持编译时检查代码和丰富的智能感知。我们将在MoviesController类

Index.cshtml视图模板中采用这种方式。

请注意该代码创建一个List对象时调用Index方法中View助手方法。代码

通过控制器传递电影列表给视图:

public ViewResult Index()
{
    return View(db.Movies.ToList());
}

通过在视图模板文件的顶部包含@model表达式,您可以指定在视图中 您期望使用的对象类型。当您创建电影控制器的时候,Visual Studio自 动在视图模板文件Index.cshtml的顶部包含@model表达式:

@model IEnumerable.Models.Movie

@model指令允许您访问由控制器使用强类型Model对象传递给 视图的电影列表。比如,Index.cshtml模板,代码通过foreach表 达式遍历了基于强类型的电影资料。

@foreach (var item in Model) {

            @Html.DisplayFor(modelItem => item.Title)

            @Html.DisplayFor(modelItem => item.ReleaseDate)

            @Html.DisplayFor(modelItem => item.Genre)

            @Html.DisplayFor(modelItem => item.Price)

            @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
            @Html.ActionLink("Details", "Details", new { id=item.ID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.ID })

}

由于实体对象是强类型(被用作可枚举对象),循环中的每个item对象

都是强类型的Movie。其他好处指的是编译时检查代码和全部的智能感知

支持。

使用SQL SERVER精简版

实体框架代码优先(Entity Framework Code First)检测到数据库连接字符串

指向Movies数据库的不存在,所以代码优先自动创建数据库。您可以通过查看

App_Data文件夹来验证它被创建。如果您没有看到Movies.sdf文件,在解决方案

资源管理器的工具栏中单击显示“所有文件”按钮,单击“刷新”按钮,然后展开

App_Data文件夹。

双击Movies.sdf文件打开服务器资源管理器。然后展开Table文件夹,

您可以看到被创建的表。

注意:如果您双击Movies.sdf文件时报错,请确保您已经安装了

SQLServer Compact 4.0(运行时+工具支持)。如果您现在安装,您

不得不关闭在重新打开visual studio。

有两个表,一个对应Movie实体集,(EdmMetadata)一个用来存储实体和

数据库之间映射关系的对应版本。EdmMetadata表被用来检测实体和数据库

是否同步。

右键Movies表,选择编辑表结构。

注意Movies表结构和Movie类对应。实体框架代码优先(Entity Framework Code First)

依据Movie类自动创建这个结构。

完成时,关闭链接。(如果不关闭链接,您下次运行这个项目的时候可能会得到一个错误)

您现在拥有了数据库并通过简单的列表展现了他们。在下一节里,我们将审视剩下的

代码并增加一个SearchIndex查找方法和一个SearchIndex视图允许您查找数据

库中的电影资料。

下一节:ASP.NET MVC3.0 入门指南 6 审视编辑方法和视图

微笑

原文网址:http://www.asp.net/mvc/tutorials/getting-started-with-mvc3-part5-cs