[转载]利用多态重构为带参方法

mikel阅读(941)

[转载]利用多态重构为带参方法 – 张逸:晴窗笔记 – 博客园.

《重构之美》之二

logo_white 我在阅读遗留代码时,经常发现存在这样一种情形。在一个类中存在两个方法,它们做了相似的工作,区别仅在于方法内部某些对象的类型。例如:
public class WorkSheet{
public void fillHeader() {
Header header = createHeader();
for (String title:header.getTitles()) {
fillCell(title);
}
}
public void fillBody() {
CellGroup cellGroup = createCellGroup();
for (Cell cell:cellGroup.getCells()) {
fillCell(cell.getText());
}
}
}

方 法fillHeader()和fillBody()的目的都是从对象中获得字符串数组,然后将其填充到单元格中。区别在于,获得字符串数组的对象并不相 同。前者为Header对象,后者为CellGroup对象。我们可以为其提供一个抽象的接口,以实现代码的有效重用:
public interface TextDataSource {
public String[] getTextArea();
}

然后,让Header和CellGroup类均实现该接口。由于CellGroup并没有直接定义返回字符串数组的方法,而是通过返回的Cell对象获得text值,因此,需要将这部分实现封装到getTextArea()方法中:
public class Header implements TextDataSource {
@Override
public String[] getTextArea() {
//这里的实现即为原有的getTitles()实现
//可以保留原有的方法,并在本方法中指向该方法
//也可以利用Rename Method手法,直接更名该方法
}
}
public class CellGroup implements TextDataSource {
@Override
public String[] getTextArea() {
List<String> textArea = new ArrayList<String>();
for (Cell cell:this.getCells()) {
textArea.add(cell.getText());
}
return textArea.toArray();
}
}

现在,就可以重构原有的WorkSheet类了。
public class WorkSheet{
public void fillSheet(TextDataSource dataSource) {
for (String text:dataSource.getTextArea()) {
fillCell(text);
}
}
}

具体需要填充什么内容,可以在调用fillSheet()方法时,根据传入的参数对象来决定。经过重构之后,WorkSheet类中的重复代码得到了移除,且具有了更好的扩展性。

这一重构手法与Parameterize Method要解决的坏味道相似,同样对相似方法提取了共同的参数,但实现的本质完全不同。它利用了多态的原理,通过对抽象方法体中的相似对象,抹去了不同类型对象之间的差异性,使得方法体中的相似结构能够被抽取出来。

我将这一重构手法命名为Parameterize Method by Polymorphism。让我仿照Martin Fowler的风格,给出这一重构方式的作法(Mechanics):
1)新建一个接口,并使原有方法中的差异对象实现该接口。
2)如果原有对象的方法与该接口定义的方法签名不同,则运用Rename Method。
3)编译。
4)新建一个参数为新接口类型的方法,使它可以替换先前所有的重复方法。
5)编译。
6)将对旧方法的调用替换为对新函数的调用。
7)编译,测试。
8)对所有旧方法重复上述步骤,每次替换后,修改并测试

如果在调用新方法时,发现创建参数实参对象是一件麻烦事,可以考虑在原有类中引入一个创建新接口对象的工厂方法,从而对复杂的创建逻辑进行封装。

[转载]浅析jQuery的链式调用 之 each函数

mikel阅读(1067)

[转载]浅析jQuery的链式调用 之 each函数 – 棍子上的萝卜 – 博客园.

如果对于JQuery的$()包装器函数还不是很清楚,请先参阅我的上一篇日志:浅析jQuery的链式调用

