[转载]SQL Server中的Merge关键字

mikel阅读(979)

[转载]SQL Server中的Merge关键字 – CareySon – 博客园.

简介

Merge关键字是一个神奇的DML关键字。它在SQL Server 2008被引入,它能将Insert,Update,Delete简单的并为一句。MSDN对于Merge的解释非常的短小精悍:”根据与源表联接的结 果,对目标表执行插入、更新或删除操作。例如,根据在另一个表中找到的差异在一个表中插入、更新或删除行,可以对两个表进行同步。”,通过这个描述,我们 可以看出Merge是关于对于两个表之间的数据进行操作的。

可以想象出,需要使用Merge的场景比如:

  •     数据同步
  •     数据转换
  •     基于源表对目标表做Insert,Update,Delete操作

 

使用Merge关键字的好处

首先是更加短小精悍的语句,在SQL Server 2008之前没有Merge的时代,基于源表对目标表进行操作需要分别写好几条Insert,Update,Delete。而使用Merge,仅仅需要使用一条语句就好。下面我们来看一个例子。

首先建立源表和目标表,并插入相关的数据,如图1所示。

1

图1.创建测试表并插入测试数据

下面我们来写一个简单的Merge语句,如图2所示。

2

图2.一个简单的Merge语句

所造成的结果如图3所示。

3

图3.Merge语句对于目标表的更新

 

最终目标表的结果如图4所示。

4

图4.最后目标表的结果

 

Merge语句还有一个强大的功能是通过OUTPUT子句,可以将刚刚做过变动的数据进行输出。我们在上面的Merge语句后加入OUTPUT子句,如图5所示。

5

图5.Merge语句后加上OUTPUT子句

 

此时Merge操作完成后,将所变动的语句进行输出,如图6所示。

6

图6.输出Merge操作产生的数据变更

 

当然了,上面的Merge关键字后面使用了多个WHEN…THEN语句,而这个语句是可选的.也可以仅仅新增或是仅仅删除,如图7所示。

7

图7.仅仅插入的Merge语句

 

我们还可以使用TOP关键字限制目标表被操作的行,如图8所示。在图2的语句基础上加上了TOP关键字,我们看到只有两行被更新。

8

图8.使用TOP关键字的Merge语句

 

但仅仅是MATCHED这种限制条件往往不能满足实际需求,我们可以在图7那个语句的基础上加上AND附加上额外的限制条件,如图9所示。

9

图9.加上了AND限制条件的Merge语句

 

Merge关键字的一些限制

  •     使用Merge关键字只能更新一个表
  •     源表中不能有重复的记录

 

小结

本文简单说明了Merge关键的字的使用。如果你使用的是SQL Server 2008之后的版本,在面对一些比如库存结账之类的业务时,放弃IF…ELSE和手写UPDATE,Insert吧,使用Merge关键字可以使这类操作更加轻松愉悦。

[转载]SQLite学习手册(实例代码)

mikel阅读(869)

[转载]SQLite学习手册(实例代码) – Stephen_Liu – 博客园.

三、高效的批量数据插入:

在给出操作步骤之前先简单说明一下批量插入的概念,以帮助大家阅读其后的示例代码。事实上,批量插入并不是什么新的概念,在其它关系型数据库的C接口 API中都提供了一定的支持,只是接口的实现方式不同而已。纵观众多流行的数据库接口,如OCI(Oracle API)、MySQL API和PostgreSQL API等,OCI提供的编程接口最为方便,实现方式也最为高效。SQLite作为一种简单灵活的嵌入式数据库也同样提供了该功能,但是实现方式并不像其他 数据库那样方便明显,它只是通过一种隐含的技巧来达到批量插入的目的,其逻辑如下:
1). 开始一个事物,以保证后面的数据操作语句均在该事物内完成。在SQLite中,如果没有手工开启一个事物,其所有的DML语句都是在自动提交模式下工作 的,既每次操作后数据均被自动提交并写入磁盘文件。然而在非自动提交模式下,只有当其所在的事物被手工COMMIT之后才会将修改的数据写入到磁盘中,之 前修改的数据都是仅仅驻留在内存中。显而易见,这样的批量写入方式在效率上势必会远远优于多迭代式的单次写入操作。
2). 基于变量绑定的方式准备待插入的数据,这样可以节省大量的sqlite3_prepare_v2函数调用次数,从而节省了多次将同一SQL语句编译成 SQLite内部识别的字节码所用的时间。事实上,SQLite的官方文档中已经明确指出,在很多时候sqlite3_prepare_v2函数的执行时 间要多于sqlite3_step函数的执行时间,因此建议使用者要尽量避免重复调用sqlite3_prepare_v2函数。在我们的实现中,如果想 避免此类开销,只需将待插入的数据以变量的形式绑定到SQL语句中,这样该SQL语句仅需调用sqlite3_prepare_v2函数编译一次即可,其 后的操作只是替换不同的变量数值。
3). 在完成所有的数据插入后显式的提交事物。提交后,SQLite会将当前连接自动恢复为自动提交模式。

下面是示例代码的实现步骤:
1). 创建测试数据表。
2). 通过执行BEGIN TRANSACTION语句手工开启一个事物。
3). 准备插入语句及相关的绑定变量。
4). 迭代式插入数据。
5). 完成后通过执行COMMIT语句提交事物。
6). 删除测试表。
见以下代码及关键性注释:

