[转载]百度K淘客站的解决方法

mikel阅读(1187)

在大概一个月前,所有淘宝客站长发现了很严重的问题,尤其是用TOPAPI来做全站的淘宝客站长,问题相当严重

在问题中,我们透过问看内部。首先是一则新闻。说淘宝客遭百度封杀,起因是淘宝和百度的商业竞争所致。看完了发现更加的杯具。但是我冷静的分析了一下。这个就是我一起杯具的站汽车用品 www.gogotao.com

1、有人说百度是人工来分析数据库中的收录链接中的淘宝客,而且这个舆论很主流。但是我观察并且调查过,发现不止那些有名气的淘宝客大站,就连 刚刚起步不久的小站都被K。但是你要想,这些淘宝客小站多如牛毛,百度要想人工来K,要雇佣多少人,就是全部是兼职的来做,也不现实。

2、根据以上结论,我得出百度肯定是用机器程序来分析出淘宝客。我又进入各个淘宝客群里还有相关论坛里。发现还真有人的淘宝客站没有被K,或者说没有受到多大影响。并且这其中有很多站也是一直以来比较牛的淘客站。这点说明,还是有可能来逃避百度这次的大屠杀。

3、上面已经给了我们希望,然后就是分析为什么他们没有被K,我发现他们都有一个特点,有列表页和商品详细页!并且所有连接都是内部的列表页和详细页的连接,只在详细页有一个明式的淘宝客连接跳转,并且他们除了商品详细页以外没有任何淘宝相关的连接!

以上我得出了,淘宝是根据你整站外链的淘宝外链数来推断的,并且我分析那些被K的淘客站,发现他们都是出站外链的淘宝外链数量太多。

我把这个分析告诉了几个曾经的牛人站长,让他们重新做站来根据我这个分析来做。目前已经过去了将近一个月。那些站长反馈告诉我他们的新站都有了 不错的收录和排名没有被百度K掉,所以我来发这篇文章来联合广大淘宝客一起和百度斗争到底。我也做了一个新站,采用的资讯式的方式来少量的放淘宝客连接指甲油 www.laoniangmen.com

[转载]SQL Server 的锁定和阻塞

mikel阅读(964)

[转载]快速搞懂 SQL Server 的锁定和阻塞 – Wizard 编程网 – 博客园.

本帖提供两种做法,可避免让 client-side 程序,持续等待 SQL Server 中事务锁定造成的不正常或长时间阻塞,而让用户也无限期等待,甚至造成 connection pooling 连接数超过容量。

日前公司 server-side 有组件,疑似因撰写时 exception-handling 做得不周全,导致罕见的特殊例外发生时,让 SQL Server 的事务未执行到 cmmmit 或 rollback,造成某些表或记录被「锁定 (lock)」。后来又有大量的 request,要透过代码访问这些被锁定的记录,结果造成了严重的长时间「阻塞」,最后有大量 process 在 SQL Server 呈现「等待中」的状态。

由于 SQL Server 的「事务隔离级别」默认是 READ COMMITTED (事务期间别人无法读取),加上 SQL Server 的锁定造成阻塞时,默认是别的进程必须无限期等待 (LOCK_TIMEOUT = -1)。结果这些大量的客户端 request 无限期等待永远不会提交或回滚的事务,并一直占用着 connection pool 中的资源,最后造成 connection pooling 连接数目超载。
查了一些书,若我们要查询 SQL Server 目前会话中的 lock 超时时间,可用以下的命令:

SELECT @@LOCK_TIMEOUT
默认为 -1,意即欲访问的对象或记录被锁定时,会无限期等待。若欲更改当前会话的此值,可用下列命令:

SET LOCK_TIMEOUT 3000

后面的 3000,其单位为毫秒,亦即会先等待被锁定的对象 3 秒钟。若事务仍未释放锁,则会抛回如下代号为 1222 的错误信息,可供程序员编程时做相关的逾时处理:

消息 1222,级别 16,状态 51,第 3 行
已超过了锁请求超时时段。
若将 LOCK_TIMEOUT 设置为 0,亦即当欲访问对象被锁定时,完全不等待就抛回代号 1222 的错误信息。此外,此一 SET LOCK_TIMEOUT 命令,影响范例只限当前会话,而非对某个表做永久的设置。

——————————————————————————————-
接下来我们在 SSMS 中,开两个会话 (查询窗口) 做测试,会话 A 创建会造成阻塞的事务进程,会话 B 去访问被锁定的记录。

会话 A
BEGIN TRAN;
UPDATE Orders SET EmployeeID=7 WHERE OrderID=10248
rollback; –故意不提交或回滚
会话 B
SELECT * FROM Orders WHERE OrderID=10248

分别执行后,因为欲访问的记录是同一条,按照 SQL Server 「事务隔离级别」和「锁」的默认值,会话 B 将无法读取该条数据,而且会永远一直等下去 (若在现实项目里写出这种代码,就准备被客户和老板臭骂)。

——————————————————————————————-
若将会话 B 先加上 SET LOCK_TIMEOUT 3000 的设置,如下,则会话 B 会先等待 3 秒钟,才抛出代号 1222 的「锁请求已超时」错误信息:

SET LOCK_TIMEOUT 3000
UPDATE Orders SET EmployeeID=7 WHERE OrderID=10248
SET LOCK_TIMEOUT -1

执行结果:

消息 1222,级别 16,状态 51,第 3 行
已超过了锁请求超时时段。
语句已终止。

——————————————————————————————-
另根据我之前写的文章「30 分钟快快乐乐学 SQL Performance Tuning」所述:
http://www.cnblogs.com/WizardWu/archive/2008/10/27/1320055.html

撰写不当的 SQL 语句,会让数据库的索引无法使用,造成全表扫描或全聚集索引扫描。例如不当的:NOT、OR 算符使用,或是直接用 + 号做来串接两个字段当作 WHERE 条件,都可能造成索引失效,变成全表扫描,除了性能变差之外,此时若这句不良的 SQL 语句,是本帖前述会话 B 的语句,由于会造成全表扫描,因此就一定会被会话 A 的事务阻塞 (因为扫描全表时,一定也会读到 OrderID=10248 的这一条记录)。
下方的 SQL 语句,由于 OrderID 字段有设索引,因此下图 1 的「执行计划」,会以算法中的「二分搜寻法」在索引中快速查找 OrderID=10250 的记录。

SELECT * FROM Orders WHERE OrderID=10250

SELECT * FROM Orders WHERE OrderID=10250 AND ShipCountry=’Brazil’

图 1 有正确使用到索引的 SQL 语句,以垂直的方向使用索引。用 AND 算符时,只要有任一个字段有加上索引,就能受惠于索引的好处,并避免全表扫描
此时若我们将这句 SQL 语句,当作前述会话 B 的语句,由于它和会话 A 所 UPDATE 的 OrderID=10248 不是同一条记录,因此不会受会话 A 事务未回滚的影响,会话 B 能正常执行 SELECT 语句。
但若我们将会话 B 的 SQL 语句,改用如下的 OR 算符,由于 ShipCountry 字段没有加上索引,此时会造成全表扫描。如此一来,除了性能低落以外,还会因为在全表扫描时,读到会话 A 中锁定的 OrderID=10248 那一条记录,造成阻塞,让会话 B 永远呈现「等待中」的状态。