话说回来,虽然JQuery 让学习前端技术的越来越多了起来,(本人就是因为学校图书馆偶然间遇到了一本jQuery基础教程(二)开始想深入的学习前端技术),关于jQuery的 博文甚至多于JavaScript,它让编程的门槛大大的降低了,但是它隐藏了太多细节了,形 如$(‘#id’).append(‘<p>xxx</p>’).clone().appendTo(x).end().css(…)……………. 这样操作的模式已经很难找到常规JavaScript的影子。浏览器的差异仿佛一去就不见了踪影,我不认为大部分都能在这安逸的环境再重新回到考虑 JavaScript如何在不同浏览器能表现一致等问题,是福是祸真的很难说。

接下来言归正传…..说正经的

由于$()函数返回的是一个包裹了原生dom对象数组的对象,并且在此对象原型上扩展的函数都是为了操作原生的dom对象,所以少不了循环遍历的操 作,熟悉jQuery库的人都知道有个jQuery.each()函数 ,大部分的涉及jquery对象的函数都会用到这个函数: 简单的实现应该是形如这样:

(再次重申,只是简单的实现原理,不要考虑具体的功能性问题)

代码

if (!window['$']) window['$'] = window['jQuery']; /*这里以上区域为上一篇文章的闭包内的内容 * 定义jQuery.each 根据传入对象来执行操作 * @param {Object} obj * @param {Object} func *简单起见我只考虑了数组以及jQuery对象两种情况,跟上一篇一样,原理应该是一致的 */ window['jQuery']['each']=function(obj,func){ if(obj.constructor==Array){ for(var i=0;i<obj.length;i++){ func.call(obj[i],i,obj[i]);//可以看到传入的func 应该是形如function(i,item) i代表遍历到的下标,item则代表在数组中遍历到的对象 } }else if(obj.elements&&obj.elements.constructor==Array){//这里采用了传说的鸭子法则,而不是判定它是不是jQuery的实例 只要你有Array类型的elements我就对你进行操作 for(var i=0;i<obj.elements.length;i++){ func.call(obj.elements[i],i,obj.elements[i]);//可以看到传入的func 应该是形如function(i,item) i代表遍历到的下标,item则代表在数组中遍历到的对象 } }else{ return null; } }

在这个函数的基础之上可以开始扩充_jQuery的prototype了;首先就是先写一个包装器对象可以直接调用的方法each:(这个并不是重复),然后通过调用这个each函数可以完成对对象数组的遍历,

比如:

代码

//写在闭包内 注意将昨天命名冲突了的jQuery构造函数名称改为_jQuery _jQuery.prototype = { each: function(func){ jQuery.each(this, func); return this; }, attr: function(key, value){
 //示例定义这个set与get于一身的操作属性的函数

if (arguments.length == 0) {
return null;
}
else
if (arguments.length == 1) {
return this.elements[0].getAttribute(key);
}
else if(arguments.length == 2){
this.each(function(i, item){
//这里可以看到重新定义each方法的好处一:精简代码 ,二:在内部函数中this仍然是指向调用的包装器对象而不是window
item.setAttribute(key, value);
})

}
}
/*
* 这里可以开始引入其他方法
*/

}

接下来做几个简单的测试:(还是上一篇的测试html

输入:

var k= $(‘#header’);
consoles.write(k.attr(‘title’,’test title!’).attr(‘title’));     //链式调用

输出:

test title!

依此类推,依靠each方法可以有效的扩充包装器的方法。

之前说的影响到jQuery的链式调用的要点有三点,事实事后一想完全不是那么简单,jQuery内部代码的维护感觉并不比有些库好,虽然至少在操 作上来讲使用起来非常顺手(当然只是针对一些小的操作,大的项目一时也无法接触到,也不好跟着一些大大人云亦云). 不过就算仅仅从遍历操作来看, 也可以看到其实这个库只能依托细化的插件,扩充下去只会显得臃肿不堪。

注:如果有仔细剖析了jquery源码的人肯定 会对我如此拙劣的所谓的实现嗤之以鼻,我的确只是看了几本比如javascript dom高级程序设计 以及javascript 高级程序设计 设计模式等好书之后有感而发而已,可能跟具体的jquery的实现有很大的落差,可以的话希望可以指正一下。

[转载]选择结构(if..else..,switch,try..catch..)的拆分

mikel阅读(961)

[转载]选择结构(if..else..,switch,try..catch..)的拆分 – 智慧掩盖真相 – 博客园.

编程中经常遇到很多条件,以及条件套条件的情况,以至于一个方法会写得非常地长。有多种方法可以规避这个问题。比如反射,策略模式,表驱动等等。先抛开这些方法不讲,从根本需求来探索这个过程。

一个switch结构可能是这样:

swicth(case)

case 1:

//do1

break;

case 2:

//do2

break;

…..

这里注释的do部分代码可能会是很多很多行,以及嵌套switch,if结构。

进一步,这个模型演化成

swicth(case)

case 1:

do1();

break;

case 2:

do2();

break;

…..

do1(){}

do2(){}

也就是将里面的代码模块化。这个方法有效的减小了一个方法的代码长度。实际上这就是一个映射关系的调用。建立映射关系,用Hash结构和delegate就可以避免使用switch了。

delegate void funtionptr();

Dictionary<int, funtionptr> dict = new Dictionary<int, funtionptr>();

dict.Add(1,do1);

dict.Add(2,do2);

int parm = 0;

if(dict.ContainKey(parm)){

dict[parm].Invoke();
}

do1(){}

do2(){}

这个方法实际上就是表驱动,因为C#中一般不用指针,因此用delegate代替了指针的作用。而java中既没有指针也没有delegate怎么办呢?那就用接口模拟指针。

(这里就用C#的语法了,不使用JAVA了)

interface FactionFace{

void do();
}

class FactionFaceImpl1 : FactionFace{

public void do(){}
}

class FactionFaceImpl2 : FactionFace{

public void do(){}
}

Dictionary<int, FactionFace> dict = new Dictionary<int, FactionFace>();

dict.Add(1,new FactionFaceImpl1());

dict.Add(2,new FactionFaceImpl2());

int parm = 0;

if(dict.ContainKey(parm)){

dict[parm].do();
}

可以看出,实际上,上面的代码就是策略模式的简单实现了。(OH~~原理策略模式这么就来的~~)

可以看出,这里实际上是依靠一个字典来维护条件和调用的关系的。那如果没有这个字典来维护怎么办呢?代码再次演化:

interface FactionFace{

void do();
}

class FactionFaceImpl1 : FactionFace{

public void do(){}
}

class FactionFaceImpl2 : FactionFace{

public void do(){}
}

string parm = “FactionFaceImpl2”;

FactionFace ff = Type.GetType(parm) as FactionFace;

ff.do();

OH~~这就是简单的反射了。再复杂一些就可以搞成工厂模式,插件模式了。

可以看出,用字典来维护映射关系就可以避免使用反射。(这个是不是一般规律,贫道没本事证明,起码这里是适应的。)

再来看try…catch…

delegate void forException(Exception ex);

public void forAException(Exception ex) {Console.WriteLine(“AException”);}
public void forAAException(Exception ex) {Console.WriteLine(“AAException”);}
public void forBException(Exception ex) {Console.WriteLine(“BException”);}

public void test() {

Dictionary<int, forException> dict = new Dictionary<int, forException>();

dict.Add(0, forAException);

dict.Add(1, forAException);

dict.Add(11, forAAException);

dict.Add(2, forBException);

try {

throw new AAException();

} catch (Exception ex)

{

if (ex is OException) {

OException eex = ex as OException;

if (dict.ContainsKey(eex.Index)) {

dict[eex.Index].Invoke(ex);

}

}

}

}

public class OException : Exception {public virtual int Index { get { return 0; } }}
public class AException : OException {public override int Index {get {return 1;}}}
public class AAException : AException {public override int Index {get {return 11;}}}
public class BException : OException {public override int Index {get {return 2;}}

这里为啥要有个Index属性呢?其实就是建立个对应的关系,而不是靠名字去维护。去掉这个属性,直接使用ex.GeType()然后获取名字类名也一样可以建立对应关系。

[转载]我在项目中如何应用LinqToSql数据库事务

mikel阅读(1129)

[转载]我在项目中如何应用LinqToSql数据库事务 – RyanDing – 博客园.

本文主要涉及LinqToSQL数据库事务相关,文章不足之处,欢迎您指出。

一、回顾T-SQL中的事务机制:


代码如下:

1 /*加入事务机制后的存储过程*/ 2  create procedure sp_example 3 @param1 int = null, 4 @param2 nvarchar(20) = null 5  as 6 begin tran tranName /*sql 事务的加入*/ 7 insert into table0 (col1,col2,col3) values ('value1','value2','value3') 8 update table1 set column1 = @param1 where 1=1 9 --删除table2中一条已经被其他外键表引用的记录,此时会报sql引用错误 10   delete from table2 where column3 = @param1 11 insert into table3 (col1,col2) values ('value1','value2') 12 if(@@error =0) 13 commit tran tranName 14 else 15 rollback tran tranName 16  go

以上代码是一个具备事务机制的简单存储过程,需要指出的是当上述代码执行到第十行时,此时如果该存储过程未加入事务机制那么势必会导致第10行之前已经被影响的数据库记录也不会被还原(rollback)。这样的代码是我们不想见到的,所以事务在复杂的商业逻辑中保持数据的完整性还是尤为重要的。

二、LinqToSQL 中的SubmitChanges内置事务机制:


众所周知LinqToSql 中我们的事务机制代码变的相对简单了,如以下代码:

1 public bool DeleteDepartment(int departmentId) 2 { 3 try 4 { 5 DataContext.SystemUser.DeleteOnSubmit( 6 DataContext.SystemUser.FirstOrDefault(u => u.DepartmentID == departmentId)); 7 8 DataContext.Department.DeleteOnSubmit( 9 DataContext.Department.FirstOrDefault(f => f.DepartmentID == departmentId)); 10 11 //事务机制被封装到SubmitChanges方法内 12   DataContext.SubmitChanges(); 13 14 return true; 15 } 16 catch 17 { 18 return false; 19 } 20 }

上诉代码很容易理解,在LinqToSql 为了删除一条部门记录。我们首选要删除该部门被引用的外键表记录这里是员工表,(上诉代码只是为举例用,实际开发中是不会有此种业务的)当外键记录都删除 成功后代码执行到第8行,这时才能能删除部门对象。否则报SqlException外键引用无法删除部门记录。我们唯一需要做的只是将 DataContext.SubmitChanges();这句放在所有Linq操作数据库语句之后这样就可以调用数据库事务机制了。比如当第5行代码执 行时SystemUser还被Order表引用。当SubmitChanges执行时会自动调用transaction.Rollback()方法回滚 SubmitChanges()之前的所有被影响的数据库记录,详情请阅Reflector。

三、在LinqToSql中SubmitChange内置事务机制无法满足的业务场景:


当程序需要处理更多更复杂的商业逻辑时,我发现光凭SubmitChange方法自带的事务机制是远远不能满足的。

该场景描述如下:

如果为完成某一个特定的业务,需要在程序中使用多次的SubmitChanges方法。比如我们要做一个库存相关业务,该业务是由两张表组成:主表+从表。分别为主表:Depot和从表:DepotDetail 两张表。两张表关系如下:

当我们通过LinqToSql生成一个库存对象时其实应先生成Depot对象后再将生成Depot对象的DepotID(主键)传递到 DepotDetail对象中用于生成库存明细表记录。也就说为了生成库存明细表记录我们必须先生成Depot主表,那样就不得不先调用 SubmitChanges方法,当保存DepotDetail对象时还需要再一次调用SubmitChanges()方法。因为调用了多次 SubmitChanges方法所以SubmitChanges内置的回滚机制已经不能满足需要了。

四、TransactionScope的应用:


我们需要引用.net 的System.Transactions 类库使用TransactionScope类,帮我们更有效的处理数据库事务机制。对TransactionScope进行封装,代码如下:

DBTransactionExtension

1 public static class DBTransactionExtension 2 { 3 public static bool Excute(out string errorMsg, params Action[] actions) 4 { 5 //使用ReadCommitted隔离级别,保持与Sql Server的默认隔离级别一致 6   return Excute(out errorMsg, IsolationLevel.ReadCommitted, null, actions); 7 8 } 9 10 public static void Excute(out string errorMsg, IsolationLevel level, params Action[] actions) 11 { 12 Excute(out errorMsg, level, null, actions); 13 } 14 15 public static void Excute(out string errorMsg, int timeOut, params Action[] actions) 16 { 17 Excute(out errorMsg, IsolationLevel.ReadCommitted, timeOut, actions); 18 } 19 20 public static bool Excute(out string errorMsg, IsolationLevel level, int? timeOut, params Action[] actions) 21 { 22 errorMsg = ""; 23 if (actions == null || actions.Length == 0) 24 return false; 25 TransactionOptions options = new TransactionOptions(); 26 27 options.IsolationLevel = level; //默认为Serializable,这里根据参数来进行调整 28   29 if (timeOut.HasValue) 30 31 options.Timeout = new TimeSpan(0, 0, timeOut.Value); //默认60秒 32   33 using (TransactionScope tran = new TransactionScope(TransactionScopeOption.Required, options)) 34 { 35 try 36 { 37 Array.ForEach<Action>(actions, action => action()); 38 tran.Complete(); //通知事务管理器它可以提交事务 39   return true; 40 } 41 catch (Exception ex)//回滚事务 42   { 43 errorMsg = ex.Message; 44 return false; 45 } 46 } 47 } 48 49 }

调用DBTransactionExtension代码如下:

1 private void SaveDepot(Depot depot) 2 { 3 DataContext.Depots.InsertOnSubmit(depot); 4 5 if (false)//TODO:保存库存主表前的逻辑判断,条件不满足时候调用 throw new exception执行TransactionScope回滚。 6   throw new Exception("自定义错误提示内容,最终由事务获取错误信息后抛给UI"); 7 8 //条件满足则调用SubmitChanges 9   DataContext.SubmitChanges(); 10 DepotDetail depotDetail = new DepotDetail(); 11 depotDetail.DepotID = depot.DepotID; 12 depotDetail.Count = 100; 13 14 DataContext.DepotDetails.InsertOnSubmit(depotDetail); 15 //又调用了一次SubmitChanges 16   DataContext.SubmitChanges(); 17 } 18 public Depot InvokeTransaction(Depot depot, out string errorMsg) 19 { 20 try 21 { 22 DBTransactionExtension.Excute(out errorMsg, () => SaveDepot(depot)); 23 return depot; 24 } 25 catch (Exception ex) 26 { 27 errorMsg = ex.Message; 28 return null; 29 } 30 }

根据上述调用方法,我们已经可以在LinqToSql中灵活的使用数据库事务了。

五、TransactionScope类使用的注意事项:


使用TransactionScope时如果调用多次LinqToSql的DataContext对象实例(等同调用多个数据库连接),那么我们必须开启MSDTC否则事务不能正常工作,具体请阅MSDTC开启。注:TransactionScope 适用于多种 Data Provider 比如 oracle 、OleDB、ODBC等。

最后希望本篇文章能给您带来帮助。

[转载]在Ajax中使用Flash实现跨域数据读取

mikel阅读(1163)

[转载]在Ajax中使用Flash实现跨域数据读取 – 阳光下的星星 – 博客园.

Ajax的跨域操作一直是一个难题,现目前主要的解决方法主要有:

1、JSONP(需要在服务器端支持)

2、IFrame(仅能在子域间操作)

3、页面代理(这个有点万能)

4、Access-Control-Allow-Origin(新版浏览器支持,需要在服务器端设置Header)

今天,小子再提供一种使用Flash进行跨域操作的方法。众所周之,其实Flash的跨域操作也是有限制的,不过,Flash的跨域配置比简单,只需要 在站点根目录下放置crossdomain.xml即可。至于crossdomain.xml的用法,大家可以看看这篇文章:http://www.44sh.com/html/23423.aspx 这篇文章。

对于,这一系列操作,小子已经封装为JQuery的插件形式,将在文章末尾附上下载地址。

使用方法:

1、在页面引入JQuery与ajaxf.js文件。

2、在页面插入以下代码。首先将Flash安装到页面中。

$(document).ready(function() { $.ajaxf.install('/Files/zsea/flash4ajax.swf'); });

3、调用方法获取远程数据。

下面详细说一下插件所提供的方法:

$.ajaxf.install(swfpath)

安装Flash到页面,可指定Flash的路径。

$.ajaxf.ready(function(){})

Flash加载完后执行的函数。

$.ajaxf.isReady()

返回Flash是否已经加载完成。返回值为Boolean

$.ajaxf.ajax(p)

原生的ajax调用支持,p为一个对象,包括:callback,回调函数;type,方法类型,支持 json,text,xml,script;url,读取数据的地址;method,请求的方法,支持get,post;data,发送的数 据;contentType,请求的contentType头;header,Object对象,附加的请求头。

$.ajaxf.get(url, data, callback, type)

通过get方式获取数据

$.ajaxf.post(url, data, callback, type)

通过post方式获取数据

下面几个大家一看名称就知道函义就不多做解释

$.ajaxf.getText(url, data, callback)

$.ajaxf.getJSON(url, data, callback)

$.ajaxf.getScript(url, data, callback)

$.ajaxf.postJSON(url, data, callback)

$.ajaxf.postText(url, data, callback)

下面演示一下如何获取获取优酷首页的HTML代码。

01 <script type="text/JavaScript">
02 $(document).ready(function() {
03 $.ajaxf.install('/Files/zsea/flash4ajax.swf');
04 $("#fdemo_get").click(function() {
05 $.ajaxf.getText("http://www.youku.com/", '', function(r) {
06 $("#fdemo").val(r);
07 });
08 });
09 });
10 </script>
11
12 <textarea id='fdemo' style='width: 500px; height: 300px;'></textarea>
13 <br />
14 <input type="button" value="获取数据" id='fdemo_get' />

可惜,小子不知道怎么在园子里运行页面上的代码。只有各位朋友自己测试了。

下载地址:http://files.cnblogs.com/zsea/Ajaxf.zip

[转载]深入理解ASP.NET MVC(7) - P_Chou Go deep and Keep learning - 博客园

mikel阅读(1114)

[转载]深入理解ASP.NET MVC(7) – P_Chou Go deep and Keep learning – 博客园.

Action的定位

再次回到Controller的ExecuteCore方法,回到action调用的入口:

if (!ActionInvoker.InvokeAction(ControllerContext, actionName))

这里的ActionInvoker是个IActionInvoke,它无疑是负责了所有action的调用逻辑,MVC中默认实现这个接口的是ControllerActionInvoke。可以想象ControllerActionInvoke面临的第一个问题是如何找到与actionName对应的action。

首先,并不是所有的action都合法,需要符合下面条件:

1、Public,非static

2、非Controller或其基类定义的方法,比如ToString(),GetHashcode()

3、不能是特殊名字System.Reflection.MethodBase的IsSpecialName标志,例如构造函数,属性包装器,事件包装器

4、具有泛型参数的方法尽管会被认为是action,但框架试图执行这样的action时会只是抛出异常。

其次,MVC为action的查找过程设计了两种“选择器”

  • ActionNameSelectorAttribute
  • ActionMethodSelectorAttribute

如果对此比较迷茫的话,看了他们的派生特性,你也许就明白了:

image

image

我们常用ActionNameAttribute来“伪装”;用HttpPostAttributeHttpGetAttribute来说明某个action符合的特定的http行为;用NoAction隐藏我们不想“暴露给url”的方法…其实这些行为的原理便是利用MVC为我们提供的action选择的机制。下面这张图说明了这个过程的完成逻辑:

image

ContollerActionInvoker.InvokeAction在内部创建一个ReflectedControllerDescriptor,并调用其FindAction方法,这个对象的FindAction负责调用一个ActionMethodSelector对象实现上述逻辑,并在最后返回一个ReflectedActionDescriptor。 ActionMethodSelector对象故名思意,原理上用反射执行上述过程,执行的结果要么返回一个MethodInfo,要么返回null,要 么抛出异常。ReflectedControllerDescriptor对返回值判断,如果是null,则也返回null;如果是 MethodInfo,则将其包装成ReflectedActionDescriptor返回。对于外面的InvokeAction方法来说只要 ReflectedControllerDescriptor返回的ReflectedActionDescriptor为空就返回false,否则继 续。Controller负责对返回false的情况调用HandleUnknownAction,该方法默认产生404错误,我们可以重写它。

在回过头来看看上图的逻辑,不难得到这样一个事实:ActionNameSelectorAttribute优先于ActionMethodSelectorAttribute,ActionMethodSelectorAttribute优先于没有ActionMethodSelectorAttribute特性的方法

更多关于这部分源码的细节,可以参考:通过源代码研究ASP.NET MVC中的Controller和View(六)

MVC的内建过滤器

ActionDescriptor(ReflectedActionDescriptor)返回后,ContollerActionInvoker开始执行action,但是在执行action前还有一些额外的步骤,那就是过滤。以下四个是MVC内建的过滤接口:

  • IActionFilter
  • IAuthorizationFilter
  • IExceptionFilter
  • IResultFilter

这四个Filter会被ControllerActionInvoker框架综合考量,下面的伪代码说明了ControllerActionInvoker处理他们的逻辑顺序:

try
{
	//依次执行IAuthorizationFilter
	Run each IAuthorizationFilter's OnAuthorization() method
	//所有的IAuthorizationFilter都返回null而不是ActionResult
	if(none of the IAuthorizationFilters cancelled execution)
	{
		Run each IActionFilter's OnActionExecuting() method
		Run the action method
		//注意到这里按照反转顺序进行
		Run each IActionFilter's OnActionExecuted() method (in reverse order)
		Run each IResultFilter's OnResultExecuting() method
		Run the action result
		//注意到这里按照反转顺序进行
		Run each IResultFilter's OnResultExecuted() method (in reverse order)
	}
	//有IAuthorizationFilter返回ActionResult
	else
	{
		Run any action result set by the authorization filters
	}
}
catch(exception not handled by any action or result filter)
{
	//注意到IExceptionFilter处理的异常只在这个try catch块中
	Run each IExceptionFilter's OnException() method
	Run any action result set by the exception filters
}

需要注意的事项,在代码中都已经给出注释,在后面一节中,将讨论其中的某些细节。

MVC中与这四个接口相关的特性如下:

image

我们熟悉的有AuthorizeOutputCacheChildAction等。曾经,对于这些形形色色的Attribute,我都不能很好的理解和归类,现在总算是找它们各自的归宿了。

下一节,将针对这四个过滤器接口深入讨论一些细节问题。

[转载]使用PostSharp在.NET平台上实现AOP

mikel阅读(1426)

[转载]使用PostSharp在.NET平台上实现AOP – EricZhang’s Tech Blog – 博客园.

摘要

本文首先介绍AOP(面向方面编程)的相关概念及理论,然后介绍如何使用PostSharp框架在.NET平台上实现AOP,最后对PostSharp的机制及AOP的优劣进行一个简单的分析。

AOP(Aspect-Oriented Programming)

AOP的基本定义及作用

根据维基百科的定 义,“AOP(Aspect-Oriented Programming)是一种将函数的辅助性功能与业务逻辑相分离的编程泛型(programming paradigm),其目的是将横切关注点(cross-cutting concerns)分离出来,使得程序具有更高的模块化特性。AOP是面向方面软件开发(Aspect-Oriented Software Development)在编码实现层面上的具体表现(面向方面软件开发AOSD是一个囊括面向方面分析、面向方面设计和面向方面编程等一系列概念的完整 工程系统——笔者注)。AOP包括编程模型和具体用于实现AOP的框架两部分。”

下面对上文提到的定义进行一些解释。

在当前大多数支持面向对象的编程语言中(例如C#,Java等),函数(Function)是表述程序功能的最小单元,而一个函数的代码层面往往同 时含有核心业务逻辑和辅助性功能。核心业务逻辑指一个函数本身主要要实现的业务功能,例如在一个在线电子商务系统中,“PlaceOrder”函数其核心 业务逻辑是“下订单”,而“UpgradeMember”函数其核心业务是“提升一个会员的等级”。但是,一个函数除了核心业务代码外,往往还会有一些辅 助性功能代码,如事务处理、缓存处理、日志记录、异常处理等等。而这些辅助性功能一般会存在于大多数甚至所有业务函数中,即形成AOSD中所谓的横切关注 点,如图1所示。

image

图1、横切关注点示意

横切关注点的存在,造成了如下几个问题。

  • 代码编写和维护困难。

横切关注点不仅横切各个函数,还可能在不同类甚至不同工程间横切,使得同一个辅助功能(如事务处理)分散到各处,如果要增加新函数时要时刻注意别忘了添加所有需要的横切代码。另外,如果需要对其进行修改,则需要到所有被横切的函数中修改,维护难度极大。

  • 引入大量冗余代码

由于同一个辅助性功能的代码几乎是完全相同的,这样就会令同样的代码在各个函数中出现,引入了大量冗余代码。

  • 降低代码质量

横切关注点令核心业务代码和辅助性代码杂糅纠缠在一起,破坏了业务函数代码的纯净性和函数职责的单一性,引入了大量繁杂的代码和结构,使得代码质量下降。

所以,AOP的核心思想就是在编写代码时将横切关注点分离出来,形成单独的模块,单独编写和维护,不再分散到各业务函数,使得业务函数仅包含核心业 务代码,从而解决以上问题。而在程序编译或运行时,通过某些手段(下文介绍)令独立的横切关注点代码可以与核心业务代码自动协作运行,完成本身需要的功 能。

AOP相关术语

方面(Aspect)

一个Aspect指上文提到的横切关注点在编程中的具体实现,它包含一个横切关注点所需要实现的具体辅助功能。具体到代码中,Aspect可能会被实现为一个Class,一个Function或一个Attribute。

连接点(Join Point)

连接点指一个业务函数代码中的一个位置或时机,在这个位置或时机允许Aspect代码插入执行。常见的连接点有进入函数执行业务代码前时、执行完全部业务代码离开函数前、当有异常发生在异常处理代码执行前等等。

织入(Point Cut)

织入指将指定的Aspect代码插入指定连接点,使得横切代码与业务代码交合在一起。

连接模型(JPM, Join Point Model)

JPM主要是面向方面语言(如AspectJ)或面向方面框架的语义模型。主要包含以下三点:有哪些可用连接点,如何指定连接点以及如何织入。

AOP的实现方式

一般来说,在纯编译型语言(如C、C++)等语言中实现AOP非常困难,必须完全从编译器角度入手。本文主要讨论托管型语言(如C#,Java)中AOP的实现方式。AOP的主要实现方式有编译时AOP和运行时AOP两种,下面分别介绍。

编译时AOP(静态织入)

编译时AOP的实现思想是给语言的编译器做扩展,使得在编译程序的时候编译器将相应的Aspect代码织入到业务代码的指定连接点,输出整合的结果。图2是编译时AOP的示意图(以.NET平台为例)。

image

图2、编译时AOP示意图

如图2所示,当使用静态织入时,带AOP扩展的编译器会在编译时将Aspect代码织入业务函数代码,形成整合后的IL,然后交由CLR运行。

运行时AOP(动态织入)

运行时AOP如图3所示。image

图3、运行时AOP的示意图

如图3所示,运行时AOP的实现方式是将扩展添加到运行虚拟机而不是编译器。Aspect和业务代码分别独立编译,而在运行时由虚拟机在必要时进行织入。

PostSharp

PostSharp简介

PostSharp是一个用于在.NET平台上实现AOP的框架,是我比较常用的一个AOP框架,官方网站http://www.sharpcrafters.com。目前最新版本为2.0,但是2.0的license不再免费,因此个人建议下载1.5版,同时下文都是基于PostSharp1.5。

PostSharp使用静态织入方式实现AOP,其连接点非常丰富,使用简单,而且相对其它一些.NET平台上的AOP框架来 说,PostSharp较为轻量级,但是功能却一点也不逊色,因此是我比较喜欢的一个AOP框架。更多关于PostSharp的介绍请参看其官方网站

另外使用PostSharp与其它框架不太一样的是一定要下载安装包安装,只引用类库是不行的,因为上文说过,AOP框架需要为编译器或运行时添加扩展。

使用PostSharp实现AOP示例

这一节将通过一个例子演示如何使用PostSharp在.NET平台上实现AOP。这个例子将通过AOP为核心业务函数增加日志记录功能。

新建项目

首先新建一个C#的WinForm应用程序,如图4所示,这里将工程命名为“PostSharpExample”。

image

图4、新建项目

编写核心业务函数

首先我们来编写核心业务。当然这里不存在真正的业务,我们只是模拟一个而已。将要模拟的核心业务是预定房间。先构建一个如图5所示的简单UI。

image

图5、UI界面

下面我们为项目增加一个“CoreBusiness”类,并在其中添加“Subscribe”方法。代码如下:

01 using System;
02
03 namespace PostSharpExample
04 {
05 public class CoreBusiness
06 {
07 public static void Describe(string memberName, string roomNumber)
08 {
09 System.Windows.Forms.MessageBox.Show(String.Format("尊敬的会员{0},恭喜您预定房间{1}成功!", memberName, roomNumber), "提示");
10 }
11 }
12 }

可以看到,这里Subscribe方法仅仅是输出一个提示框。当然,在真正项目中这种输出型代码不应该写在业务逻辑中,这里这样写主要是为了演示方便。然后,我们在Form1中调用Subscribe业务方法:

01 using System;
02 using System.Windows.Forms;
03
04 namespace PostSharpExample
05 {
06 public partial class Form1 : Form
07 {
08 public Form1()
09 {
10 InitializeComponent();
11 }
12
13 private void BTN_SUBSCRIBE_Click(object sender, EventArgs e)
14 {
15 if (!String.IsNullOrEmpty(TXB_NAME.Text.Trim()) && !String.IsNullOrEmpty(TXB_ROOM.Text.Trim()))
16 CoreBusiness.Describe(TXB_NAME.Text.Trim(), TXB_ROOM.Text.Trim());
17 else
18 MessageBox.Show("信息不完整","提示");
19 }
20 }
21 }

运行程序就可以看到相应的效果:

image

图6、预定房间成功演示效果

使用AOP增加日志记录功能

现在加入我们要为程序添加日志功能,记录业务函数的执行情况。这里我们假定需要将日志记录到纯文本文件中,首先我们完成日志记录工具类,LoggingHelper。

01 using System;
02 using System.IO;
03
04 namespace PostSharpExample
05 {
06 class LoggingHelper
07 {
08 private const String _errLogFilePath = @"log.txt";
09
10 public static void Writelog(String message)
11 {
12 StreamWriter sw = new StreamWriter(_errLogFilePath, true);
13 String logContent = String.Format("[{0}]{1}", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"), message);
14 sw.WriteLine(logContent);
15 sw.Flush();
16 sw.Close();
17 }
18 }
19 }

如果不使用AOP,则我们要为包括Subscribe在内的每一个方法在核心业务代码的前后插入日志记录代码(Writelog),我们看看使用 PostSharp如何将这种横切关注点分离出来。因为要使用PostSharp,所以要先添加对PostSharp库文件的引用,安装过 PostSharp后,在系统可引用项中会多出“PostSharp.Laos”、“PostSharp.Public”和 “PostSharp.AspNet”,这里我们做的是Winform程序,所以只需添加对“PostSharp.Laos”和 “PostSharp.Public”的引用即可。

下面我们就要写Aspect了,PostSharp的Aspect是使用Attribute实现的,下面是我实现的日志记录Aspect代码。

01 using System;
02 using PostSharp.Laos;
03
04 namespace PostSharpExample
05 {
06 [Serializable]
07 [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
08 public sealed class LoggingAttribute : OnMethodBoundaryAspect
09 {
10 public string BusinessName { get; set; }
11
12 public override void OnEntry(MethodExecutionEventArgs eventArgs)
13 {
14 LoggingHelper.Writelog(BusinessName + "开始执行");
15 }
16
17 public override void OnExit(MethodExecutionEventArgs eventArgs)
18 {
19 LoggingHelper.Writelog(BusinessName + "成功完成");
20 }
21 }
22 }

我们约定每个Aspect类的命名必须为“XXXAttribute”的形式。其中“XXX”就是这个Aspect的名字。PostSharp中提 供了丰富的内置“Base Aspect”以便我们继承,其中这里我们继承“OnMethodBoundaryAspect ”,这个Aspect提供了进入、退出函数等连接点方法。另外,Aspect上必须设置“[Serializable] ”,这与PostSharp内部对Aspect的生命周期管理有关,具体为什么请参看这里

我们的LoggingAttribute非常简单,就是在进入(Entry)和离开(Exit)函数时分别记录日志到log文件。现在我们把这个Aspect应用到业务方法上:

1 [Logging(BusinessName="预定房间")]
2 public static void Describe(string memberName, string roomNumber)
3 {
4 System.Windows.Forms.MessageBox.Show(String.Format("尊敬的会员{0},恭喜您预定房间{1}成功!", memberName, roomNumber), "提示");
5 }

可以看到,应用Aspect非常简单,就是将相应的Attribute加到业务方法上面。现在我们再运行预定房间程序,结果和上次没什么两样,但是如果我们打开程序目录,会看到多了一个“log.txt”文件,里面记录有类似图7的内容。

image

图7、日志内容

可以看到,我们已经通过AOP实现了日志记录功能。通过AOP将横切关注点分离出来后,日志记录的代码都放在LoggingAttribute里,需要修改只要修改一处即可。同时,业务方法仅含有业务代码,这样大大提高了程序代码的可读性和可维护性。

对PostSharp运行机制的简要分析

上文已经说到,PostSharp使用的是静态织入技术,下面我们分析一下PostSharp是如何实现的。

首先,当安装PostSharp时,它自动为Visual Studio编译器添加了AOP扩展。如果仔细观察PostSharpExample编译信息,会发现有这么两行:

image

图8、PostSharp编译信息

很明显,在.NET Complier编译完成后,下面PostSharp又做了一部分工作,这部分工作就是静态织入的过程。如果我们用.NET Reflector查看PostSharpExample.exe中Subscribe方法的反编译代码,会发现多了很多东西:

image

图9、织入Aspect后的Describe代码(由.NET Reflector反编译)

这些多出来的代码,就是PostSharp静态织入进去的。当然,这些代码在每次编译完成后,PostSharp都会重新织入一次,所以整个过程对程序员是透明的,我们只需维护纯净的业务代码和Aspect代码即可。

使用PostSharp的优点和缺点(即使用AOP的优点和缺点)

总体来说,使用PostSharp,将会带来如下优点:

  • 横切关注点单独分离出来,提高了代码的清晰性和可维护性。
  • 只要在Aspect中编写辅助性功能代码,在一定程度上减少了工作量和冗余代码。

当然,使用PostSharp也不是没有缺点,主要缺点有如下两方面:

  • 增加了调试的难度。
  • 相比于不用AOP的代码,运行效率有所降低。

所以,对于是否引入AOP,请根据项目具体情况,权衡而定。

对于PostSharp的进一步学习

本文只是简要介绍了PostSharp以及实现了一个小例子,并不打算详细完整地介绍PostSharp的方方面面,而只想起到一个抛砖引玉的作用。PostSharp还有非常丰富的功能等待各位学习,因此,如果您对PostSharp十分有兴趣,想进一步学习,请参看PostSharp官方参考文档

相关下载

本文用到的Example请点击这里下载

PostSharp1.5安装包请点击这里下载

[转载]使用jQuery.Ajax向ASP.NET MVC控制器Post数据

mikel阅读(1147)

[转载]使用jQuery.Ajax向ASP.NET MVC控制器Post数据 – Killmyday – 博客园.

JQuery自带了Ajax的函数,你可以使用它从远程服务器下载网页,但问题是,大部分Ajax都是通过GET这种方式与远程服务器交互的。

但如果你想通过Ajax向远程服务器传递数据呢?当然JQuery.Ajax里面有一个参数data参数允许你传递发送到服务器的数据。 但问题是,这个数据默认是采用GET方式,即附加在请求字符串(URL QueryString)后面实现的,也就是说你会受到URL最大不能超过4KB的限制。而如果你尝试用POST方法向ASP.NET MVC控制器发送数据的时候,在控制器一端,你会发现数据没有传入到控制器上。比如说下面的JQuery代码:

代码


<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head>
<title>Tools</title>
<link href=”http://ajax.googleapis.com/ajax/libs/jQueryui/1.8/themes/base/jQuery-ui.css” rel=”stylesheet” type=”text/css”/>
<LINK rel=”stylesheet” href=”http://jqueryui.com/demos/demos.css” />
<script type=”text/JavaScript src=”Scripts/jquery-1.4.2.min.js”></script>
<script type=”text/JavaScript src=”Scripts/jquery-ui-1.8.6.custom.min.js”></script>
<script type=”text/JavaScript src=”Scripts/tinymce/tiny_mce.js”></script>
<script type=”text/javascript” src=”Scripts/tinymce/jquery.tinymce.js”></script>
</script>
<script language=”javascript” type=”text/javascript”>
var editor_selector = #note_editor;
var annotation_catalog_url = /Comment/Save;

function show_annotation_dialog() {
$(editor_selector).tinymce({
mode:
textareas,
theme:
simple
});
}

function save_handler() {
if (tinyMCE.get(note_editor)) {
var html = tinyMCE.get(note_editor).getContent();
var comment = {
Id:
1,
User:
,
PageUrl:
+ window.location.href,
Content: html
};

$.ajax({
url: annotation_catalog_url,
type: POST,
dataType:
json,
data: comment,
contentType:
application/json; charset=utf-8,
success:
function (data) {
if (data != null)
$(
#result).html(data.Message);
}
});
}
}

$(document).ready(function () {
$(
#submit).click(save_handler);
});
</script>
</head>
<body>
<div id=”note_editor”>
</div>
<input type=”button” value=”提交” id=”submit” />

<div id=”result”>
</div>
</body>

点击提交后,在控制器一端根本没有被调用,这是因为虽然你的jQuery代码指明了数据类型是json,但是数据格式并不是json格式的。你需要将对象序列化成json格式,使用json2.js就可以做这个事情,下载地址是:http://www.json.org/js.html

代码

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head>
<title>Tools</title>
<link href=”http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css” rel=”stylesheet” type=”text/css”/>
<LINK rel=”stylesheet” href=”http://jqueryui.com/demos/demos.css” />
<script type=”text/javascript” src=”Scripts/jquery-1.4.2.min.js”></script>
<script type=”text/javascript” src=”Scripts/jquery-ui-1.8.6.custom.min.js”></script>
<script type=”text/javascript” src=”Scripts/tinymce/tiny_mce.js”></script>
<script type=”text/javascript” src=”Scripts/tinymce/jquery.tinymce.js”>
<script type=text/javascript src=Scripts/json2.js></script>
</script>
</script>
<script language=”javascript” type=”text/javascript”>
var editor_selector = #note_editor;
var annotation_catalog_url = /Comment/Save;

function show_annotation_dialog() {
$(editor_selector).tinymce({
mode:
textareas,
theme:
simple
});
}

function save_handler() {
if (tinyMCE.get(note_editor)) {
var html = tinyMCE.get(note_editor).getContent();
var comment = {
Id:
1,
User:
,
PageUrl:
+ window.location.href,
Content: html
};

$.ajax({
url: annotation_catalog_url,
type: POST,
dataType:
json,
data: JSON.stringify(comment),
contentType:
application/json; charset=utf-8,
success:
function (data) {
if (data != null)
$(
#result).html(data.Message);
}
});
}
}

$(document).ready(function () {
$(
#submit).click(save_handler);
});
</script>
</head>
<body>
<div id=”note_editor”>
</div>
<input type=”button” value=”提交” id=”submit” />

<div id=”result”>
</div>
</body>

如果你在服务器的控制器一端,发生数据没有被正确地反序列化(如果你用ASP.NET MVC 3 RC之前的版本应该就会碰到这个问题)。这是因为虽然客户端指明了数据是json对象,但是,服务器MVC路由器不知道如何解释这个对象,要在服务器端加上json反序列化支持,请在global.asax.csApplication_Start()函数里,添加下面一行代码(表示你要支持json反序列化):

ValueProviderFactories.Factories.Add(new JsonValueProviderFactory());

ASP.NET MVC 3 RC以后的版本就不需要添加这一行代码了。

[转载]Socket 同步和异步模式

mikel阅读(959)

[转载]Socket 同步和异步模式 – 寒江蓑苙 – 博客园.

学习socket 的一些笔记,希望和大家一起讨论 。
一.什么是socket
所谓socket通常也称作”套接字”, 应用程序通常通过”套接字”向网络发出请求或者应答网络请求。 以J2SDK-1.3为例,Socket和ServerSocket类库位于java .net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例, 操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是 ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。

二 .开发原理:
服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口, 在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。   客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定 打开的端口,通常临时的、动态的分配一个1024以上的端口。Socket接口是TCP/IP网络的API,Socket接口定义了许多函数或例程,程序 员可以用它们来开发TCP/IP网络上的应用程序。要学Internet上的TCP/IP网络编程,必须理解Socket接口。 Socket接口设计者最先是将接口放在Unix操作系统里面的。如果了解Unix系统的输入和输出的话,就很容易了解Socket了。网络的 Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返 回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

三.简单的socket 同步代码,目前代码没有上服务器测试,不知道同步和异步的效果怎么样!

需求

1.客户端是公司内部的机器200台计算机。
2.可以同时连接,长连接。
3.服务器接受到客户端发来的信息,发送 socket数据  发送给总公司socket服务器 ,总公司返回数据。

socket 同步简单代码

代码

public class Server
{
static void Main(string[] args)
{

IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(127.0.0.1), 9050);
//创建 socket Tcp
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(
10);
while (true)
{
Socket connection
= listener.Accept();
SocketProxy sp
= new SocketProxy(connection);
Thread thread
= new Thread(new ThreadStart(sp.TcpSendData));
thread.Name
= connection.RemoteEndPoint.ToString();
thread.Start();

}

}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
}
}

public class SocketProxy
{

private Socket _socket;
public SocketProxy(Socket connection)
{
this._socket = connection;
}

public void TcpSendData()
{
byte[] bytes;
string data = string.Empty;
while (true)
{

bytes = new byte[1024];
// Receive client data
int bytesRec = this._socket.Receive(bytes);
// convert bytes to  string
data = Encoding.ASCII.GetString(bytes, 0, bytesRec);
Console.WriteLine(data);

}
}
}

异步代码

internal class SocketServer
{
static void Main(string[] args)
{

SocketTcpListener listener = new SocketTcpListener();
listener.StartListening();

}
}


internal class SocketTcpListener
{

public static ManualResetEvent allDone = new ManualResetEvent(false);
//开始监听
public void StartListening()
{
//Data buffer for incoming data.
byte[] bytes = new Byte[1024];

//Establish the local endpoint for the socket.
//The DNS name of the computer
//running the listener is “host.contoso.com”.
//IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = IPAddress.Parse(127.0.0.1);
IPEndPoint localEndPoint
= new IPEndPoint(ipAddress, 9050);

// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(
10);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine(Waiting for a connection);
StateObject state
= new StateObject();
listener.BeginAccept(
new AsyncCallback(AcceptCallback), listener);

// Wait until a connection is made before continuing.
allDone.WaitOne();
}

}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}

}

//异步接受
private static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();

// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler
= listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket
= handler;

Array.Clear(state.buffer, 0, state.buffer.Length); // 清空缓存,避免脏读

handler.BeginReceive(state.buffer,
0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
}

//异步读取
public static void ReadCallback(IAsyncResult ar)
{
String content
= String.Empty;
String resultdata
= string.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler
= state.workSocket;

// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);

if (bytesRead > 0)
{
// There  might be more data, so store the data received so far.
// state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));

// Check for end-of-file tag. If it is not there, read
// more data.

content = Encoding.ASCII.GetString(state.buffer, 0, bytesRead);
Array.Clear(state.buffer,
0, state.buffer.Length); // 清空缓存,避免脏读

Console.WriteLine(content);
if (content.Length > 1)
{

// All the data has been read from the
// client. Display it on the console.
//Console.WriteLine(“Read {0} bytes from socket. \n Data : {1}”, content.Length, content);
// Echo the data back to the client.

//SocketTcpClient.CreateSocketObjest();

// get caac data
// 0 如果是第一次,需要创建socket对象,发送登陆信息,匹配出用户名(<name,socket>),存放到集合
// 1.如果用户存在,需要使用用户名来匹配socket对象,如果没有连接配置,需要创建socket对象,保存到集合以便下次使用连接对象,避免再次的登陆
// 2.发送数据完,远程服务器会返回信息给Socket Server
// 3.Socket Server 返回信息给 Clinet

state.ResultData = content;
state.SocketName
= Jackyong;

resultdata =SocketTcpClient.Send(handler, content);

// send client

//Send(handler, resultdata);
Send(state);

}
//else
//{
// // Not all data received. Get more.
// handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(ReadCallback), state);
//}
}
}

//发送数据
private static void Send(StateObject state)
{
Array.Clear(state.buffer,
0, state.buffer.Length); // 清空缓存,避免脏读
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(state.ResultData);
// Begin sending the data to the remote device.
state.workSocket.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), state);
}

// 异步发送数据给client
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
// Socket handler = (Socket)ar.AsyncState;
StateObject state = (StateObject)ar.AsyncState;

// Complete sending the data to the remote device.
int bytesSent = state.workSocket.EndSend(ar);

// if detect exceptoin  socket shut down /close
// handler.Shutdown(SocketShutdown.Both);
// handler.Close();

// continue to listener
//StateObject state = new StateObject();
//state.workSocket = handler;

Array.Clear(state.buffer,
0, state.buffer.Length); // 清空缓存,避免脏读
state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);

}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

}

internal class SocketTcpClient
{
/// <summary>
/// create socket object  ,client send server
/// </summary>
/// <param name=”command”>command</param>
/// <returns>data</returns>
public static Socket CreateSocketObjest()
{
//需要存储这个连接对象

string receivemsg = string.Empty;
byte[] data = new byte[1024];
string IP = 127.0.0.1;
int Port = 9050;
IPEndPoint ie
= new IPEndPoint(IPAddress.Parse(IP), Port);//服务器的IP和端口
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
//因为客户端只是用来向特定的服务器发送信息,所以不需要绑定本机的IP和端口。不需要监听。
client.Connect(ie);
}
catch (SocketException e)
{
//close  socket and writing error exception
Console.WriteLine(e.ToString());

}
return client;
}

/// <summary>
/// client send command server
/// </summary>
/// <param name=”client”>socket</param>
/// <param name=”command”>command</param>
/// <returns>data</returns>
public static string Send(Socket client, string command)
{
byte[] data = new byte[1024];
string result = string.Empty;
//send command
ClientSendDataServer(client, command);

if (client.Poll(1, SelectMode.SelectRead))
{
//如果传递过来的是string.empty 字符串也是0

int recv = client.Receive(data);
if (recv == 0)
{
//socket连接已断开
}
result
= Encoding.ASCII.GetString(data, 0, recv);
Console.WriteLine(result);
}

return result;
}

/// <summary>
/// client to server
/// </summary>
/// <param name=”handler”>socket </param>
/// <param name=”data”>comand</param>
private static void ClientSendDataServer(Socket client, String command)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(command);
// Begin sending the data to the remote device.
client.Send(byteData);
}

}

/// <summary>
/// 存放客户端与总公司的登陆信息
/// </summary>
internal class SocketUser
{
private System.Collections.Generic.Dictionary<string, Socket> _sockets;

public SocketUser()
{
_sockets
= new Dictionary<string, Socket>();
}

/// <summary>
/// select client user
/// </summary>
/// <param name=”key”></param>
/// <returns></returns>
public Socket Find(string key)
{
Socket socket
= null; ;
if (key == null || key == string.Empty)
{

}
if (_sockets[key] != null)
{
socket
= _sockets[key] as Socket;
}
return socket;
}

/// <summary>
/// add  client in  sockets
/// </summary>
/// <param name=”key”></param>
/// <param name=”sk”></param>
public void Add(string key, Socket sk)
{
_sockets.Add(key, sk);

}

/// <summary>
/// Remove client  user
/// </summary>
/// <param name=”key”></param>
public void Remove(string key)
{
_sockets.Remove(key);
}

}

// socket state

internal class StateObject
{
// socket name
public string SocketName { get; set; }
// Client  socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public string ResultData { get; set; }
}

[转载]DataGridView 经典用法总结(上)

mikel阅读(928)

[转载]DataGridView 经典用法总结(上)–附有可下载的Demo – elivn – 博客园.

一、DataGridView 单元格验证

比如只允许输入数字

要求:验证错误后焦点不离开。

有两种方法:

DataGridView.EditingControlShowing 事件和DataGridView.CellValidating 事件。

(1) DataGridView.EditingControlShowing 事件。

显示用于编辑单元格的控件时发生,命名空间: System.Windows.Forms

程序集: System.Windows.Forms(在 system.windows.forms.dll 中)。

如:

void dgvCs_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)

{

e.CellStyle.BackColor = Color.Aquamarine;//设置编译时的颜色

control = new TextBox();

control = (TextBox)e.Control;

control.KeyPress += new KeyPressEventHandler(txt_KeyPress);//

}

然后在txt_KeyPress这里进行验证。

(2) DataGridView.CellValidating 事件。

在单元格失去输入焦点时发生,并启用内容验证功能。命名空间: System.Windows.Form,程序集: System.Windows.Forms(在 System.Windows

.Forms.dll 中)

备注:

验证不通过时调用e.Cancel = true,终止事件链,单元格将保持编辑状态。

调用dgv_details.CancelEdit();可以使单元格的内容会滚到修改前的值。

使用System.Windows.Forms.SendKeys.Send(“^a”);将全选单元格的内容。

如:

void dgv_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)

{

decimal tmp = 0.0m;

if (!decimal.TryParse(e.FormattedValue.ToString(), out tmp))//是否是数字

{

if (e.FormattedValue != null && e.FormattedValue.ToString().Length != 0)

{

DevComponents.DotNetBar.MessageBoxEx.Show(请输入有效数字!”, 提示”);

e.Cancel = true;

}

}

}

这两种方法都能验证。第一种方法当按键按下时(即当编译时)就去验证,而第二种方法是当焦点离开单元格编译区域时触发。所以个人感觉第一种方法更优一点。

二、指定选中单元格并开始编辑状态

实现:

//获得焦点

DataGridView.Focus();

//指定当前单元格

DataGridView.CurrentCell = dgv_details[0, 0]; []中对应参数为列索引(或列标题)、行索引。(注意:不是默认的先行索引)

//开始编辑状态

d DataGridView.BeginEdit(false);false是指对指定行进行编辑。

DataGridView.BeginEdit 方法 尝试将网格置于允许编辑的状态。

命名空间: System.Windows.Forms

程序集: System.Windows.Forms(在 System.Windows.Forms.dll 中)

三、在拖动列的滚动条时可以将指定的列冻结。

this.dataGridView1.Columns[“AddToCartButton”].Frozen = true;

说明:中括号([])中指相应列的索引或者相应列的标题

这个知道了后一看就应该明白,无需多加解释。

四、 DataGridView选择的部分拷贝至剪贴板 。

拷贝模式设定

DataGridView1.ClipboardCopyMode= DataGridViewClipboardCopyMode.EnableWithoutHeaderText //设置可复制的模式

其中DataGridView.ClipboardCopyMode 属性获取或设置一个值,该值指示用户是否可以将单元格的文本值复制到 Clipboard,以及是否包括行标题和列标题文本。

命名空间: System.Windows.Forms

程序集: System.Windows.Forms(在 System.Windows.Forms.dll 中)

选中部分拷贝

Clipboard.SetDataObject(DataGridView1.GetClipboardContent()) //将控件选中的数据置于系统剪贴板中

DataGridView粘贴

代码

if (DataGridView1.CurrentCell.Value == null)

{

return;

}

int insertRowIndex = DataGridView1.CurrentCell.RowIndex;

string pasteText=Clipboard.GetText();//从系统剪贴板中获取数据

if(string.IsNullOrEmpty(pasteText))

{

return;

}

string[] lines=pasteText.Split(‘\r’);//按行分组

bool isHeader=true;

foreach(string line in lines)

{

if(isHeader)

{

isHeader=false;//当可复制模式中含有标题时的过滤操作

}

else

{

string[] vals=line.Split(‘\t’);//按tab空格分组

if (vals.Length – 1 != DataGridView1.ColumnCount)

{

throw new ApplicationException(“列数错误”);

}

DataGridViewRow row = DataGridView1.Rows[insertRowIndex];

row.HeaderCell.Value=vals[0];

for(int i=0;i<row.Cells.Count-1;i++)

{

row.Cells[i].Value=vals[(i+1)];

}

insertRowIndex+=1;

}

}

五、DatagridView自动编号

代码

private void dataGridView1_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)

{

//自动编号与数据库无关

Rectangle rectangle = new Rectangle(e.RowBounds.Location.X, e.RowBounds.Location.Y

,dataGridView1.RowHeadersWidth – 4,e.RowBounds.Height);

TextRenderer.DrawText(e.Graphics, (e.RowIndex + 1).ToString(),dataGridView1.

RowHeadersDefaultCellStyle.Font, rectangle,

dataGridView1.RowHeadersDefaultCellStyle.ForeColor, TextFormatFlags.VerticalCenter | TextFormatFlags.Right);

}

显示行号

六、 指定单元格属性

DataGridViewCell dgcell = new DataGridViewTextBoxCell();//申明单元格类型

this.dgvCss.Rows[i].Cells[k] = dgcell;

其实很简单,只是很多人不知道有这个属性。但这种方式和带有复选框类型的单元格使用时,一般情况下出错,也给一些人一种错觉以为单元格类型不能这么操作。其实是因为你在申明列的时候先申明的checkbox类型的,而这时他们便有一个默认值(FormattedValue)false,当你重新给他赋值单元格类型时便会出现FormattedValue错误,这时你只需给它一个默认值就可以,如

this.dgvCss.Rows[i].Cells[k].Value = “”; 这样便可解决。

部分代码Demo 其中Demo中有部分是(中)、(下)的内容

转载请注明出处!

欢迎大家讨论:如有疑问请联系elivn@vip.qq.com