#include <sqlite3.h>
 #include <string>
 #include <stdio.h>
 
 using namespace std;
 
 void doTest()
 {
     sqlite3* conn = NULL;
     //1. 打开数据库
     int result = sqlite3_open("D:/mytest.db",&conn);
     if (result != SQLITE_OK) {
         sqlite3_close(conn);
         return;
     }
     const char* createTableSQL = 
         "CREATE TABLE TESTTABLE (int_col INT, float_col REAL, string_col TEXT)";
     sqlite3_stmt* stmt = NULL;
     int len = strlen(createTableSQL);
     //2. 准备创建数据表,如果创建失败,需要用sqlite3_finalize释放sqlite3_stmt对象,以防止内存泄露。
     if (sqlite3_prepare_v2(conn,createTableSQL,len,&stmt,NULL) != SQLITE_OK) {
         if (stmt)
             sqlite3_finalize(stmt);
         sqlite3_close(conn);
         return;
     }
     //3. 通过sqlite3_step命令执行创建表的语句。对于DDL和DML语句而言,sqlite3_step执行正确的返回值
     //只有SQLITE_DONE,对于SELECT查询而言,如果有数据返回SQLITE_ROW,当到达结果集末尾时则返回
     //SQLITE_DONE。
     if (sqlite3_step(stmt) != SQLITE_DONE) {
         sqlite3_finalize(stmt);
         sqlite3_close(conn);
         return;
     }
     //4. 释放创建表语句对象的资源。
     sqlite3_finalize(stmt);
     printf("Succeed to create test table now.\n");
 
     //5. 显式的开启一个事物。
     sqlite3_stmt* stmt2 = NULL;
     const char* beginSQL = "BEGIN TRANSACTION";
     if (sqlite3_prepare_v2(conn,beginSQL,strlen(beginSQL),&stmt2,NULL) != SQLITE_OK) {
         if (stmt2)
             sqlite3_finalize(stmt2);
         sqlite3_close(conn);
         return;
     }
     if (sqlite3_step(stmt2) != SQLITE_DONE) {
         sqlite3_finalize(stmt2);
         sqlite3_close(conn);
         return;
     }
     sqlite3_finalize(stmt2);
 
     //6. 构建基于绑定变量的插入数据。
     const char* insertSQL = "INSERT INTO TESTTABLE VALUES(?,?,?)";
     sqlite3_stmt* stmt3 = NULL;
     if (sqlite3_prepare_v2(conn,insertSQL,strlen(insertSQL),&stmt3,NULL) != SQLITE_OK) {
         if (stmt3)
             sqlite3_finalize(stmt3);
         sqlite3_close(conn);
         return;
     }
     int insertCount = 10;
     const char* strData = "This is a test.";
     //7. 基于已有的SQL语句,迭代的绑定不同的变量数据
     for (int i = 0; i < insertCount; ++i) {
         //在绑定时,最左面的变量索引值是1。
         sqlite3_bind_int(stmt3,1,i);
         sqlite3_bind_double(stmt3,2,i * 1.0);
         sqlite3_bind_text(stmt3,3,strData,strlen(strData),SQLITE_TRANSIENT);
         if (sqlite3_step(stmt3) != SQLITE_DONE) {
             sqlite3_finalize(stmt3);
             sqlite3_close(conn);
             return;
         }
         //重新初始化该sqlite3_stmt对象绑定的变量。
         sqlite3_reset(stmt3);
         printf("Insert Succeed.\n");
     }
     sqlite3_finalize(stmt3);
 
     //8. 提交之前的事物。
     const char* commitSQL = "COMMIT";
     sqlite3_stmt* stmt4 = NULL;
     if (sqlite3_prepare_v2(conn,commitSQL,strlen(commitSQL),&stmt4,NULL) != SQLITE_OK) {
         if (stmt4)
             sqlite3_finalize(stmt4);
         sqlite3_close(conn);
         return;
     }
     if (sqlite3_step(stmt4) != SQLITE_DONE) {
         sqlite3_finalize(stmt4);
         sqlite3_close(conn);
         return;
     }
     sqlite3_finalize(stmt4);
 
     //9. 为了方便下一次测试运行,我们这里需要删除该函数创建的数据表,否则在下次运行时将无法
     //创建该表,因为它已经存在。
     const char* dropSQL = "DROP TABLE TESTTABLE";
     sqlite3_stmt* stmt5 = NULL;
     if (sqlite3_prepare_v2(conn,dropSQL,strlen(dropSQL),&stmt5,NULL) != SQLITE_OK) {
         if (stmt5)
             sqlite3_finalize(stmt5);
         sqlite3_close(conn);
         return;
     }
     if (sqlite3_step(stmt5) == SQLITE_DONE) {
         printf("The test table has been dropped.\n");
     }
     sqlite3_finalize(stmt5);
     sqlite3_close(conn);
 }
 
 int main()
 {
     doTest();
     return 0;
 }
 //输出结果如下:
 //Succeed to create test table now.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //Insert Succeed.
 //The test table has been dropped.

该结果和上一个例子(普通数据插入)的结果完全相同,只是在执行效率上明显优于前者。

四、数据查询:

数据查询是每个关系型数据库都会提供的最基本功能,下面的代码示例将给出如何通过SQLite API获取数据。
1). 创建测试数据表。
2). 插入一条测试数据到该数据表以便于后面的查询。
3). 执行SELECT语句检索数据。
4). 删除测试表。
见以下示例代码和关键性注释:

#include <sqlite3.h>
 #include <string>
 #include <stdio.h>
 
 using namespace std;
 
 void doTest()
 {
     sqlite3* conn = NULL;
     //1. 打开数据库
     int result = sqlite3_open("D:/mytest.db",&conn);
     if (result != SQLITE_OK) {
         sqlite3_close(conn);
         return;
     }
     const char* createTableSQL = 
         "CREATE TABLE TESTTABLE (int_col INT, float_col REAL, string_col TEXT)";
     sqlite3_stmt* stmt = NULL;
     int len = strlen(createTableSQL);
     //2. 准备创建数据表,如果创建失败,需要用sqlite3_finalize释放sqlite3_stmt对象,以防止内存泄露。
     if (sqlite3_prepare_v2(conn,createTableSQL,len,&stmt,NULL) != SQLITE_OK) {
         if (stmt)
             sqlite3_finalize(stmt);
         sqlite3_close(conn);
         return;
     }
     //3. 通过sqlite3_step命令执行创建表的语句。对于DDL和DML语句而言,sqlite3_step执行正确的返回值
     //只有SQLITE_DONE,对于SELECT查询而言,如果有数据返回SQLITE_ROW,当到达结果集末尾时则返回
     //SQLITE_DONE。
     if (sqlite3_step(stmt) != SQLITE_DONE) {
         sqlite3_finalize(stmt);
         sqlite3_close(conn);
         return;
     }
     //4. 释放创建表语句对象的资源。
     sqlite3_finalize(stmt);
     printf("Succeed to create test table now.\n");
 
     //5. 为后面的查询操作插入测试数据。
     sqlite3_stmt* stmt2 = NULL;
     const char* insertSQL = "INSERT INTO TESTTABLE VALUES(20,21.0,'this is a test.')";
     if (sqlite3_prepare_v2(conn,insertSQL,strlen(insertSQL),&stmt2,NULL) != SQLITE_OK) {
         if (stmt2)
             sqlite3_finalize(stmt2);
         sqlite3_close(conn);
         return;
     }
     if (sqlite3_step(stmt2) != SQLITE_DONE) {
         sqlite3_finalize(stmt2);
         sqlite3_close(conn);
         return;
     }
     printf("Succeed to insert test data.\n");
     sqlite3_finalize(stmt2);
 
     //6. 执行SELECT语句查询数据。
     const char* selectSQL = "SELECT * FROM TESTTABLE";
     sqlite3_stmt* stmt3 = NULL;
     if (sqlite3_prepare_v2(conn,selectSQL,strlen(selectSQL),&stmt3,NULL) != SQLITE_OK) {
         if (stmt3)
             sqlite3_finalize(stmt3);
         sqlite3_close(conn);
         return;
     }
     int fieldCount = sqlite3_column_count(stmt3);
     do {
         int r = sqlite3_step(stmt3);
         if (r == SQLITE_ROW) {
             for (int i = 0; i < fieldCount; ++i) {
                 //这里需要先判断当前记录当前字段的类型,再根据返回的类型使用不同的API函数
                 //获取实际的数据值。
                 int vtype = sqlite3_column_type(stmt3,i);
                 if (vtype == SQLITE_INTEGER) {
                     int v = sqlite3_column_int(stmt3,i);
                     printf("The INTEGER value is %d.\n",v);
                 } else if (vtype == SQLITE_FLOAT) {
                     double v = sqlite3_column_double(stmt3,i);
                     printf("The DOUBLE value is %f.\n",v);
                 } else if (vtype == SQLITE_TEXT) {
                     const char* v = (const char*)sqlite3_column_text(stmt3,i);
                     printf("The TEXT value is %s.\n",v);
                 } else if (vtype == SQLITE_NULL) {
                     printf("This value is NULL.\n");
                 }
             }
         } else if (r == SQLITE_DONE) {
             printf("Select Finished.\n");
             break;
         } else {
             printf("Failed to SELECT.\n");
             sqlite3_finalize(stmt3);
             sqlite3_close(conn);
             return;
         }
     } while (true);
     sqlite3_finalize(stmt3);
 
     //7. 为了方便下一次测试运行,我们这里需要删除该函数创建的数据表,否则在下次运行时将无法
     //创建该表,因为它已经存在。
     const char* dropSQL = "DROP TABLE TESTTABLE";
     sqlite3_stmt* stmt4 = NULL;
     if (sqlite3_prepare_v2(conn,dropSQL,strlen(dropSQL),&stmt4,NULL) != SQLITE_OK) {
         if (stmt4)
             sqlite3_finalize(stmt4);
         sqlite3_close(conn);
         return;
     }
     if (sqlite3_step(stmt4) == SQLITE_DONE) {
         printf("The test table has been dropped.\n");
     }
     sqlite3_finalize(stmt4);
     sqlite3_close(conn);
 }
 
 int main()
 {
     doTest();
     return 0;
 }
 //输出结果如下:
 //Succeed to create test table now.
 //Succeed to insert test data.
 //The INTEGER value is 20.
 //The DOUBLE value is 21.000000.
 //The TEXT value is this is a test..
 //Select Finished.
 //The test table has been dropped.