SELECT * FROM Orders WHERE OrderID=10250 OR ShipCountry=’Brazil’

图 2 未正确使用索引的 SQL 语句,以水平的方向使用索引。用 OR 算符时,必须「所有」用到的字段都有加上索引,才能真正有效使用索引、避免全表扫描

——————————————————————————————-
发生阻塞时,透过以下命令,可看出是哪个进程 session id,阻塞了哪几个进程 session id,且期间经过了多少「毫秒 (ms)」。如下图 3 里 session id = 53 阻塞了 session id = 52 的进程。另透过 SQL Server Profiler 工具,也能看到相同的内容。

SELECT blocking_session_id, wait_duration_ms, session_id FROM sys.dm_os_waiting_tasks

图 3 本帖前述会话 A 的 UPDATE 语句 (53),阻塞了会话 B 的 SELECT 语句 (52)
透过以下两个命令,我们还能看到整个数据库的锁定和阻塞详细信息:

SELECT * FROM sys.dm_tran_locks

EXEC sp_lock

图 4 session id = 52 的 process 因阻塞而一直处于等待中 (WAIT)
另透过 KILL 命令,可直接杀掉造成阻塞的 process,如下:

KILL 53

——————————————————————————————-
欲解决无限期等待的问题,除了前述的 SET LOCK_TIMEOUT 命令外,还有更省事的做法,如下,在会话 B 的 SQL 语句中,在表名称后面加上 WITH (NOLOCK) 关键字,表示要求  SQL Server,不必去考虑这个表的锁定状态为何,因此也可减少「死锁 (dead lock)」发生的机率。但 WITH (NOLOCK) 不适用 INSERT、UPDATE、DELETE。

SELECT * FROM Orders WITH (NOLOCK) WHERE OrderID=10248
类似的功能,也可如下,在 SQL 语句前,先设置「事务隔离级别」为可「脏读 (dirty read)」。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SELECT * FROM Orders WHERE OrderID=10248
两种做法的效果类似,让会话 B 即使读到被锁阻塞的记录,也永远不必等待,但可能读到别人未提交的数据。虽然说这种做法让会话 B 不用请求共享锁,亦即永远不会和其他事务发生冲突,但应考虑项目开发实际的需求,若是会话 B 要查询的是原物料的库存量,或银行系统的关键数据,就不适合用这种做法,而应改用第一种做法的 SET LOCK_TIMEOUT 命令,明确让数据库抛回等候逾时的错误代号 1222,再自己写代码做处理。
——————————————————————————————-
归根究柢,我们在编程时,就应该避免写出会造成长时间阻塞的 SQL 语句,亦即应最小化锁定争用的可能性,以下为一些建议:

  • 尽可能让事务轻薄短小、让锁定的时间尽量短,例如把不必要的命令移出事务外,或把一个大量更新的事务,切成多个更新较少的事务,以改善并发性。
  • 将组成事务的 SQL 语句,摆到一个「批 (batch) 处理」,以避免不必要的延迟。这些延迟常由 BEGIN TRAN … COMMIT TRAN 命令之间的网络 I/O 所引起。
  • 考虑将事务的 SQL 语句写在一个存储过程内。一般来说,存储过程的执行速度会比批处理的 SQL 语句快,且存储过程可降低网络的流量和 I/O,让事务可更快完成。
  • 尽可能频繁地认可 Cursor 中的更新,因为 Cursor 的处理速度较慢,会让锁定的时间较长。
  • 若无必要,使用较宽松的事务隔离级别,如前述的 WITH (NOLOCK) 和 READ UNCOMMITTED。而非为了项目开发方便,全部使用默认的 READ COMMITTED 级别。
  • 避免在事务执行期间,还要等待用户的反馈或交互,这样可能会造成无限期的持有锁定,如同本帖一开始提到的状况,最后造成大量的阻塞和数据库 connection 被占用。
  • 避免事务 BEGIN TRAN 后查询的数据,可能在事务开始之前先被引用。
  • 避免在查询时 JOIN 过多的表,否则除了性能较差外,也很容易读到正被锁定或阻塞中的表和字段。
  • 应注意在一个没有索引的表上,过量的「行锁」,或一些锁定使用了过多的内存和系统资源时,SQL Server 为了有效地管理这些锁定,会尝试将锁定扩展为整个表的「表锁」,此时会很容易造成其他 process 在访问时的阻塞和等待。

[转载].net 4.0 中对多线程新特性(四)--任务和任务工厂

mikel阅读(887)

[转载].net 4.0 中对多线程新特性(四)–任务和任务工厂 – 不懈 – 博客园.

在4.0前如果需要进行并行任务往往都是自己实现Task和Task Factory来管理任务,其中难免会牵涉到大量的线程和线程池的管理工作。到了4.0 这部分内容都已经集成到基础类库中了。在System.Threading.Task中新增了几个类:

Task

TaskFactory

TaskScheduler

这几个类一看名字就知道干啥的了,先看看Task的一个简单例子

代码

1 [STAThread]
2 static void Main()
3 {
4 var tasks = new Task[10];
5 for (int i = 0; i < 10; i++)
6 {
7 tasks[i] = new Task(Action,TaskCreationOptions.LongRunning);
8 tasks[i].Start();
9 }
10
11 //Task.WaitAll(tasks);
12
13 Console.WriteLine(Finished);
14 Console.ReadLine();
15 }
16
17 static Random rd = new Random();
18 static void Action()
19 {
20 var threadId = Thread.CurrentThread.ManagedThreadId;
21 Console.WriteLine(Current Thread Id:{0} ,threadId);
22 Thread.Sleep(rd.Next(100, 1000));//随机休眠一段时间,模拟线程实际调用时间消耗
23 }
程序输出如下:
Current Thread Id:11
Finished

Current Thread Id:12

Current Thread Id:14
Current Thread Id:13
(实际的输出可能和这里有所不同)
细心的你可能会发现代码中的第11行已经被注释了,WaitAll的名字是不是非常熟悉?是的,这在我们做线程同步时经常会用到WaitHandle类的WaitAll效果是一样的
取消注释运运行一下我们会得到如下输出:

Current Thread Id:11
Current Thread Id:12

Current Thread Id:14
Current Thread Id:13
Finished

在输出中的Finished一定会等所有的Task完成之后才会输出。是不是非常容易了。Task启动的时候会自动使用到线程池,系统会给Task自动分配线程。

如果任务很多的时时候我们需要细粒度的控制每个Taks的调度Schedule可以用TaskFactory就是来完成做这个工作的,看个简单的例子:

代码

1 [STAThread]
2 static void Main()
3 {
4 var factory = new TaskFactory();
5 var tasks = new Task[10];
6 for (int i = 0; i < 10; i++)
7 {
8 tasks[i] = factory.StartNew(Action );
9 }
10
11 Task.WaitAll(tasks);
12
13 Console.WriteLine(Finished);
14 Console.ReadLine();
15 }
16
17 static Random rd = new Random();
18 static void Action()
19 {
20 var threadId = Thread.CurrentThread.ManagedThreadId;
21 Console.WriteLine(Current Thread Id:{0} ,threadId);
22 Thread.Sleep(rd.Next(100, 1000));//随机休眠一段时间,模拟线程实际调用时间消耗
23 }