[转载]用jQuery 编写自定义插件

mikel阅读(1036)

[转载]用jQuery 编写自定义插件 – 妙計出自山人 – 博客园.
一、基本的代码结构:

  方法一:

//name : 插件名
  (function(){
     $.fn.name = function(options){
       //自定义参数对象(插件自带) for example
       var defaults = {
         value1 : 'a',
         value2 : 'b'
       };
        var opts = $.extend(defaults,options);    //此处options为用户设置的参数对象(用户传参)
         //实现插件的代码 bla bla bla
      } 
  })(jQuery);

 方法二:

(function(){
     $.fn.extend({
         //name1,name2 对象名
         name1 : function(){
             //功能代码
         },
         name2 : function(){
             //功能代码
         },
         //其他功能
     });
 })(jQuery);

二、具体实例:

q: 加减法功能

  方法一:

(function(){
     $.fn.add = function(options){
         var defaults = {a:0,b:0};
         var opts = $.extend(defaults,options);
         return opts.a + opts.b;
     }
     $.fn.dev = function(options){
         var defaults = {a:0,b:0};
         var opts = $.extend(defaults,options);
         return opts.a - opts.b;
     }
 })(jQuery);

方法二:

(function(){
     $.fn.extend({
         add : function(a,b){
             var n = a + b;
             return n;
         },
         dev : function(a,b){
             var n = a - b;
             return n;
         }
     });
 })(jQuery);

调用代码:

var n = $(this).add(3,2);
 alert(n); //5
 var m = $(this).dev(3,2);
 alert(m);//1

[转载]从此不再惧怕URI编码:JavaScript及C# URI编码详解

mikel阅读(970)

[转载]从此不再惧怕URI编码:JavaScript及C# URI编码详解 – artwl – 博客园.

混乱的URI编码

JavaScript中编码有三种方法:escape、encodeURI、encodeURIComponent

C#中编码主要方法:HttpUtility.UrlEncode、Server.UrlEncode、Uri.EscapeUriString、Uri.EscapeDataString

  JavaScript中的还好,只提供了三个,C#中主要用的就有这么多,还没有列出其他编码(HTML),一多就弄不明白,弄不 明白就心生恐惧,心生恐惧就变得苦逼,本文就向大家详细解释在JavaScriptC#中如何对URI进行编码的方法(注:本文不涉及到其他编码)。

escape:不推荐使用

原因:eacape是BOM中的方法,只能对ASCII符号正确编码,而encodeURI、encodeURIComponent可以对所有的Unicode符号编码。ECMAScript v3 反对使用该方法,应用使用 decodeURI() 和 decodeURIComponent() 替代它。

  escape不编码字符有69个:*,+,-,.,/,@,_,0-9,a-z,A-Z

encodeURI:用于对网址编码(不包含参数)

encodeURI不编码字符有82个:!,#,$,&,’,(,),*,+,,,-,.,/,:,;,=,?,@,_,~,0-9,a-z,A-Z

encodeURI就是为这个而设计的。encodeURI不对URI中的特殊字符进行编码,如冒号(:)、斜杠(/)。下面看个示例:

encodeURI("http://www.cnblogs.com/a file with spaces.html")
// outputs http://www.cnblogs.com/a%20file%20with%20spaces.html

可以看到仅仅把空格替换成了20%,所以此方法可用于对网址进行编码。

由于encodeURI不对冒号(:)、斜杠(/)进行编码,所以如果参数(如把网址作为参数)中包含冒号(:)、斜杠(/),就会解析出错,所以此方法不能对参数进行编码。

encodeURIComponent:用于对网址参数进行编码

encodeURIComponent不编码字符有71个:!, ‘,(,),*,-,.,_,~,0-9,a-z,A-Z

  可以看到此方法对:/都进行了编码,所以不能用它来对网址进行编码。由于此方法对中文,空格,井号(#),斜线(/),冒号(:)都进行了编码,所以适合对URI中的参数进行编码。看下面的示例:

var param="博客园";
var url="http://www.cnblogs.com/?key="+encodeURIComponent(param)+"&page=1";
console.log(url);//outputs http://www.cnblogs.com/?key=%E5%8D%9A%E5%AE%A2%E5%9B%AD&page=1

可以看到,这正是我们想要的结果(这里只对需要编码的参数(page=1不需要编码)进行了编码)。

Server.UrlEncode && HttpUtility.UrlEncode:不推荐

把这两个放到一起说是因为这两个方法在绝大多数情况下是一样的。它们的区别是HttpUtility.UrlEncode默认使用UTF8格式编码,而Server.UrlEncode是使用系统预设格式编码,Server.UrlEncode使用系統预设编码做为参数调用HttpUtility.UrlEncode编码,所以如果系统全局都用UTF8格式编码,这两个方法就是一样的。

这两个方法是怎么编码的呢,我们来看个示例:

string url1 = "http://www.cnblogs.com/a file with spaces.html?a=1&b=博客园#abc";
Response.Write(HttpUtility.UrlEncode(url1) );

//output
http%3a%2f%2fwww.cnblogs.com%2fa+file+with+spaces.html%3fa%3d1%26b%3d%e5%8d%9a%e5%ae%a2%e5%9b%ad%23abc

由上面的例子我们可以看出,HttpUtility.UrlEncode对冒号(:)和斜杠(/)进行了编码,所以不能用来对网址进行编码。

那么能不能对参数进行编码呢,答案也是否定的。因为在参数中空格应该被编码为%20而不是被HttpUtility.UrlEncode编码为加号(+),所以不推荐用这两个方法对URI进行编码。

Uri.EscapeUriString:用于对网址编码(不包含参数)

我们还是用例子说话:

string url1 = "http://www.cnblogs.com/a file with spaces.html?a=1&b=博客园#abc";
Response.Write( Uri.EscapeUriString(url1));
//outputs:
http://www.cnblogs.com/a%20file%20with%20spaces.html?a=1&b=%E5%8D%9A%E5%AE%A2%E5%9B%AD#abc

可以看出,Uri.EscapeUriString对空格进行了编码,也对中文进行了编码,但对冒号(:)、斜杠(/)和井号(#)未编码,所以此方法可以用于网址进行编码,但不能对参数进行编码,作用类似JavaScript中的encodeURI方法。

Uri.EscapeDataString:用于对网址参数进行编码

仍然用例子说话:

string url1 = "http://www.cnblogs.com/a file with spaces.html?a=1&b=博客园#abc";
Response.Write(Uri.EscapeDataString(url1));
//outputs:
http%3A%2F%2Fwww.cnblogs.com%2Fa%20file%20with%20spaces.html%3Fa%3D1%26b%3D%E5%8D%9A%E5%AE%A2%E5%9B%AD%23abc

可以看出,Uri.EscapeDataString对冒号(:)、斜杠(/)、空格、中文、井号(#)都进行了编码,所以此方法不可以用于网址进行编码,但可以用于对参数进行编码,作用类似JavaScript中的encodeURIComponent方法。

小结

在JavaScript中推荐的做法是用encodeURI对URI的网址部分编码,用encodeURIComponent对URI中传递的参数进行编码。

  在C#中推荐的做法是用Uri.EscapeUriString对URI的网址部分编码,用Uri.EscapeDataString对URI中传递的参数进行编码。

解码部分就不说了,与编码方法相对应。

[转载]Add search functionality in DataGrid - jQuery EasyUI

mikel阅读(1092)

[转载]Add search functionality in DataGrid – jQuery EasyUI.

In this tutorial we will show you how to get data from database and display them to datagrid. And then demonstrate how to search through the results according the search terms the user enters.

Create DataGrid

Create the datagrid with paging feature and then add a toolbar to it.

  1. <table id=“tt” class=“easyui-datagrid” style=“width:600px;height:250px”  
  2.         url=“datagrid24_getdata.php” toolbar=“#tb”  
  3.         title=“Load Data” iconCls=“icon-save”  
  4.         rownumbers=“true” pagination=“true”>  
  5.     <thead>  
  6.         <tr>  
  7.             <th field=“itemid” width=“80”>Item ID</th>  
  8.             <th field=“productid” width=“80”>Product ID</th>  
  9.             <th field=“listprice” width=“80” align=“right”>List Price</th>  
  10.             <th field=“unitcost” width=“80” align=“right”>Unit Cost</th>  
  11.             <th field=“attr1” width=“150”>Attribute</th>  
  12.             <th field=“status” width=“60” align=“center”>Stauts</th>  
  13.         </tr>  
  14.     </thead>  
  15. </table>  

The toolbar is defined as:

  1. <div id=“tb” style=“padding:3px”>  
  2.     <span>Item ID:</span>  
  3.     <input id=“itemid” style=“line-height:26px;border:1px solid #ccc”>  
  4.     <span>Product ID:</span>  
  5.     <input id=“productid” style=“line-height:26px;border:1px solid #ccc”>  
  6.     <a href=“#” class=“easyui-linkbutton” plain=“true” onclick=“doSearch()”>Search</a>  
  7. </div>  

When user enters search values and press search button, the ‘doSearch’ function will be called:

  1. function doSearch(){  
  2.     $(‘#tt’).datagrid(‘load’,{  
  3.         itemid: $(‘#itemid’).val(),  
  4.         productid: $(‘#productid’).val()  
  5.     });  
  6. }  

The code above we call ‘load’ method to load new datagrid data. We need to pass ‘itemid’ and ‘productid’ parameters to server.

The Server Code

  1. include ‘conn.php’;  
  2.   
  3. $page = isset($_POST[‘page’]) ? intval($_POST[‘page’]) : 1;  
  4. $rows = isset($_POST[‘rows’]) ? intval($_POST[‘rows’]) : 10;  
  5. $itemid = isset($_POST[‘itemid’]) ? mySQL_real_escape_string($_POST[‘itemid’]) : ;  
  6. $productid = isset($_POST[‘productid’]) ? mySQL_real_escape_string($_POST[‘productid’]) : ;  
  7.   
  8. $offset = ($page-1)*$rows;  
  9.   
  10. $result = array();  
  11.   
  12. $where = “itemid like ‘$itemid%’ and productid like ‘$productid%'”;  
  13. $rs = mysql_query(“select count(*) from item where “ . $where);  
  14. $row = mysql_fetch_row($rs);  
  15. $result[“total”] = $row[0];  
  16.   
  17. $rs = mysql_query(“select * from item where “ . $where . ” limit $offset,$rows”);  
  18.   
  19. $items = array();  
  20. while($row = mysql_fetch_object($rs)){  
  21.     array_push($items, $row);  
  22. }  
  23. $result[“rows”] = $items;  
  24.   
  25. echo json_encode($result);  

Download the EasyUI example:

[转载]jQuery LigerUI 表格LigerGrid 结合 ASP.NET MVC 显示数据

mikel阅读(1102)

[转载]jQuery LigerUI 表格LigerGrid 结合 ASP.NET MVC 显示数据 – 谢略 – 博客园.

准备工作。创建MVC项目,拷贝LIGERUI库到Web程序中。

一,准备工作

使用开发工具:Microsoft Visual Studio 2010

使用插件版本: JQuery 1.4.1  、 JQuery LigerUI 1.1.7

二,创建MVC项目

 

三,下载最新版的ligerui,并拷贝到web的根目录

下载地址:http://ligerui.googlecode.com/

 

 

 

增加视图和Action,引入JQuery库和ligerUI库的引用,模板页中增加视图的链接

一,增加视图

 

二,增加Action

增加Action

 

三,引入jQuery库和ligerui的引用

 

引入jQuery库和ligerui的引用

 

四:模板页增加视图的链接

 

模板页增加视图的链接

准备数据结构(ligerGrid的调用) 和数据源(增加一个Action,返回JSON格式)

一,编写JS代码调用ligerGrid

 

这里要注意一下URL的格式 : /Home/GetData

 

二,准备数据源(增加一个Action,返回JSON格式)

准备数据源(增加一个Action,返回JSON格式)

 

三,效果

如何分页和排序。

一,ligerGrid服务器端分页的原理

可以利用firebug来调试,可以查看到grid加载分页数据的时候,会往服务器传几个数据:

那么在后台我们需要根据这几个参数返回grid适合的数据:

二,如何使用MVC Action接收并返回数据:

 1    public ActionResult GetData2()
 2         {
 3             //排序的字段名
 4             string sortname = Request.Params["sortname"];
 5             //排序的方向
 6             string sortorder = Request.Params["sortorder"];
 7             //当前页
 8             int page = Convert.ToInt32(Request.Params["page"]);
 9             //每页显示的记录数
10             int pagesize = Convert.ToInt32(Request.Params["pagesize"]);
11 
12             IList<Node> list = new List<Node>();
13             var total = 1000;
14             for (var i = 0; i < total; i++)
15             {
16                 list.Add(new Node()
17                 {
18                     id = i,
19                     name = "部门" + i,
20                     time = DateTime.Now
21                 });
22             }
23             //这里模拟排序操作
24             if (sortorder == "desc")
25                 list = list.OrderByDescending(c => c.id).ToList();
26             else
27                 list = list.OrderBy(c => c.id).ToList();
28 
29             IList<Node> targetList = new List<Node>();
30             //这里模拟分页操作
31             for (var i = 0; i < total; i++)
32             {
33                 if (i >= (page - 1) * pagesize && i < page * pagesize)
34                 {
35                     targetList.Add(list[i]);
36                 }
37             }
38             var griddata = new { Rows = targetList, Total = total };
39             return Json(griddata);
40         }

三,前台调用

四,效果

 

源码下载

下载地址:GridMvcApp.7z

[转载]ITeye新闻热点月刊总第48期下载

mikel阅读(935)

ITeye新闻热点月刊总第48期(2012年2月版)发布了!制作精美,内容丰富,为您总结一个月最精彩的技术新闻。

【点击下载2012年2月ITeye新闻热点月刊】

精彩内容推荐:

>>Java SE 6 生命将在今年11月终结

甲骨文公司发布公告称,其将在2012年11月份终止对Java SE 6的支持。

>>Chrome全球市场份额升至28.4%,超越IE指日可待

据最新统计,Chrome全球市场份额已达28.4%,距离IE的37.45%仅有9个百分点的差距。

【点击下载2012年2月ITeye新闻热点月刊】

[原创]WordPress增加多个百度分享按钮教程

mikel阅读(1045)

百度分享是百度推出的分享工代码,获取代码地址

获取百度分享按钮Wordpress插件点击下载:WordPress 2.7-3.2 插件更新日期:2011.09.21

  • 1、下载百度分享插件的wordpress版本,将解压得到的baidushare文件夹复制到您站点的wp- content\plugins目录下。您也可以登录后台管理中心,单击左侧的”插件”标签,单击上方的”安装插件”按钮,选择”上传”,直接选择您下载 的zip压缩包点击”现在安装”即可。wordpress
  • 2、使用管理员账号登录您的站点后台管理中心,单击左侧的”插件”标签,您将会看到页面上列出了百度分享插件,请单击插件下方的”启用”按钮启用百度分享插件。启用插件之后,请单击插件说明文字结尾处的配置链接。wordpress
  • 3、之后页面将会跳转到配置页面。插件默认嵌入按钮式标准风格,您可以在本页面通过拷贝您获得的分享代码更改样式。同时,您还可以设置分享按钮的位置和分享内容的简单定义。更改设置后需单击”保存配置”提交您的更改。wordpress
  • 4、进行上述操作后,您就可以在您的博客页面中看到百度分享按钮,并通过该按钮方便的分享内容到人人网、开心网、QQ空间、新浪微博等一系列SNS站点。下图为默认设置下的分享按钮示例。wordpress
  • 5、如果您需要在页面的其他位置嵌入按钮,百度分享插件为您提供了Wordpress小工具形式的按钮,可以方便的嵌入到 页面的各个位置。您只需要在管理页面选择”外观”标签下的”小工具”子标签,就可以在页面中找到百度分享按钮的小工具,将该小工具拖动到页面右侧的相应位 置即可。wordpress
  • 使用百度分享插件可以将多个分享代码放置在不同的位置,只需要在wordpress后台设置中选择百度分享将你需要的分享代码依次粘贴在分享的代码的文本框中保存设置即可在多处显示分享按钮

[转载]C#综合揭秘——细说进程、应用程序域与上下文

mikel阅读(812)

[转载]C#综合揭秘——细说进程、应用程序域与上下文 – 风尘浪子 – 博客园.

引言

本文主要是介绍进程(Process)、应用程序域(AppDomain)、.NET上下文(Context)的概念与操作。
虽然在一般的开发当中这三者并不常用,但熟悉三者的关系,深入了解其作用,对提高系统的性能有莫大的帮助。
在本篇最后的一节当中将会介绍到三者与线程之间的关系,希望对多线程开发人员能提供一定的帮助。
因为时间仓促,文中有错误的地方敬请点评。

目录

一、进程的概念与作用

二、应用程序域

三、深入了解.NET上下文

四、进程应用程序域与线程的关系



一、进程的概念与作用

进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。进程之间是相对独立的,一个进程无法访问另一个 进程的 数据(除非利用分布式计算方式),一个进程运行的失败也不会影响其他进程的运行,Windows系统就是利用进程把工作划分为多个独立的区域的。进程可以 理解为一个程序的基本边界。

1.1 Process 的属性与方法

在 System.Diagnostics 命名空间当中存在Process类,专门用于管理进程的开始、结束,访问进程中的模块,获取进程中的线程,设定进程的优先级别等功能。表1.0 显示了Process类的常用属性:

属性 说明
BasePriority 获取关联进程的基本优先级。
ExitCode 获取关联进程终止时指定的值。
ExitTime 获取关联进程退出的时间。
Handle 返回关联进程的本机句柄。
HandleCount 获取由进程打开的句柄数。
HasExited 获取指示关联进程是否已终止的值。
Id 获取关联进程的唯一标识符。
MachineName 获取关联进程正在其上运行的计算机的名称。
MainModule 获取关联进程的主模块。
Modules 获取已由关联进程加载的模块。
PriorityClass 获取或设置关联进程的总体优先级类别。
ProcessName 获取该进程的名称。
StartInfo 获取或设置要传递给Process的Start方法的属性。
StartTime 获取关联进程启动的时间。
SynchronizingObject 获取或设置用于封送由于进程退出事件而发出的事件处理程序调用的对象。
Threads 获取在关联进程中运行的一组线程

 表1.0

除了上述属性,Process类也定义了下列经常使用的方法:

方法 说明
GetProcessById 创建新的 Process 组件,并将其与您指定的现有进程资源关联。
GetProcessByName 创建多个新的 Process 组件,并将其与您指定的现有进程资源关联。
GetCurrentProcess 获取新的 Process 组件并将其与当前活动的进程关联。
GetProcesses 获取本地计算机上正在运行的每一个进程列表。
Start 启动一个进程。
Kill 立即停止关联的进程。
Close 释放与此组件关联的所有资源。
WaitForExit 指示 Process 组件无限期地等待关联进程退出。

 表1.1

Process类的详细信息可以参考 http://msdn.microsoft.com/zh-cn/library/system.diagnostics.process.aspx
下面将举例详细介绍一下Process的使用方式

1.2 建立与销毁进程

利用Start()与Kill()方法可以简单建立或者销毁进程,下面例子就是利用 Start 方法启动记事本的进程,并打开File.txt文件。2秒钟以后,再使用 Kill 方法销毁进程,并关闭记事本。

1         static void Main(string[] args)
2         {
3             Process process = Process.Start("notepad.exe","File.txt");
4             Thread.Sleep(2000);
5             process.Kill();
6         }

1.3 列举计算机运行中的进程

在表1.0 中可以看到,使用 GetProcesses 方法可以获取本地计算机上正在运行的每一个进程列表。
而进程的 Id 属性是每个进程的唯一标志,通过下面的方法,可以显示当前计算机运行的所有进程信息。
因为篇幅关系,下面例子只获取前10个进程。

 1         static void Main(string[] args)
 2         {
 3             var processList = Process.GetProcesses()
 4                 .OrderBy(x=>x.Id)
 5                 .Take(10);
 6             foreach (var process in processList)
 7                 Console.WriteLine(string.Format("ProcessId is:{0} \t ProcessName is:{1}",
 8                     process.Id, process.ProcessName));
 9             Console.ReadKey();
10         }

运行结果

如果已知进程的Id,就可以通过 GetProcessById 方法获取对应的进程。

 1         static void Main(string[] args)
 2         {
 3             try
 4             {
 5                 var process = Process.GetProcessById(1772);
 6                 Console.WriteLine("Process name is:" + process.ProcessName);
 7             }
 8             catch (ArgumentException ex)
 9             {
10                 Console.WriteLine("Process is nothing!");
11             }
12             Console.ReadKey();
13         }

同样地,你也可能通过GetProcessByName方法获取多个对应名称的进程。

注意:如果不能找到当前ID的进程,系统就会抛出ArgumentException异常。所以使用方法 GetProcessById 获取进程时应该包含在 try{…} catch{..} 之内。

1.4 获取进程中的多个模块

在表1.0 中包含了Modules属性,通过此属性可能获取进程中的多个模块。
这些模块可以是以 *.dll 结尾的程序集,也可以是 *.exe 结尾的可执行程序。
下面的例子就是通过 Process 的 GetCurrentProcess 方法获取当前运行的进程信息,然后显示当前进程的多个模块信息。

1         static void Main(string[] args)
2         {
3             var moduleList = Process.GetCurrentProcess().Modules;
4             foreach (System.Diagnostics.ProcessModule module in moduleList)
5                 Console.WriteLine(string.Format("{0}\n URL:{1}\n Version:{2}",
6                     module.ModuleName,module.FileName,module.FileVersionInfo.FileVersion));
7             Console.ReadKey();
8         }

运行结果:

回到目录


二、应用程序域

使用.NET建立的可执行程序 *.exe,并没有直接承载在进程当中,而是承载到应用程序域(AppDomain)当中。应用程序域是.NET引入的一个新概念,它比进程所占用的资源要少,可以被看作是一个轻量级的进程。
在一个进程中可以包含多个应用程序域,一个应用程序域可以装载一个可执行程序(*.exe)或者多个程序集(*.dll)。这样可以使应用程序域之间实现深度隔离,即使进程中的某个应用程序域出现错误,也不会影响其他应用程序域的正常运作。

当一个程序集同时被多个应用程序域调用时,会出现两种情况:
第一种情况:CLR分别为不同的应用程序域加载此程序集。
第二种情况:CLR把此程序集加载到所有的应用程序域之外,并实现程序集共享,此情况比较特殊,被称作为Domain Neutral。

2.1 AppDomain的属性与方法

在System命名空间当中就存在AppDomain类,用管理应用程序域。下面是AppDomain类的常用属性:

属性 说明
ActivationContext 获取当前应用程序域的激活上下文。
ApplicationIdentity 获得应用程序域中的应用程序标识。
BaseDirectory 获取基目录。
CurrentDomain 获取当前 Thread 的当前应用程序域。
Id 获得一个整数,该整数唯一标识进程中的应用程序域。
RelativeSearchPath 获取相对于基目录的路径,在此程序集冲突解决程序应探测专用程序集。
SetupInformation 获取此实例的应用程序域配置信息。

表2.0

AppDomain类中有多个方法,可以用于创建一个新的应用程序域,或者执行应用程序域中的应用程序。

方法 说明
CreateDomain 创建新的应用程序域。
CreateInstance 创建在指定程序集中定义的指定类型的新实例。
CreateInstanceFrom 创建在指定程序集文件中定义的指定类型的新实例。
DoCallBack 在另一个应用程序域中执行代码,该应用程序域由指定的委托标识。
ExecuteAssembly 执行指定文件中包含的程序集。
ExecuteAssemblyByName 执行程序集。
GetAssemblies 获取已加载到此应用程序域的执行上下文中的程序集。
GetCurrentThreadId 获取当前线程标识符。
GetData 为指定名称获取存储在当前应用程序域中的值。
IsDefaultAppDomain 返回一个值,指示应用程序域是否是进程的默认应用程序域。
SetData 为应用程序域属性分配值。
Load 将 Assembly 加载到此应用程序域中。
Unload 卸载指定的应用程序域。

表2.1

AppDomain类中有多个事件,用于管理应用程序域生命周期中的不同部分。

事件 说明
AssemblyLoad 在加载程序集时发生。
AssemblyResolve 在对程序集的解析失败时发生。
DomainUnload 在即将卸载 AppDomain 时发生。
ProcessExit 当默认应用程序域的父进程存在时发生。
ReflectionOnlyAssemblyResolve 当程序集的解析在只反射上下文中失败时发生。
ResourceResolve 当资源解析因资源不是程序集中的有效链接资源或嵌入资源而失败时发生。
TypeResolve 在对类型的解析失败时发生。
UnhandledException 当某个异常未被捕获时出现。

表2.2

下面将举例详细介绍一下AppDomain的使用方式

2.2 在AppDomain中加载程序集

由表2.1中可以看到,通过CreateDomain方法可以建立一个新的应用程序域。
下面的例子将使用CreateDomain建立一个应用程序域,并使用Load方法加载程序集Model.dll。最后使用GetAssemblies方法,列举此应用程序域中的所有程序集。

1         static void Main(string[] args)
2         {
3             var appDomain = AppDomain.CreateDomain("NewAppDomain");
4             appDomain.Load("Model");
5             foreach (var assembly in appDomain.GetAssemblies())
6                 Console.WriteLine(string.Format("{0}\n----------------------------",
7                     assembly.FullName));
8             Console.ReadKey();
9         }

运行结果

注意:当加载程序集后,就无法把它从AppDomain中卸载,只能把整个AppDomain卸载。

当需要在AppDomain加载可执行程序时,可以使用ExecuteAssembly方法。

AppDomain.ExecuteAssembly(“Example.exe”);

2.3 卸载AppDomain

通过Unload可以卸载AppDomain,在AppDomain卸载时将会触发DomainUnload事件。
下面的例子中,将会使用 CreateDomain建立一个名为NewAppDomain的应用程序域。然后建立AssemblyLoad的事件处理方法,在程序集加载时显示程序 集的信息。最后建立DomainUnload事件处理方法,在AppDomain卸载时显示卸载信息。

 1         static void Main(string[] args)
 2         {
 3             //新建名为NewAppDomain的应用程序域 4             AppDomain newAppDomain = AppDomain.CreateDomain("NewAppDomain");
 5             //建立AssemblyLoad事件处理方法 6             newAppDomain.AssemblyLoad +=
 7                 (obj, e) =>
 8                 {
 9                     Console.WriteLine(string.Format("{0} is loading!", e.LoadedAssembly.GetName()));
10                 };
11             //建立DomainUnload事件处理方法12             newAppDomain.DomainUnload +=
13                 (obj, e) =>
14                 {
15                     Console.WriteLine("NewAppDomain Unload!");
16                 };
17             //加载程序集18             newAppDomain.Load("Model");
19             //模拟操作20             for (int n = 0; n < 5; n++)
21                 Console.WriteLine(" Do Work.......!");
22              //卸载AppDomain23             AppDomain.Unload(newAppDomain);
24             Console.ReadKey();
25         }

运行结果

2.4 在AppDomain中建立指定程序集中指定类的对象

使用CreateInstance方法,能建立程序集中指定类的对像。但使用此方法将返回一个ObjectHandle对象,若要将此值转化为原类型,可调用Unwrap方法。
下面例子会建立Model.dll程序集中的Model.Person对象。

 1 namespace Test
 2 {
 3      public class Program
 4     {
 5          static void Main(string[] args)
 6          {
 7              var person=(Person)AppDomain.CurrentDomain
 8                           .CreateInstance("Model","Model.Person").Unwrap();
 9              person.ID = 1;
10              person.Name = "Leslie";
11              person.Age = 29;
12              Console.WriteLine(string.Format("{0}'s age is {1}!",person.Name,person.Age));
13              Console.ReadKey();
14          }
15     }
16 }
17
18 namespace Model
19 {
20     public class Person
21     {
22           public int ID
23           {
24               get;
25               set;
26           }
27           public string Name
28           {
29                get;
30                set;
31           }
32           public int Age
33           {
34                get;
35                set;
36           }
37      }
38 }


回到目录

三、深入了解.NET上下文

3.1 .NET上下文的概念

应用程序域是进程中承载程序集的逻辑分区,在应用程序域当中,存在更细粒度的用于承载.NET对象的实体,那就.NET上下文Context。
所有的.NET对象都存在于上下文当中,每个AppDomain当中至少在于一个默认上下文(context 0)。
一般不需要指定特定上下文的对象被称为上下文灵活对象(context-agile),建立此对象不需要特定的操作,只需要由CLR自行管理,一般这些对象都会被建立在默认上下文当中。

图3.0

3.2 透明代理

在上下文的接口当中存在着一个消息接收器负责检测拦截和处理信息,当对象是MarshalByRefObject的子类的时候,CLR将会建立透明代理,实现对象与消息之间的转换。
应 用程序域是CLR中资源的边界,一般情况下,应用程序域中的对象不能被外界的对象所访问。而MarshalByRefObject 的功能就是允许在支持远程处理的应用程序中跨应用程序域边界访问对象,在使用.NET Remoting远程对象开发时经常使用到的一个父类。
但此文章针对的是进程与应用程序域的作用,关于.NET Remoting远程对象开发可参考:“回顾.NET Remoting分布式开发”

3.3 上下文绑定

当系统需要对象都使用消息接收器机制的时候,即可使用ContextBoundObject类。ContextBoundObject继承了MarshalByRefObject类,保证了它的子类都会通过透明代理被访问。
在 第一节介绍过:一般类所建立的对象为上下文灵活对象(context-agile),它们都由CLR自动管理,可存在于任意的上下文当中。而 ContextBoundObject 的子类所建立的对象只能在建立它的对应上下文中正常运行,此状态被称为上下文绑定。其他对象想要访问ContextBoundObject 的子类对象时,都只能通过代透明理来操作。

下面的例子,是上下文绑定对象与上下文灵活对象的一个对比。Example 是一个普通类,它的对象会运行在默认上下文当中。而ContextBound类继承了ContextBoundObject,它的对象是一个上下文绑定对 象。ContextBound还有一个Synchronization特性,此特性会保证ContextBound对象被加载到一个线程安全的上下文当中 运行。另外,Context类存在ContextProperties属性,通过此属性可以获取该上下文的已有信息。

 1     class Program
 2     {
 3         public class Example
 4         {
 5             public void Test()
 6             {
 7                 ContextMessage("Example Test\n");
 8             }
 9             //访问上下文绑定对象测试10             public void Sync(ContextBound contextBound)
11             {
12                 contextBound.Test("Example call on contextBound\n");
13             }
14         }
15
16         [Synchronization]
17         public class ContextBound:ContextBoundObject
18         {
19             public void Test(string message)
20             {
21                 ContextMessage(message);
22             }
23         }
24
25         static void Main(string[] args)
26         {
27             Example example = new Example();
28             example.Test();
29             ContextBound contextBound = new ContextBound();
30             contextBound.Test("ContentBound Test\n");
31             example.Sync(contextBound);
32             Console.ReadKey();
33         }
34
35         //显示上下文信息36         public static void ContextMessage(string data)
37         {
38             Context context = Thread.CurrentContext;
39             Console.WriteLine(string.Format("{0}ContextId is {1}", data, context.ContextID));
40             foreach (var prop in context.ContextProperties)
41                 Console.WriteLine(prop.Name);
42             Console.WriteLine();
43         }
44     }

运行结果

由运行结果可以发现,example对象一般只会工作于默认上下文context 0 当中,而contextBound则会工作于线程安全的上下文 context 1当中。当example需要调用contextBound对象时,就会通过透明代理把消息直接传递到context 1中。

回到目录

四、进程、应用程序域、线程的相互关系

4.1 跨AppDomain运行代码

在应用程序域之间的数据是相对独立的,当需要在其他AppDomain当中执行当前 AppDomain中的程序集代码时,可以使用CrossAppDomainDelegate委托。把CrossAppDomainDelegate委托 绑定方法以后,通过AppDomain的DoCallBack方法调用执行委托。

 1         static void Main(string[] args)
 2         {
 3             Console.WriteLine("CurrentAppDomain start!");
 4             //建立新的应用程序域对象 5             AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
 6             //绑定CrossAppDomainDelegate的委托方法 7             CrossAppDomainDelegate crossAppDomainDelegate=new CrossAppDomainDelegate(MyCallBack);
 8             //绑定DomainUnload的事件处理方法 9             newAppDomain.DomainUnload += (obj, e) =>
10             {
11                 Console.WriteLine("NewAppDomain unload!");
12             };
13             //调用委托14             newAppDomain.DoCallBack(crossAppDomainDelegate);
15             AppDomain.Unload(newAppDomain) ;
16             Console.ReadKey();
17         }
18
19         static public void MyCallBack()
20         {
21             string name = AppDomain.CurrentDomain.FriendlyName;
22             for(int n=0;n<4;n++)
23             Console.WriteLine(string.Format( " Do work in {0}........" , name));
24         }

运行结果

4.2 跨AppDomain的线程

线程存在于进程当中,它在不同的时刻可以运行于多个不同的AppDomain当中。它是进程 中的基本执行单元,在进程入口执行的第一个线程被视为这个进程的主线程。在.NET应用程序中,都是以Main()方法作为入口的,当调用此方法时 系统就会自动创建一个主线程。线程主要是由CPU寄存器、调用栈和线程本地存储器(Thread Local Storage,TLS)组成的。CPU寄存器主要记录当前所执行线程的状态,调用栈主要用于维护线程所调用到的内存与数据,TLS主要用于存放线程的状 态信息。
关于线程的介绍,可参考 C#综合揭秘——细说多线程(上)”、“C#综合揭秘——细说多线程(下)”

下面的例子将介绍一下如何跨AppDomain使用线程,首先建立一个ConsoleApplication项目,在执行时输入当前线程及应用程序域的信息,最后生成Example.exe的可执行程序。

1         static void Main(string[] args)
2         {
3             var message = string.Format(" CurrentThreadID is:{0}\tAppDomainID is:{1}",
4                 Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
5             Console.WriteLine(message);
6             Console.Read();
7         }

然后再新建一个ConsoleApplication项目,在此项目中新一个AppDomain对象,在新的AppDomain中通过ExecuteAssembly方法执行Example.exe程序。

 1         static void Main(string[] args)
 2         {
 3             //当前应用程序域信息 4             Console.WriteLine("CurrentAppDomain start!");
 5             ShowMessage();
 6
 7             //建立新的应用程序域对象 8             AppDomain newAppDomain = AppDomain.CreateDomain("newAppDomain");
 9             //在新的应用程序域中执行Example.exe10             newAppDomain.ExecuteAssembly("Example.exe");
11
12             AppDomain.Unload(newAppDomain);
13             Console.ReadKey();
14         }
15
16         public static void ShowMessage()
17         {
18             var message = string.Format(" CurrentThreadID is:{0}\tAppDomainID is:{1}",
19                 Thread.CurrentThread.ManagedThreadId, AppDomain.CurrentDomain.Id);
20             Console.WriteLine(message);
21         }

运行结果

可见,ID等于9的线程在不同时间内分别运行于AppDomain 1与AppDomain 2当中。

4.3 跨上下文的线程

线程既然能够跨越AppDomain的边界,当然也能跨越不同的上下文。
下面这个例子中,线程将同时运行在默认上下文与提供安全线程的上下文中。

 1     class Program
 2     {
 3         [Synchronization]
 4         public class ContextBound : ContextBoundObject
 5         {
 6             public void Test()
 7             {
 8                 ShowMessage();
 9             }
10         }
11
12         static void Main(string[] args)
13         {
14             //当前应用程序域信息15             Console.WriteLine("CurrentAppDomain start!");
16             ShowMessage();
17
18             //在上下文绑定对象中运行线程19             ContextBound contextBound = new ContextBound();
20             contextBound.Test();
21             Console.ReadKey();
22         }
23
24         public static void ShowMessage()
25         {
26             var message = string.Format(" CurrentThreadID is:{0}\tContextID is:{1}",
27                  Thread.CurrentThread.ManagedThreadId, Thread.CurrentContext.ContextID);
28             Console.WriteLine(message);
29         }
30     }

运行结果

本篇总结

进程(Process)、线程(Thread)、应用程序域(AppDomain)、上下文 (Context)的关系如图5.0,一个进程内可以包括多个应用程序域,也有包括多个线程,线程也可以穿梭于多个应用程序域当中。但在同一个时刻,线程 只会处于一个应用程序域内。线程也能穿梭于多个上下文当中,进行对象的调用。

虽然进程、应用程序域与上下文在平常的开发中并非经常用到,但深入地了解三者的关系,熟悉其操作方式对合理利用系统的资源,提高系统的效率是非常有意义的。
尤其是三者与线程之间的关系尤为重要,特别是在一个多线程系统中,如果不能理清其关系而盲目使用多线程,容易造成资源抢占与死锁之类的错误。

 图5.0

希望本篇文章对相关的开发人员有所帮助。
对.NET开发有兴趣的朋友请加入博客园讨论小组“.NET高级编程”一起探讨!

[转载]提高你开发效率的十五个 Visual Studio 使用技巧

mikel阅读(934)

[转载]提高你开发效率的十五个 Visual Studio 使用技巧 – Ron Ngai – 博客园.

相信做开发的没有不重视效率的。开发C#,VB的都知道,我们很依赖VS,或者说,我们很感谢VS。能够对一个IDE产生依赖,说明这个IDE确实 有它的独特之处。无容置疑,VS是一个非常强大的IDE,它支持多语言编辑。支持C#,VB,C/C++,HTML……它拥有强大的调试编译功 能。它让我们不用去记住那些安装,环境变量设置,服务器设置,编译的繁琐过程。高度集成化。凡事有利有弊,在敏捷开发盛行的时代,VS是否值得我们使用是 无容置疑的。但是强大的VS也拥有众多的设置,众多的技巧。记住某些小技巧可以让我们更加方便,快捷地使用VS。这是很有必要的。每个人或多或少记住了一 些小技巧。但是不可能全部都记住,我们按照我们自己的编程习惯记住一些自己比较常用的就好。

下面是鄙人在编码过程中发现而且比较经常使用的一些小技巧,希望对你有所帮助。

 

1.行编辑(复制,剪切,删除,交换)


 

当你在光标停留行使用快捷键Ctrl+C,X,L时,可以复制,剪切,删除整行内容。当然,右键也是可以的。跟平时的复制,剪切,删除就是选中和没选中代码的区别而已。

如果你想交换上下两行,你可以使用快捷键(Shift+Alt+T),前提是光标要停留在上面那一行。替换之后,光标会一直跟随原本的那一行。

 

2.注释(//TODO:…)


 

看标题的话,你可能想打我。那个程序员不知道注释啊,不就//或者/*…..*/亦或者<!–….–>(HTML/XML注释)。但是使用过

// TODO:注释部分

的,估计是少数吧。如果你喜欢用“任务列表”记录一些要做的事情,这个小功能最适合你了。你可以再VS 2010的菜单上找到任务列表窗,点击“菜单->视图->任务列表”,你也可以点击快捷键“Ctrl+W,T”。VS还提供了,HACK,UNTODU,UnresolvedMergeConflict标记注释,你可以在“工具->选项->环境->任务列表”找到并且编辑/添加/删除标记注释。下面是图示:

标记注释和任务列表

标记注释设置

3.创建区域(#region和#endregion)


 

当代码越来越多的时候,你会期望可以隐藏一些代码,而#region 和#endregion 就是这样的功能。你可以在任何位置隐藏任何代码。即使是隐藏的内容不属于同一个函数。你可以点击#region旁边的+/-,展开/隐藏代码。在隐藏的时 候,当你的光标放放置在备注上面的时候,VS会显示出隐藏的代码内容。

隐藏代码

4.选择一个单词/选择一个字符串


 

如你所知双击一个单词的时候会选择整个单词。按住Ctrl键单击单词的任意位置同样可以选中单词。

双击字符串第一个引号的左侧可以选中整个字符串。按住Ctrl键单击第一个引号的前面同样可以选中整个字符串。

 

5.将代码放入工具箱


 

工具箱是拿来放控件的地方。我们在使用控件的时候,只需要从控件当中拖动控件到代码就可以了,这样可以省去大量代码的编辑工作。既然工具箱如此方便,那么是否可以将一段重用性很高的代码放入工具箱呢。答案当然是可以的。

你可以选中你的代码,然后拖入工具箱的空白处,你的代码就保存到工具箱了。就像你将控件拖 入代码页面一样,也可以将代码拖入工具箱中。以后你就可以像使用控件一样使用重用的代码。这是非常方便的。而且工具箱的内容不会因为你关闭VS而消失,在 你下次打开VS的时候工具箱同样保存了你的代码。如果你需要查看工具箱保存的代码而又不想拖到代码页面中,你只需要将光标停留在工具箱的代码图标上面。如 图所示:

将代码放入工具箱

 

6.格式化代码


 

这个很重要,即使VS在你每次打完“;”回车之后会自动格式化代码。但是难免代码的格式会有所变化,譬如粘贴一段代码之后,代码的格式往往会受到影响。所以,这个还是很有必要知道的。

格式化部分代码:选中代码->Ctrl+K,F。或者Ctrl+E,F

格式化整个文档:编辑->高级->设置文档的格式。或者 Ctrl+K,D。或者Ctrl+E,D

 

7.切换设计/代码图示


 

ASP.NET页面切换(HTML): Ctrl+PgUp/Ctrl+PgDn

在windows窗体切换:F7/Shift+F7 (代码/设计)

 

8.查找错误代码。


当错误列表有错误或者警告提示时,你可以双击这个错误或提示,就可以跳转到错误或警告的语句前。

 

9.跳转到指定行号


 

如果代码很多的时候,这是很有用的。在ASP.NET编程的时候,很多错误只有在运行网站的时候才能发现,而这个错误又没被在错误列表提示的时候,你就可以使用这个小技巧跳到错误的代码前面。

双击右下角状态栏的行号,会跳出一个行号跳转窗体。或者快捷键Ctrl+G调出窗体。当然,还可以从菜单栏点击“编辑->跳转..”使用这个功能。

跳转到指定行号

 

10.快速查找


光标停留在需要查找的词上面,使用快捷键Ctrl+F3可以跳转到下一个相同的词。按Shift+F3可以往上查找。

 

11.查找“{/}”


 

查找:你是否很烦恼有些对应的标记找的到头找不到尾,找得到尾不知道那个是头。当你把光标放在“{”的前面,VS会将相对应的”}“标记起来。你也可以将光标停留在“}”的后面,可以达到同样的效果。

查找“{/}”

 

12.查找和替换


 

当你想查找/替换掉某个字符串的时候,你可以按快捷键“Ctrl+F”或者“Ctrl+H”,进行这一操作。另外VS支持正则表达式和通配符。

如果你想从整个项目进行查找/替换,你需要使用快捷键“Ctrl+Shift+F”或者“Ctrl+Shift+H”。当然这一切都可以在菜单栏找到。“编辑->查找和替换”。当你想中止全局替换的时候,你可以使用快捷键“Ctrl+Pause Break”。

查找和替换

 

13. 书签


 

书签是很有用的功能,用过Chrome的都知道。在VS当中,书签同样适用。它可以帮你保存位置,以便你写代码。

放置书签:Ctrl+B,T

上一个书签:Ctrl+B,P

下一个书签:Ctrl+B,N

删除所有书签:Ctrl+K,C

除此之外,VS还提供了其它的书签操作。

书签

 

14.跳转到定义


 

当你查看代码的时候,往往需要去查看原函数,这是难免的。但是千万不要去手动寻找函数。这效率往往是很低的。你可以右键该函数,选择跳转到定义即可。当然你也可以使用快捷键F12

跳转到定义

 

15.以文本形式插入外部文本


 

菜单->编辑->将文件作为文本插入

好处是,你不需要打开文件去复制粘贴。

将文件以文本的形式插入

 

或许这些小技巧你早就知道了。亦或是,你觉得这些技巧根本没啥用。当然,我们最主要的任务还是去编码而已。没有必要将心思花在这上面。但是,当你习惯使用这些小技巧的时候,这为你带来的收益觉得不会让你有所失望的。有些技巧,个人认为还是很有必要掌握的。

总之,捡你想捡的吧,让其他人折腾去吧。