程序输出如下:

Current Thread Id:11
Current Thread Id:11
Current Thread Id:12
Current Thread Id:11
Current Thread Id:11
Current Thread Id:12
Current Thread Id:11
Current Thread Id:12
Current Thread Id:11
Current Thread Id:12
Finished

从结果中看到10个任务只分配到了2个线程来处理,线程被最大的限度的复用了。如果需要更好的控制任务的调度,你需显示的配置TaskScheduler,对于你来说应该非常easy的

[转载]HTML 中表单form 的相关知识

mikel阅读(837)

[转载]HTML 中表单form 的相关知识 – xugang – 博客园.

JavaScript 中,页面上的每一对<form> 标记都解析为一个对象,即form 对象。

可以通过document.forms 获取以源顺序排列的文档中所有form 对象的集合。

如果一个表单对象定义如下:
<form name=frm1 method=post action=login.aspx>
获得该表单对象的方法:
i> document.forms[“frm1”]; // 根据name属性值
ii> document.forms[0]; // 根据索引号
iii> document.frm1; // 直接根据name值获得对象

form 表单应该注意的属性:

elements:获取以源顺序排列的给定表单中所有控件的集合。<input  type=”image” > 对象不在此集合内。

var txtName = myform.elements[0]; //获得表单的第一个元素
var txtName = myform.elements[txtName]; //获得name属性值为”txtName”的元素
var txtName = myform.elements.txtName; //获得name属性值为”txtName”的元素

enctype:设置或获取表单的多用途网际邮件扩展(MIME) 编码。

这个属性的默认值为:application/x-www-form-urlencoded

如果要上传文件,则应该设置为:multipart/form-data

form 表单中的<label> 标记:

每一个表单元素的文字描述都应该使用<label> 标记!

该标记用于将文字绑定到对应的表单元素上,它的for 属性指定它所要绑定的表单元素id 值。绑定后单击该文字,鼠标将聚焦到对应的文本框中或选中对应的选项。

若安装了某些桌面主题,某些表单元素还会变换颜色来予以提示,这大大提高了用户体验。

示例代码:

<form method=post name=frm1>
<label for=txt>点我将聚焦到文本框</label>
<input type=text id=txt name=myname>
<br/>
<label for=rdo>点我将选中单选框</label>
<input type=radio id=rdo name=male/>
<br/>
<label for=cbo>点我将选中复选框</label>
<input type=checkbox id=cbo name=hobby>
</form>
效果如下:



注意:

i >    每个表单元素应当尽量使用<label>标签来提高用户体验;

ii >   每个表单元素应当分配 name 属性id 属性

name 属性:用来将数据提交到服务器

id 属性:用来在客户端做相应的操作;如:<label>标签的绑定、CSS 选择器的使用等。

在客户端,JavaScript表单表单元素的操作,更青睐于使用其name 属性。

因为,对于某些特定的表单元素(如:单选按钮等),使用其name 属性更易于获得元素值,也更方便向服务器传送数据!

JavaScript 操作form 表单元素,比较少用的属性:

defaultChecked 设置或获取复选框或单选钮的状态。
defaultValue 设置或获取对象的初始内容。
disabled 设置或获取控件的状态。

提交表单

提交表单的示例:

<form name=frm method=post action=javascript:alert(‘提交成功!’);>
<input type=button value=提交功能
onclick
=document.forms[‘frm’].submit();>
<input type=button value=禁用反复提交
onclick
=this.disabled=true; this.form.submit();>
</form>
效果如下:

注意:

i >   如果使用submit( ) 方法来提交表单,则不会触发<form> 表单元素的onsubmit 事件

这是与用<input  type=”submit”>提交元素不同的地方;

ii >  可以在按钮的提交或点击事件中,使用disabled 属性来禁用用户反复点击提交按钮的行为,

减少服务器的响应负担;

设置文本框

i >   控制文本框的字符个数

<script language=”javascript”>
function LessThan(_textArea){
//返回文本框字符个数是否符号要求的boolean值
return _textArea.value.length < _textArea.getAttribute(maxlength);
}
</script>
<label for=name>文本框:</label>
<input type=text name=name id=name value=姓名 maxlength=10></p>
<br>
<label for=comments>多行文本框:</label>
<textarea name=comments id=comments cols=40 rows=4 maxlength=50 onkeypress=return LessThan(this);></textarea>

注意:多行文本框<textarea> 中的maxlength 为自定义属性;如果在<textarea> 中输入字符时,换行也算一个字符;

ii >  自动选中文本框

<script language=”javascript”>
window.onload = function(){
var txtName = document.getElementsByName(myName)[0];
txtName.onmouSEOver = function(){
this.focus();
};
txtName.onfocus = function(){
this.select();
};
}
</script>
<INPUT TYPE=text NAME=myName value=默认值被选中 />

遵循了行为与结构分离的原则。

设置单选按钮

获取选中的单选按钮& 设置单选按钮被选中。代码如下:

<script language=javascript>
//获取选中项
function getChoice(){
var oForm = document.forms[myForm1];
//radioName是单选按钮的name属性值
var aChoices = oForm.radioName;
//遍历整个单选项表
for(i=0;i<aChoices.length;i++)
if(aChoices[i].checked)
break; //如果发现了被选中项则退出
alert(您选中的是:+aChoices[i].value);
}
//设置选中项
function setChoice(_num){
var oForm = document.forms[myForm1];
//radioName是单选按钮的name属性值
oForm.radioName[_num].checked = true; //其它选项的checked值会自动设置为false
}
</script>
//调用代码
<input type=”button” value=”获取选中项” onclick=”getChoice();” />
<input type=”button” value=”设置第1项被选中” onclick=”setChoice(0);” />

需要保证同一组单选按钮的name 属性值相同即可。

设置复选框

设置复选框的“全选”、“全不选”及“反选”功能。

<script language=javascript>
function changeBoxes(_action){
var myForm = document.forms[myForm1];
//myCheckbox 为所有复选框的name属性值
var oCheckBox = myForm.myCheckbox;
for(var i=0;i<oCheckBox.length;i++) //遍历每一个选项
if(_action < 0)//反选
oCheckBox[i].checked = !oCheckBox[i].checked;
else
//_action为1是则全选,为0时则全不选
oCheckBox[i].checked = _action;
}
</script>
<form name=myForm1>
<input type=checkbox name=myCheckbox>aa
<input type=checkbox name=myCheckbox>bb
<input type=button value=全选 onclick=changeBoxes(1); />
<input type=button value=全不选 onclick=changeBoxes(0); />
<input type=button value=反选 onclick=changeBoxes(-1); />
</form>

设置下拉列表框

下拉列表框的multiple 属性用于设置或获取下拉列表框是否可以选中多个选项

下拉列表框默认只能选中一项,若要设置为可以选中多项,则<select  multiple = “multiple”> 即可。

type 属性:javascript 语言根据type 属性的值获得下拉列表框select 控件的类型。

type 属性的值为:select-multiple 或 select-one (注意:下拉列表框的类型由multiple 属性控制

i >  查看下拉列表框的选项(单选项 & 多选项)

<script language=javascript>
function getSelectValue(_myselect){
var oForm = document.forms[myForm1];
//根据参数(下拉列表框的name属性值)获得下拉菜单项
var oSelectBox = oForm.elements[_myselect];
//判断是单选还是多选
if(oSelectBox.type == select-one){
var iChoice = oSelectBox.selectedIndex; //获取选中项
alert(单选,您选中了 + oSelectBox.options[iChoice].text);
}
else{
var aChoices = new Array();
//遍历整个下拉菜单
for(var i=0;i<oSelectBox.options.length;i++)
if(oSelectBox.options[i].selected)//如果被选中
//压入到数组中
aChoices.push(oSelectBox.options[i].text);
//输出结果
alert(多选,您选了: + aChoices.join());
}
}
</script>
<form method=post name=myForm1>
<select id=mysel name=mysel multiple=multiple style=height:60px;>
<option value=a>AA</option>
<option value=b>BB</option>
<option value=c>CC</option>
</select>
<input type=button onclick=getSelectValue(‘mysel’) value=查看选项 />

ii >  添加下拉列表框的选项

追加新选项到末尾

<script language=javascript>
function AddOption(Box){ //追加选项到末尾
var oForm = document.forms[myForm1];
var oBox = oForm.elements[Box];
var oOption = new Option(乒乓球,Pingpang);
oBox.options[oBox.options.length] = oOption;
}
</script>

插入新选项到指定位置

<script language=javascript>
function AddOption(_select,_num){
var oForm = document.forms[myForm1];
var oBox = oForm.elements[_select];
var oOption = new Option(text值,value值);
//兼容IE7,先添加选项到最后,再移动
oBox.options[oBox.options.length] = oOption;
oBox.insertBefore(oOption,oBox.options[_num]);
}
</script>
<input type=button value=添加乒乓球 onclick=AddOption(‘myselect’,1); />

注意:如果直接使用insertBefore( ) 方法插入选项,将会在IE 中出现一个空选项的bug。为了解决IE 的这个bug ,只能使用先追加新选项到末尾,然后再使用insertBefore( ) 方法移动到具体位置的办法。

类似这样:为了跳过浏览器的某些bug 或限制,实现预定目的的小技巧,通常称之为hack 。

iii >  替换某一选项

<script language=javascript>
//替换选项
function ReplaceOption(_select,_num){
var oForm = document.forms[myForm1];
var oBox = oForm.elements[_select];
var oOption = new Option(text值,value值);
oBox.options[_num] = oOption; //替换第_num 个选项
}
</script>
<input type=button value=替换选项 onclick=ReplaceOption(‘selName’,1); />

通过oBox.options[_num] = oOption 直接将创建的新选项替换掉指定位置的选项。

iiii >  删除某一选项

<script language=javascript>
function RemoveOption(_select,_num){
var oForm = document.forms[myForm1];
var oBox = oForm.elements[_select];
oBox.options[_num] = null; //删除选项
}
</script>
</head>
<body>
<select id=mysel name=mysel multiple=multiple>
......
<input type=button value=删除选项 onclick=RemoveOption(‘mysel’,1); />

直接通过oBox.options[_num] = null 删除选项。

[转载]MVC模式简单讲解

mikel阅读(1249)

[转载]MVC模式简单讲解 – 设计模式 – Java – JavaEye论坛.

意图

最终目标就是:分离视图和模型,让多个视图可以共用一个模型

类图


时序图



Test

Java代码
  1. package mvc;
  2. /**
  3. * MVC模式,最终目标就是:分离视图和模型,让多个视图可以共用一个模型
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public class Test {
  8. public static void main(String[] args){
  9. /* 模型可以重用*/
  10. IModel model = new ModelImpl();
  11. ViewTextAndControl viewText = new ViewTextAndControl();
  12. IController controller = new Controller();
  13. /*视图要委托给控制器处理请求,所以需要有个控制器的引用*/
  14. viewText.setController(controller);
  15. /*控制器选择模型去处理业务逻辑,所以需要有个模型的引用*/
  16. controller.setModel(model);
  17. /*为模型注册观察者,当模型变化时,及时通知给观察者*/
  18. model.registerObserver(viewText);
  19. ViewHtml viewHtml = new ViewHtml();
  20. /*这个html视图,只接受模型的数据
  21. IController controller2 = new Controller();
  22. viewHtml.setController(controller2);
  23. controller2.setModel(model);
  24. */
  25. model.registerObserver(viewHtml);
  26. /*模拟视图层的操作*/
  27. Test t = new Test();
  28. t.test(viewText);
  29. }
  30. private void test(ViewTextAndControl v){
  31. v.start(100);
  32. try {
  33. Thread.sleep(1000);
  34. v.add();
  35. Thread.sleep(1000);
  36. v.add();
  37. catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. }

IModel

Java代码
  1. package mvc;
  2. /**
  3. * 模型接口,被观察者
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public interface IModel {
  8. public void start(int startCount);
  9. public void stop();
  10. public void add();
  11. public int getCount();
  12. public void registerObserver(Observer observer);
  13. public void removeObserver(Observer observer);
  14. }

ModelImpl

Java代码
  1. package mvc;
  2. import java.util.ArrayList;
  3. import java.util.Iterator;
  4. /**
  5. * 模型层实现
  6. * alex295111
  7. * 2010-8-5
  8. */
  9. public class ModelImpl implements IModel {
  10. private int count;
  11. private ArrayList observers = new ArrayList();
  12. public void add() {
  13. count++;
  14. notifyObservers();
  15. }
  16. public int getCount() {
  17. return count;
  18. }
  19. public void registerObserver(Observer observer) {
  20. observers.add(observer);
  21. }
  22. public void removeObserver(Observer observer) {
  23. if(observers.indexOf(observer)<0){
  24. observers.remove(observer);
  25. }
  26. }
  27. public void start(int startCount) {
  28. count = startCount;
  29. notifyObservers();
  30. }
  31. private void notifyObservers(){
  32. for(Iterator it = observers.iterator(); it.hasNext();){
  33. Observer observer = (Observer) it.next();
  34. observer.update(this);
  35. }
  36. }
  37. public void stop() {
  38. }
  39. }

IController

Java代码
  1. package mvc;
  2. /**
  3. * 控制器接口
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public interface IController {
  8. /**开始计数器*/
  9. public void start(int startCount);
  10. /** 停止计数器*/
  11. public void stop();
  12. /**数值增加*/
  13. public void add();
  14. public void setModel(IModel model);
  15. }

Controller

Java代码
  1. package mvc;
  2. /**
  3. * 控制器选择模型去处理业务逻辑,所以需要有个模型的引用
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public class Controller implements IController{
  8. private IModel model;
  9. public void add() {
  10. model.add();
  11. }
  12. public void start(int startCount) {
  13. model.start(startCount);
  14. }
  15. public void stop() {
  16. }
  17. public void setModel(IModel model) {
  18. this.model = model;
  19. }
  20. }

Observer

Java代码
  1. package mvc;
  2. /**
  3. * 观察者接口,在这里主要供给视图实现,因为视图要及时反映模型的状态
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public interface Observer {
  8. public void update(IModel model);
  9. }

ViewTextAndControl(视图1

Java代码
  1. package mvc;
  2. /**
  3. * 视图,处理展现还带有控制功能
  4. * 视图持有模型层和控制层引用
  5. * 将请求委托给控制器,由控制器决定使用哪个模型处理该请求
  6. * alex295111
  7. * 2010-8-5
  8. */
  9. public class ViewTextAndControl implements Observer {
  10. private IController controller;
  11. public void setController(IController controller) {
  12. this.controller = controller;
  13. }
  14. /*启动计数器*/
  15. public void start(int startCount){
  16. controller.start(startCount);
  17. }
  18. /*计数器加1*/
  19. public void add(){
  20. controller.add();
  21. }
  22. public void update(IModel model) {
  23. System.out.println(“我是文本格式ViewText:刚刚获得模型层数据count:”+model.getCount());
  24. }
  25. }

ViewHtml(视图2

Java代码
  1. package mvc;
  2. /**
  3. * html形式的视图,只接收数据
  4. * alex295111
  5. * 2010-8-5
  6. */
  7. public class ViewHtml implements Observer {
  8. public void update(IModel model) {
  9. System.out.println(“<h1>我是Html格式View2,刚刚获得模型层数据count:”+model.getCount()+“</h1>”);
  10. }
  11. }
    以下为了有个用户界面,添加了个Swing视图

    ViewSwingAndControl(视图3

    Java代码
    1. package mvc_swing;
    2. import java.awt.FlowLayout;
    3. import java.awt.event.ActionEvent;
    4. import java.awt.event.ActionListener;
    5. import javax.swing.JButton;
    6. import javax.swing.JFrame;
    7. import javax.swing.JTextField;
    8. public class ViewSwingAndControl implements Observer {
    9. private IController controller;
    10. public void setController(IController controller) {
    11. this.controller = controller;
    12. }
    13. private JFrame frame = new JFrame(“我是swing视图”);
    14. private JTextField countField = new JTextField(“count暂无值”);
    15. private JButton startButton = new JButton(“开始”);
    16. private JButton addButton = new JButton(“+1”);
    17. public ViewSwingAndControl(){
    18. frame.setLayout(new FlowLayout());
    19. frame.add(countField);
    20. frame.add(startButton);
    21. frame.add(addButton);
    22. frame.setLocation(500, 300);
    23. frame.setSize(200,200);
    24. frame.setVisible(true);
    25. addListener();
    26. setAddButtonDisable();
    27. }
    28. public void addListener(){
    29. startButton.addActionListener(new ActionListener(){
    30. public void actionPerformed(ActionEvent e) {
    31. controller.start(100);
    32. setAddButtonEnable();
    33. }
    34. });
    35. addButton.addActionListener(new ActionListener(){
    36. public void actionPerformed(ActionEvent e) {
    37. controller.add();
    38. }
    39. });
    40. }
    41. public void setAddButtonDisable(){
    42. this.addButton.setEnabled(false);
    43. }
    44. public void setAddButtonEnable(){
    45. this.addButton.setEnabled(true);
    46. }
    47. public void update(IModel model) {
    48. countField.setText(Integer.toString(model.getCount()));
    49. }
    50. }

    Test

    Java代码
    1. package mvc_swing;
    2. /**
    3. * MVC模式,最终目标就是:分离视图和模型,让多个视图可以共用一个模型
    4. * alex295111
    5. * 2010-8-5
    6. */
    7. public class Test {
    8. public static void main(String[] args){
    9. /* 模型可以重用*/
    10. IModel model = new ModelImpl();
    11. ViewTextAndControl viewText = new ViewTextAndControl();
    12. IController controller = new Controller();
    13. /*视图要委托给控制器处理请求,所以需要有个控制器的引用*/
    14. viewText.setController(controller);
    15. /*控制器选择模型去处理业务逻辑,所以需要有个模型的引用*/
    16. controller.setModel(model);
    17. /*为模型注册观察者,当模型变化时,及时通知给观察者*/
    18. model.registerObserver(viewText);
    19. /*创建一个swing视图*/
    20. ViewSwingAndControl viewSwing = new ViewSwingAndControl();
    21. IController controller2 = new Controller();
    22. viewSwing.setController(controller2);
    23. controller2.setModel(model);
    24. model.registerObserver(viewSwing);
    25. ViewHtml viewHtml = new ViewHtml();
    26. /*这个html视图,只接受模型的数据
    27. IController controller2 = new Controller();
    28. viewHtml.setController(controller2);
    29. controller2.setModel(model);
    30. */
    31. model.registerObserver(viewHtml);
    32. /*模拟视图层的操作*/
    33. /*
    34. Test t = new Test();
    35. t.test(viewText);
    36. */
    37. }
    38. private void test(ViewTextAndControl v){
    39. v.start(100);
    40. try {
    41. Thread.sleep(1000);
    42. v.add();
    43. Thread.sleep(1000);
    44. v.add();
    45. catch (InterruptedException e) {
    46. e.printStackTrace();
    47. }
    48. }
    49. }

[转载]XML数据读取方式性能比较(一)

mikel阅读(1001)

[转载]XML数据读取方式性能比较(一) – XiaoMing的点滴 – 博客园.

几个月来,疑被SOA,一直在和XML操作打交道,SQL差不多又忘光了。现在已经知道,至少有四种常用人XML数据操作方式(好像Java差不多),不过还没有实际比较过这些方式各有哪些特点或优劣。正好看到网上也没有这方面的实验,偶来总结一下。

测试开始先读取XML源,用一个比较大的RSS文件链接,复制到项目bin/Debug目录下。

Stream xmlStream = new MemoryStream(File.ReadAllBytes(path));

一、XmlDocument 方式

代码

二、XPathNavigator 方式

代码

三、XmlTextReader 方式

代码

四、Linq to XML 方式

代码

测试结果:

XmlDocment 47ms
XPathNavigator 42ms
XmlTextReader 23ms
Xml Linq 28ms

小结一下自己的认识,XmlDocument的操作基本按W3C的DOM操作方式,不过要将全部节点解析成对象加载到内存中,往往造成很大浪 费。所以微软自己的编程规范也不推荐用它。这里由于读取了所有节点,可能因此性能和Navigator方式相差不大。在三种随机读取方式中,Xml Linq性能最高,只是方法名有点别扭。XmlTextReader方式是所谓的SAX,只读向前,无疑性能最高,不过实现上麻烦了不少,要比较精确的控 制访问逻辑,也无法用匿名类存储数据。

.Net 3.5发布Xml Linq可以很好地取代前两种方式,通常情况下,最好用它。只有个别场合,如果对性能要求极高,或者读取Xml数据量太大不能一下子下载或读取到内存中,那就只好痛苦委身于XmlTextReader了。

[转载]你可能不知道的10个JavaScript小技巧

mikel阅读(1020)

[转载]你可能不知道的10个JavaScript小技巧 – 梦想天空 – 博客园.

尽管我使用JavaScript来做开发有很多年了,但它常有一些让我很惊讶的小特 性。对于我来说,JavaScript是需要持续不断的学习的。在这篇文章中,我将列出10个Javascript使用小技巧,主要面向 Javascript新手和中级开发者。希望每个读者都能至少从中学到一个有用的技巧。

1.变量转换

看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法。始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做法反而效率更高。

1 var myVar   = "3.14159",
2 str     = ""+ myVar,//  to string
3 int     = ~~myVar, //  to integer
4 float   = 1*myVar, //  to float
5 bool    = !!myVar, /*  to boolean - any string with length
6 and any number except 0 are true */
7 array   = [myVar]; //  to array

转换日期(new Date(myVar))和正则表达式(new RegExp(myVar))必须使用构造函数,而且创建正则表达式的时候要使用/pattern/flags的形式。

(更多…)

[转载]EntLib微软企业库5.0 学习之路

mikel阅读(1196)

[转载][EntLib]微软企业库5.0 学习之路——第一步、基本入门 – kyo-yo – 博客园.

话说在大学的时候帮老师做项目的时候就已经接触过企业库了但是当初一直没明白为什么要用这个,只觉得好麻烦啊,竟然有那么多的乱七八糟的配置(原来我不知道有配置工具可以进行配置,请原谅我的小白)。

直到去年在做项目的时候在看代码生成工具的时候看到了许多生成工具都内置了企业库的数据库访问及一些相关模块,才突然顿悟了,可能企业库应该还是有点用 的,于是就试着使用了企业库的一些模块(当时版本是4.1),果然觉得企业库还是很不错的,微软在企业库中为我们封装了许多日常经常使用的模块,如:数据 库访问,缓存,日志,异常处理,加密,IOC等等,在日常的项目开发中,企业库已经贯穿了一个项目从底层到表示层基本上需要用到的模块,大大方便了我们的 开发,毕竟自己再重复造轮子还不如就用微软的(毕竟企业库现在都到5.0)

最近在看园子里有好几位都在写,如:virus,huangcong。我现在写的其实基本上都是自己的学习之路(可能没前面2位写的好,但就当自己的学习笔记吧)。

废话不多说了,首先开始学习企业库的基本概念,一下摘自sfbirp的企业库学习笔记:

一、什么是企业库?

企业库包含一系列应用程序模块和核心架构。这些高复用的组件旨在帮助开发者解决一些共同的企业开发问题。
企业库同时提供高可 配置的特性,使我们管理重复的工作更加容易,例如我们知道的在应用的很多地方发生的横切关注点。包括像日志记录、缓存、异常管理等。另外,它提供的依赖注 入容器能够简化和分解你的设计,使他们更加可实验和容易理解,帮助你创建更加高性能的设计和各种应用。
二、使用企业库的3个简单步骤:
1.引用和配置你需要的程序集。
2.通过企业库配置应用。
3.在你的代码中创建和使用企业库对象。
三、企业库的好处:
应用模块帮助解决开发者从一个项目到另一个项目面对的共同问题。他们的设计封装了微软推荐的做法,这些都是基于微软框架的应用开发。例如,数据访问应用模 块提供了对ADO.NET访问最频繁使用的特征。在某些情况下,应用模块还添加了一些基础类库没有直接提供的相关功能。
四、企业库的目标
1.一致。所有的企业库模块都坚持一致的设计模式和实现方式。
2.可扩展性。所有的应用模块包括定义扩展点,允许开发人员通过添加自己的代码定制应用模块的行为。
3.易用性。企业库提供了许多实用性的东西,包括一个图形化配置工具,简单的安装过程,完成的文档和示例。
4.集成。企业库应用模块被设计得能够一起很好的工作,并且也被这样测试过。但是你不必一起使用他们。我们可以单独使用这些应用模块,同时这些应用模块之间也有些依赖,比如在企业库核心和Unity中的一些通用组件。

上面是企业库的基本概念,理解了企业库的相关知识后,我们可以开始来安装企业库了

1、下载地址:点我进入下载页面(不是直接下载),安装后就可以使用了。

这次5.0相比4.1的最大改动就是整个配置工具采用了WPF重新构建和实例化和管理对象生命周期的全依赖注入的实现,同时支持VS2008SP1和VS2010,话说虽然这次的配置工具变化挺大的,但是一旦熟悉了就觉得比4.1的好,因为可以清楚的看见每个模块的之间的依赖关系

附相关改动,还是摘自sfbirp的企业库学习笔记:

一、Unity和对象生成器的整合
在这个版本中,用于创建对象的基本技术是一个单一的依赖注入容器,默认的是Unity。你可以使用容器生成企业库对象的实例并注入到其他的对象。 企业库提供一个标准的接口从Unity容器中来获得定义在企业库配置中的对象的实例,如SQLDatabase或LogWriter.另外,注入友好的实 例门面处理静态门面之外是有效的,因为静态门面不能用注入,但是为了向后兼容以前的版本而存在。在本版本中的示例中都是用依赖注入,但是以前版本中是用的 静态工厂类和静态门面在这个版本中还是支持的。对象生成器,一个低版本的依赖注入机制在这个版本中被归入Unity中,在项目中不再需要单独引用对象生成 器集。
支持Unity容器,必须引用一个被包含在企业库中的Microsoft.Practices.ServiceLocation程序集。如果要使用一个第3方的依赖注入容器,必须直接实现IServiceLocator接口或者通过适配器实现。

二、影响所有模块的变化:
1.在企业库中主要修正是使用依赖注入机制。所用的应用模块以及核心系统都是用依赖注入机制,并使用Unity作为默认的依赖注入容器,来创建和管理企业库对象。
2.自主容器的实现通过实现Common Service Locator项目提供的 IServiceLocator 接口来完成。
3.由于错误配置引发的错误将提供更多有用的错误信息。
4.配置系统公开了一个 fluent接口,用来为单个对象或整个应用创建和填充配置源。fluent API使得为各种情景创建配置源更加容易。
5.ConfigurationView类被删除.
6.一些配置元素有默认值,可能不同于以前版本的隐式默认值.
7.企业库现在允许你通过另一个配置文件合成一个混合配置文件.
8.可以可通过不同的配置文件读取不同的配置信息.
9.企业库不支持XAML浏览器应用程序(XBAP).
10.WmiEnabled标志为了像前兼容仍然存在企业库中,但是在5.0中是被忽略的,而且将来会被删除.
11.改进式的安装允许你只安装部分应用模块及配置工具.
12.在以前版本中要做统一集成,必须添加核心企业库的扩展和每个模块的扩展。现在如果你只需要直接访问容器,那么只有核心扩展是必须的。单独模块将自动支持。
旧的功能为了保持像前兼容仍然保留,但已经不起作用。
13.FileConfigurationSource.Save 的签名已经改变,参数由3个变为2个。
14.快速入门不再包含在主安装程序中。

三、Breaking变化:
1.企业库现在抛出了一个配置错误ActivationException,之前是System.Configuration.ConfigurationErrorsException。这包括试着解决没有配置信息错误的一个实例提供者。
2.以前版本在获取应用模块错误时抛出BuildFailedException错误,现在对于所有的应用模块都抛出ActivationException
3 .之前的版本,在讲一个空源传到容器来调用容器的时候,会抛出ArgumentNullException,现在抛出NullReferenceException
4.ObjectBuilder2不再是一个单独的程序集,而是集成到了Unity集合中,在项目中也不需要引用ObjectBuilder2.dll。

5.WMI支持已经从企业库中删除,除了在logging模块中的WMI跟踪监听器。
6.如果你没有关闭DbDataReader,可能会导致随机的、很难在您的代码中找到的错误,尤其是当你正在一个由TransactionScope上下文创建的隐式事务下操作时。
你必须始终确保您的应用程序及时关闭DbDataReader,无论是明确的DbDataReader.Close方法关闭或是逼迫DbDataReader释放。
7.如果你使用 validator 特性必须引用 System.ComponentModel.DataAnnotations程序集。
8.为FileConfigurationSource.Save方法签名已更改。该方法有两个参数,而不是3个参数
9.Microsoft.Practices.EnterpriseLibrary.Configuration.Design.dll集合的功能和其他 设计时集合被一个新的集合 Microsoft.Practices.EnterpriseLibrary.Configuration.DesignTime.dll代替。
10,性能计数器异常从PolicyInjection.CallHandlers移到 PolicyInjection 程序集。
11.包含在Policy Injection Application Block中的CachingCallHandler有未处理的安全漏洞,已经从Policy Injection Application Block中移除。

四、配置工具的改变:
1.新的企业拥有一个新的GUI库和一个元数据驱动的可扩展性模。
2.支持向导
3.新的类型选择。
4.不支持对依赖策略的Environmental Overrides 。日志模块处理Categories。
五、缓存模块变化:
1.缓存清除已被完全重写的性能优化
六、数据库访问模块:
1.ExecuteReader, ExecuteXmlReader, ExecuteScalar, and ExecuteNonQuery方法具有异步版本。
2.包含了很多新的方法和类允许你提取数据作为对象序列。例如在合适的时候使用客户端查询技术,如LINQ.
3.存在的方法ExecuteReader和新的方法BeginExecuteReader不接收CommandBehavior 参数。默认的当调用这些方法的时候这些方法
会自动设置CommandBehavior 属性到reder中用来关闭连接直到指定一个事务。

七、异常处理模块:
1.日志异常处理重新使用日志模块的Log Writer and Trace Listeners 。这在之前版本中不是默认设置。
2.增加一个功能,通过ExceptionManager.Process 方法接收一个默认值并返回一个值。

在安装完企业库同时知道了5.0的变化后,就开始正式的学习了,学习的最好办法不是看企业库的源代码(我是看不懂),而是下载微软发布的学习例子

学习例子下载地址:点我进入下载页面(不是直接下载)

这个学习例子里面已经包含了所需的类库及代码演示(C#和VB.NET都有),同时可以右键例子中的App.config来看下例子中的模块是怎么配置的。

同时,在学习例子的时候可以看下由http://www.entlib.com/发布的中文学习手册,虽然是4.1的,但是里面的所有内容都和5.0的例子一样(5.0只不过是重新实现了1遍),把例子和文档结合起来看学习起来就快了很多了:

中文学习手册:点我进入下载页面(不是直接下载)

到此,Entlib5的学习之路的第一步就结束了,基本上通过微软发布的学习例子和对应的中文学习手册可以对5.0的使用已经有了个基本的了解。

[转载]C#中直接打印Report文件(rdlc)

mikel阅读(1333)

[转载]C#中直接打印Report文件(rdlc) – 聆听代码的声音 – 博客园.

Visual Studio自带的报表文件(rdlc,后面提到的报表,都指rdlc报表文件)虽然功能相对不是十分强大,但应付一般的报表要求也是绰绰有余了。关于 rdlc报表的使用和设计方法,这里就不做讲解了,本文主要介绍一下如何不使用报表预览控件(ReportViewer),直接将报表的内容在打印机上打 印出来。

一般情况下,我们设计好报表后,在程序运行的时候,会将其加载到ReprotViewer控件中进行预览并打印,但有些情况下,我们不想预览报表的 内容,而直接将其在打印机上打印出来,又该怎么做呢?要想实现直接打印的功能,我们需要用到.Net提供的2个对象,LocalReport和 PrintDocument。LocalReport对象负责加载一个报表文件生成实际的报表并将报表按照知道的格式输出,PrintDocument对 象负责将LocalReport的输出内容发送到打印机打印。具体的实现步骤如下:

Step 1: 声明一个LocalReport对象并加载一个报表文件(假定我们已经设计好一个报表文件,名称为PrintMe.rdlc)。

   1: LocalReport report = new LocalReport();

   2: //设置需要打印的报表的文件名称。

   3: report.ReportPath = @"c:\PrintMe.rdlc";

   4: //创建要打印的数据源

   5: ReportDataSource source = new ReportDataSource(SourceTalbe.TableName, SourceTalbe);

   6: report.DataSources.Add(source);

   7: //刷新报表中的需要呈现的数据

   8: report.Refresh();

Step 2: 将报表的内容输出为指定格式的数据流。

   1: string deviceInfo =

   2:   "<DeviceInfo>" +

   3:   "  <OutputFormat>EMF</OutputFormat>" +

   4:   "  <PageWidth>8.5in</PageWidth>" +

   5:   "  <PageHeight>11in</PageHeight>" +

   6:   "  <MarginTop>0.25in</MarginTop>" +

   7:   "  <MarginLeft>0.25in</MarginLeft>" +

   8:   "  <MarginRight>0.25in</MarginRight>" +

   9:   "  <MarginBottom>0.25in</MarginBottom>" +

  10:   "</DeviceInfo>";

  11: Warning[] warnings;

  12: //将报表的内容按照deviceInfo指定的格式输出到CreateStream函数提供的Stream中。

  13: report.Render("Image", deviceInfo, CreateStream, out warnings);

这里使用LocalReport对象的Render方法,将报表的内容输出到指定的数据流(Stream)中。Render方法的第一个参数指定输 出的流的格式,这里指定为Image格式(图形格式);第二个参数为一个XML格式的字符串,用来描述输出格式的详细信息;第三个参数是一个回调函数 (CreateStreamCallback委托类型),你需要为这个参数声明一个函数,Render方法会将报表的内容输出到这个函数返回的 Stream对象的实例中,这个函数看上去类似下面的声明

   1: //声明一个Stream对象的列表用来保存报表的输出数据

   2: //LocalReport对象的Render方法会将报表按页输出为多个Stream对象。

   3: private List<Stream> m_streams;

   4: //用来提供Stream对象的函数,用于LocalReport对象的Render方法的第三个参数。

   5: private Stream CreateStream(string name, string fileNameExtension,

   6:   Encoding encoding, string mimeType, bool willSeek)

   7: {

   8:     //如果需要将报表输出的数据保存为文件,请使用FileStream对象。

   9:     Stream stream = new MemoryStream();

  10:     m_streams.Add(stream);

  11:     return stream;

  12: }

你可以使用这个函数的参数执行更多的操作,具体内容请参考CreateStreamCallback委托

第四个参数用来输出报表处理过程中产生的警告信息。

Step 3: 使用PrintDocument对象执行打印操作。

   1: //用来记录当前打印到第几页了

   2: private int m_currentPageIndex;

   3:

   4: private void Print()

   5: {

   6:     m_currentPageIndex = 0;

   7:

   8:     if (m_streams == null || m_streams.Count == 0)

   9:         return;

  10:     //声明PrintDocument对象用于数据的打印

  11:     PrintDocument printDoc = new PrintDocument();

  12:     //指定需要使用的打印机的名称,使用空字符串""来指定默认打印机

  13:     printDoc.PrinterSettings.PrinterName = "";

  14:     //判断指定的打印机是否可用

  15:     if (!printDoc.PrinterSettings.IsValid)

  16:     {

  17:         MessageBox.Show("Can't find printer");

  18:         return;

  19:     }

  20:     //声明PrintDocument对象的PrintPage事件,具体的打印操作需要在这个事件中处理。

  21:     printDoc.PrintPage += new PrintPageEventHandler(PrintPage);

  22:     //执行打印操作,Print方法将触发PrintPage事件。

  23:     printDoc.Print();

  24: }

具体的PrintPage事件处理程序如下:

   1: private void PrintPage(object sender, PrintPageEventArgs ev)

   2: {

   3:     //Metafile对象用来保存EMF或WMF格式的图形,

   4:     //我们在前面将报表的内容输出为EMF图形格式的数据流。

          m_streams[m_currentPageIndex].Position = 0;

   5:     Metafile pageImage = new Metafile(m_streams[m_currentPageIndex]);

   6:     //指定是否横向打印

   7:     ev.PageSettings.Landscape = false;

   8:     //这里的Graphics对象实际指向了打印机

   9:     ev.Graphics.DrawImage(pageImage, 0, 0);

  10:     m_streams[m_currentPageIndex].Close();

  11:     m_currentPageIndex++;

  12:     //设置是否需要继续打印

  13:     ev.HasMorePages = (m_currentPageIndex < m_streams.Count);

  14: }

到此,我们的报表数据就已经打印出来了,在这个过程中,你可以根据需要添加自己的打印逻辑。

获取本地的打印机信息请参考PrinterSettings

[转载]访问者模式的简洁实现

mikel阅读(1080)

[转载]访问者模式的简洁实现 – 蜡笔小巢 – 博客园.

作为《设计模式:可复用面向对象软件的基础》中最复杂的一个模式,访问者模式被限制在一个相对特定的类型体系环境下,经典访问者模式实现上非常有技巧性,同时也带来客户类型与Visitor之间交织在一起的双因素依赖关系。对于非常重视结构清晰、实现简洁的大型企业应用而言,经典访问者模式的实现方式“过犹不及”了。

以这段实现为例:

C#
/// visitor 需要影响的Element,Visitable
public interface IEmployee
{
/// 相关属性
string Name { get; set;}
double Income { get; set;}
int VacationDays { get; set;}

void Accept(IVisitor visitor);
}

/// 抽象Visitor接口
public interface IVisitor
{
void VisitEmployee(IEmployee employee);
void VisitManager(Manager manager);
}

/// 一个具体的Element
public class Employee : IEmployee
{
private string name;
private double income;
private int vacationDays;

public Employee(string name, double income, int vacationDays)
{
this.name = name;
this.income = income;
this.vacationDays = vacationDays;
}

public string Name
{
get{return name;}
set{name = value;}
}
public double Income
{
get{return income;}
set{income = value;}
}
public int VacationDays
{
get{return vacationDays;}
set{vacationDays = value;}
}

/// 引入Visitor对自身的操作
public virtual void Accept(IVisitor visitor)
{
visitor.VisitEmployee(
this);
}
}

/// 另一个具体的Element
public class Manager : Employee
{
private string department;
public string Department { get { return department; } }

public Manager(string name, double income, int vacationDays,
string department)
:
base(name, income, vacationDays)
{
this.department = department;
}
/// 引入Visitor对自身的操作
public override void Accept(IVisitor visitor)
{
visitor.VisitManager(
this);
}
}

/// 为了便于对HR系统的对象进行批量处理增加的集合类型
public class EmployeeCollection : List<IEmployee>
{
/// 组合起来的批量Accept操作
public virtual void Accept(IVisitor visitor)
{
foreach (IEmployee employee in this)
employee.Accept(visitor);
}
}
Unit Test
private EmployeeCollection employees = new EmployeeCollection();

/// 具体的Visitor, 增加休假天数
class ExtraVacationVisitor : IVisitor
{
public void VisitEmployee(IEmployee employee)
{
employee.VacationDays
+= 1;
}
public void VisitManager(Manager manager)
{
manager.VacationDays
+= 2;
}
}

/// 具体的Visitor, 加薪
class RaiseSalaryVisitor : IVisitor
{
public void VisitEmployee(IEmployee employee)
{
employee.Income
*= 1.1;
}

public void VisitManager(Manager manager)
{
manager.Income
*= 1.2;
}
}

[TestMethod]
public void Test()
{
employees.Add(
new Employee(joe, 25000, 14));
employees.Add(
new Manager(alice, 22000, 14, sales));
employees.Add(
new Employee(peter, 15000, 7));

employees.Accept(new ExtraVacationVisitor());
employees.Accept(
new RaiseSalaryVisitor());

IEmployee joe = employees[0];
Assert.AreEqual
<double>(25000 * 1.1, joe.Income);
IEmployee peter
= employees[2];
Assert.AreEqual
<int>(7 + 1, peter.VacationDays);

IEmployee alice = employees[1];
Assert.AreEqual
<int>(14 + 2, alice.VacationDays);
Assert.AreEqual
<double>(22000 * 1.2, alice.Income);
}

其实只需2行即可完成

代码

employees.Where(x => x.GetType() == typeof(Employee)).ToList().ForEach(x=>{ x.Income *= 1.1; x.VacationDays++;});
employees.Where(x
=> x.GetType() == typeof(Manager)).ToList().ForEach(x => { x.Income *= 1.2; x.VacationDays+=2; });

(注:这里判断x => x.GetType() == typeof(Employee) 没有用is语句,因为Manager: Employee,因此如果不做精确匹配,Manager类型的实例会连续执行两次处理)

示例非常短小,但同样能达到前面IVisitor、IElement绕来绕去双因素依赖后的效果。为什么?

  • 首先,Visitor是通过ForEach()语句赋予的,相当于Accept(Visitor)过程,实际的Visitor就是个System.Action<EmployeeBase>委托。
  • 其 次,Visitor的Visit()处理则是在ForEach()过程中执行的,employees中所有满足条件的类型会随着ForEach的迭代依次 执行System.Action<EmployeeBase>委托指向的处理,这里采取了最简化的处理,委托的指向为一个LAMADA语法定 义的匿名方法,例如:对于Employee类型就是x=>{ x.Income *= 1.1; x.VacationDays++;}。
  • 实际项目中,如果处理过程特别复杂,您完全可以让执行System.Action<EmployeeBase>委托指向独立的方法、类甚至外部的某个程序集中的处理逻辑。
  • 从效果看,IElement类型与IVisitor间的“脐带”也用委托剪断了,依赖关系大为简化,从双因素依赖变为单因素依赖,对于Visit()逻辑很简单,使用匿名委托的情况,甚至可以称之为“半”单因素依赖。

实际项目中,访问者模式的思路可以保留,至于经典模式中“八股”&“繁琐”的实现方式建议还是尽可能避免。