
1 已经添加过引用,却找不到类型或名字空间。 可以看下项目的的.net framework版本是否一致。 项目(例如类库项目)右键(vs解决方案资源管理器)——》属性——》应用程序——》目标框架。 也就是检查一下引用项目与被引用项目的目标框架是否版本一致。 2 svn曾经用两个用户登录,然后因为两个用户连接的版本仓库不同,所以导致其中一个用户检检入时总失败。在svn Settings中——》已保存数据——》认证数据——》清除,打开后,可以看到帐号,点击清除全部,只能保证一次成功,下一交动作又把原来的帐号加上了(还是以另一个帐号做提交动作),所以还出错,只能再清除全部。 可以尝试打开目录: C:\Users\administrator\AppData\Roaming\Subversion\auth 把里边内容全部删除。再重新登录认证就正常了。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
由于线程停在某个无法进行垃圾回收的点(可能是因为已对代码进行了优化),因此无法计算表达式的值. 先说下我出现这个异常情况的条件:我是在做WEB服务接入时,发生的,为了调用接口的方法,我先封装了一次,封装方法有2个参数,都是类类型。其它的这种方法都可以,只有这一个接口不通过。例如这个封装的方法叫M1,那它是这样的M1(class1 c1,class2 c2),其中在调用它时,c1,c2都是正常的,属性和字段都有值。但跟踪到这个方法时,通过看c1,c2的值会发现c1,c2中的属性就会有:由于线程停在某个无法进行垃圾回收的点(可能是因为已对代码进行了优化),因此无法计算表达式的值 但字段是好的。当时我想难道是类型属性的问题,又一行一行的检查了一下,发现没有问题啊。于是把两个类中的成员全注释。发现还是有这个问题。 在网上搜索了一些内容,没有任何帮助,都是按情况解决的。不具有通用性。 通过这个例子,看到构造器时,灵光一闪,终于发现了问题所在。原来是构造器的参数过多造成的。这个接口有150多个属性,所以我构造了一个150个参数的构造器。 改过之后,就好了。 所以,出现这个问题,所得经验如下: 1 与计算机系统无关, 2 与vs2008环境无关 3 与vs2008配置项无关 4 检查代码吧,是代码的问题。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
上一节中的输入,即视图中的return HttpResponse()部分。函数中的内容为<html><body>…… 意思就是,前端文件,要每次都要手写,打印,这非常麻烦。通常,它会包括很多内容,还有js文件,css文件等。而且设计页面也不好设计,或者设计好了,再粘贴html字串,进行输出。且会发现html代码与python后台代码掺杂在一起。 1 通过自定义, 直接在视图中写。新建立视图(函数)hello_who如下: def hello_who(request): t=template.Template('hello {{name}}') c=template.Context({'name':'宋江'}) return HttpResponse(t.render(c)) 通过创建模板,输出hello,who 这里who的名字为中文的宋江。,如果在页面中正常显示中文,需要在settings.py中设置LANGUAGE_CODE = 'zh-cn' 且在视图文件views.py中,第一行加入#encoding=utf-8 2 使用模板 先建立模板文件,文件名任意。模板文件就是html前台文件。与aspx页类似。先建立一个hello.html文件。如下: <html> <body> <div style="color:red;"><b>Hello,{{name}}</b></div> </body> </html> 然后在视图中加载这个模板,并给变量赋值。 视图hello_who改为: def hello_who(request): t = get_template('hello.html') html = t.render(Context({'name': '宋江'})) return HttpResponse(html) 其中,获得模板,和呈现函数,可以利用一个函数实现,如下:def hello_who(request): return render_to_response('hello.html', {'name': '宋江'}) 另使用这个函数,需要引入: from django.shortcuts import render_to_response 通过locals()返回所有定义的映射 def hello_who(request): name= '宋江' return render_to_response('hello.html', locals()) 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
1 建立视图 进入到第二个mysite目录。新建立views.py文件。 代码如下: from django.http import HttpResponse def hello(request): return HttpResponse('hello world') 可以知道,视图文件,从django中引入模块http,并导入HttpResponse函数。视图中一个hello函数。其中的reutrn HttpResponse可以理解为c#中的response.Write。 2 创建路由 通过浏览器的地址,从路由中去匹配视图。 在urls.py文件中配置路由。 from django.conf.urls import patterns, include, url from mysite.views import * urlpatterns = patterns('', (r'^hello/',hello), # Examples: # url(r'^$', 'mysite.views.home', name='home'), # url(r'^mysite/', include('mysite.foo.urls')), # Uncomment the admin/doc line below to enable admin documentation: # url(r'^admin/doc/', include('django.contrib.admindocs.urls')), # Uncomment the next line to enable the admin: # url(r'^admin/', include(admin.site.urls)), ) 上边第二行红色的就是为hellp视图配置的路由。(r'^hello/',hello),r表示忽略转义字符,与C#中的@类似的含义。红色部分表示浏览器的地址,这里是正则支持。通过正则表达式来匹配浏览器地址。然后如果匹配地址成功,则把此地址的请求路由到蓝色的部分的视图。即,如果浏览器中访问hello,那会匹配到hello视图。注意的是,因为要用到views.py中的hello函数,所以要先加载views.py模块。即代码中的第一行红色的。 3 浏览器中访问 浏览器中输入http://127.0.0.1:8000/hello 即可看到hello world字样。 4 返回服务器时间的页 同上,自己想下,从浏览器访问time。那要有个time的匹配正则,即(r'^time/$',current_datetime),红色部分。就是在浏览器中输入time后,应该能找到与之匹配的路由。如果找到这个路由,应该调用一个视图。这个视图名可以随便命名,这里命名为current_datetime。有了路由,现在开始定义current_datetime的视图。 打开 views.py.新建立一个函数 def current_datetime(request): now=datetime.datetime.now() html="<thml><body>it is now %s.</body></html>" %now return HttpResponse(html) 使用之前,先要导入datetime。 import datetime 然后,浏览器中访问地址http://127.0.0.1:8000/time/ 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
1 安装python。 现在已经到了3.0.但现在推荐使用2.*.。官网地址:http://www.python.org/ 下载地址:http://www.python.org/ftp/python/2.7.4/python-2.7.4.msi 解压缩后,直接安装即可。 例如,安装到c盘根目录。即地址为:C:\Python27 2 配置windows环境变量。在path里添加C:\Python27即可。 配置完后,在命令行,输入python,回车即可进入python命令行。 3 安装django 当前最新版本1.5 官网地址:https://www.djangoproject.com/ 下载地址:https://www.djangoproject.com/m/releases/1.5/Django-1.5.1.tar.gz 解压缩后,进行安装。 我直接解压缩到D盘。目录为:D:\Django-1.5.1 通过命令行进入目录D:\Django-1.5.1\Django-1.5.1 命令行执行 python setup.py install 回车。进行安装。 安装完毕后在 python 命令行,输入django.VERSION 即可得到当前的django的版本,也表示安装完毕。 4创建一个网站项目 命令行进入目录 D:\Django-1.5.1\Django-1.5.1\django\bin 执行:python django-admin startproject mysite,回画。会自动生成目录及文件。 目录mysite -mysite -manage.py 5 对以上创建的项目不做任何改动。启动http服务器。 命令行进入第一个mysite,执行python manage.py runserver,回车。即可启动。默认端口为8000 6,浏览器中输入http://127.0.0.1:8000,即可看到it worked 到此配置完毕。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
索引的用途 索引的主要作用就是为加快检索的速度(效率)。主要是为查询。这里的查询包括表自身的查询,还包括连接查询。此外,sql的查询优化器(隐式的)也依赖索引,如果使用索引比不使用索引时效率高,那优化器就会选择使用索引。 索引可能极大提高检索的速度。 例如:图书馆的图书如果无序排放,则要找《水浒传》这一本书,极有可能会把所有书都翻一遍,当然也有可能会第一本书就是它。如果给图书馆的图书按书名分列于不同书架,以字母排序,那找到S列书架,然后找Sh,shu,shui,然后找在shui书架中再找hu,然后找zhuan,那么就会找到这本书。 索引的分类 索引分聚集(聚簇)索引和非聚集(聚簇)索引。 聚集索引:表中的数据的物理顺序与排序顺序相同,表中可建立且只可建立一个聚集索引(物理顺序)。聚集索引由索引页构成,索引页底层称为叶级,上层称为非叶级。叶级存放着实际的排序的数据,非叶级存放着排序的索引。以图书馆查找《水浒传》为例的示意图: 上图是个示意图,但不表示聚集索引的正确图。索引通过B-tree算法来实现,中文的意思是平衡树,不是二叉树。 非聚集索引:不在物理顺序上排列数据,而是通过排序指针,指针指向数据。 非聚集索引也由索引页构成,索引页底层称为叶级,上层称为非叶级。叶级存放着的排序的指向实际数据的指针,非叶级存放着排序的索引。 Ms sql存储结构 Sql server在存储时,为提高数据库性能,以盘区为单位向数据库分配空间。一个盘区为8个连续的页(Page)。页是数据库存储的基本单位。页大小为8K。一个盘区为8*8=64K。页分为好几种页类型,例如:数据页,索引页,映射表页等。 例如:图书馆的图书以书架(页)为单位,每8个书架在一个小隔间(区)。 索引的参数 ·FillFactor:设置创建索引时每个索引页的页级别中可用空间的比。用来设置索引页中预留的空间。它是填充因子,设置为100%,表示不留预留空间。对于只读表来说,可以设置为100%。设置填充因子的原因就是防止页分裂。 例如:假设图书馆的6号书架为S系统的书籍,现在摆满图书,则它的填充因子(度)为100%,那么如果图书馆新增加了一本书《三侠剑》,按索引来安排,则它应该放到6号书架,但6号书架已经满了,那么可以增加一个书架(页),这个书架可能挨着6号书架,但也有可能有一定距离,那么,在查找《三侠剑》时,就要求在两个书架中查找,可能6号书架中找不到,到新增加的那个书架去找,而这个书架离6号书架较远,影响了查找的效率。那么如果6号书架没有摆满,而是只摆了60本书(假设一本书为1%),那么填充因子(度)为60%。此时当添加《三侠剑》时,可以放到6号书架中,而不必再增加一个额外的书架。 ·Pad_Index:设置创建索引时每个索引页的非页级别中的可用空间的比。它的值由FillFactor指定。 性能分析(简) 简单说明一下,详细说明在后篇说明。 ·showplan_text:设置sql server不执行(on)sql语句,而返回如何执行语句的详细信息。须做为单独的批处理运行,且不能用在存储过程中。与它类似的还有showplan_all,它的信息更详细。 ·statistics io:显示执行sql语句的磁盘活动量。 输出项 中文输出名 含义 Table 表 表的名称。 Scan count 扫描计数 执行的索引或表扫描数。 logical reads 逻辑读取 从数据缓存读取的页数。 physical reads 物理读取 从磁盘读取的页数。 read-ahead reads 预读 为进行查询而放入缓存的页数。 lob logical reads Lob逻辑读取 从数据缓存读取的 text、ntext、image 或大值类型 (varchar(max)、nvarchar(max)、varbinary(max)) 页的数目。 lob physical reads Lob物理读取 从磁盘读取的 text、ntext、image 或大值类型页的数目。 Lob read-ahead reads Lob预读 为进行查询而放入缓存的 text、ntext、image 或大值类型页的数目。 ·statistics time:显示分析,编译,执行sql语句花费的时间(毫秒)。 ·DBCC:全名为数据库控制台命令,即DataBase Console Command。 命令类别 执行 维护 对数据库、索引或文件组进行维护的任务。 杂项 杂项任务,如启用跟踪标志或从内存中删除DLL。 信息 收集并显示各种类型信息的任务。 验证 对数据库、表、索引、目录、文件组或数据库页的分配进行的验证操作。 主要介绍几个: DBCC ShowContig:显示指定的表或视图的数据和索引的碎片信息。这个命令在高版本sql中推荐由动态管理函数sys.dm_db_index_physical_stats代替。 Dbcc showcontig(表名(id)|索引名(id)|视图名(id)) with 对于with部分,常用的是tableresults,用于通过表格显示信息结果。 DBCC DbReindex:重建立索引。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
创建类: class后跟类名,其中类名的首字母必须要大写。实例化时,通过new方法实例化。在c#中有构造器,构造器与类同名。在ruby中,构造器为initialize方法。当通过new方法实例化一个类对象后,会自动调用initialize方法,把new中的参数传递给initialize方法进行初始化。在c#中的域,在ruby中称为实例变量,定义时前边加@前缀,表示是一个实例变量。 class Customer def initialize(name,age) @name,@age=name,age end end c1=Customer.new("Tom",20) 访问器 实例变量只能通过实例方法来访问。如果要访问它们可以提供方法访问器。在c#中称为属性,Java中也是定义方法来表示属性。 class Customer def initialize(name,age) @name,@age=name,age end def name @name end def age @age end end 以上定义了两个方法x,y,它们可以读@x,@y实例变量。这就定义了读属性方法。如果要进行赋值操作,还要定义写属性方法: def name=(v) @name=v end def age=(v) @age=v end 要注意的是: c1=Customer.new("Tom",20) c1.name="Jhon" 只有在实例中才能这样使用写属性。如果在类中,不能通过name=value这样来代替@name=value。但可以通过self来这样使用:self.name=value Ruby提供了简化定义属性方法:attr_reader和attr_accessor。后边跟符号,会自动创建同名的读写属性。 class Customer def initialize(name,age) @name,@age=name,age end attr_reader :name,:age attr_accessor:name,:age end c1=Customer.new("Tom",20) puts c1.name,c1.age c1.name="Jhon" c1.age=30 puts c1.name,c1.age 类变量,常量,类方法 类变量以@@开头;常量通过[类名+::常量名]进行访问;类方法在实例方法方法名前加类名。在定义类方法时,可以采用一种方式: class<<self def 方法 end end 那么,这个方法就是一个类方法: class Customer def initialize(name,age) @name,@age=name,age end class <<self def showName 'ok' end end end puts Customer.showName 方法的访问性 public:公有的,默认情况下类中的方法是公有的,可以用在任何地方。构造方法initialize为私有的。 private:私有的,类内部使用的,只能被类或子类的实例方法所调用。只能通过self隐式调用,不能通过一个对象显示调用。一个私有方法m,只能通过m调用,而不能通过o.m或self.m调用。 protected:受保护的,类内部或子类内部使用的方法。与私有的区别是:除self隐式调用外,还可以通过该类对象显示调用。 可以通过以下方法来声明方法的访问性: #访问性 private protected public private def private_method puts "private method testing" end protected def protected_method puts "protected method testing" end public:protected_method 工厂方法 使用new方法私有,然后通过类方法创建实例 class Fruit private_class_method:new def Fruit.CreateFruit new Fruit end end f=Fruit.CreateFruit 模块module module作用之一是做为名字空间用。调用类时与调用常量相同:两个冒号 另一作用是作为混入。通过include把模块中的实例方法包含到其它类中。感觉功能像C#中的名字空间引入。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
Ruby中的方法是一个有名称的代码块,是与一个或多个对象关联的参数化的代码。调用时须给出方法名,接收者(所在对象),参数值,且最后一个表达式的值做为返回值。与Ruby方法相似的是代码块,它没有名字,且调用时只通过迭代器或被间接调用。 (一)方法 (1)定义方法 定义方法就是 def 方法名(参数列表) 方法体 end 最后一个表达式做为返回值返回,如果有值,则返回;无值则返回nil。可以省略return。 在类中定义的方法为实例方法,实例方法可以在类的实例对象上使用。如果是在一个特定的对象上定义方法,那么此方法就是一个单键方法,只能在这个对象上使用。 class Test def instance_method_test(v) puts "#{v}: instance method" end end t1=Test.new t2=Test.new t1.instance_method_test("t1") t2.instance_method_test("t2") def t1.singleton_method_test(v) puts "#{v}:singleton method" end t1.singleton_method_test("t1") #t2.singleton_method_test("t2") 在ruby中,方法一般小写字母开头,如果超过一个单词,则由下划线隔开(看习惯)。有意思的是方法名可以以等号、问号、叹号结尾,且有不同的意义:以等号(=)结尾表示这是一个赋值方法(写属性);以问号(?)结尾表示一个断言方法,可以回答调用者提出问题的值,例如判断大小;叹号(!)结尾表示是一个可变方法,可能会影响到对象的状态,要小心使用。这三个符号很有意思,但它们不是必须的。 (2)取消方法 通过undef 方法名 可以取消方法。对于被继承的方法,undef可以取消子类中的继承来的方法,但不会取消父类中的此方法。 (3)方法参数 在参数中添加等号和值就可以为这一个参数设置默认值。如果参数有默认值,那么在调用方法时,可以为这个参数指定值或不指定值。 def createRole(name,level=1) puts "role name is#{:name} level is #{level}" end createRole("way") createRole("clound",5) 通过在参数前边加星号(*),可以为方法指定为可变参数个数(数组参数),且指定的个数最多为一个。 def add_Person(*users) puts users end add_Person("a1","a2") add_Person("a1","a2","a3") 如果要把数组传递给方法,要在数组前加星号(*)。 (二)代码块 方法调用后可以紧跟一个代码块,关联代码块的方法可以通过yield来调用。 def showMessage(i) yield i*2 print i end showMessage(5){|x|print "#{x}"} 代码块通过yield调用,也可以做为方法参数传递,但前提是这个参数前需要添加&做为前缀且为最后一个参数。而此时它以一个proc对象传递,调用时不能通过yield调用,而是通过proc的call方法调用。另外,如果以显示方式,即传递proc对象给方法,那方法定义的参数就不加&前缀了。通过&前缀,代码块可以与任何方法调用关联起来,即使此方法没有yield语句。任何方法调用都可以用&参数作为最后一个参数。所有支持to_proc的方法都能用&。 #传递代码块,代码块参数须为最后一个,且添加&前缀;且调用时用proc的call方法 def fun_block(i,&b) b.call(i*2) end fun_block(2){|x|puts x} #显示传递proc对象。那么方法中proc对象部分的参数不加&前缀 def fun_proc(i,b) b.call(i*2) end p=Proc.new{|x|puts x} fun_proc(2,p) c=[1,2,3,4,5,6] b=c.select{|x|x%2==0} p1=Proc.new{|x|x%2==0} d=c.select(&p1) puts d 代码块是Ruby的一种句法结构,不是对象,但可以创建对象来表示一个代码块。根据对象的创建方式,分为proc或lambda。proc行为与代码块相似;lambda行为与方法相似,但它们都是Proc类的实例。 通过Proc.new创建一个proc,在1.9版本中,与proc方法为同义词;通过lambda方法,创建一个lambda。lambda方法不带参数,调用时段关联一个代码块。 在ruby 1.9中,支持一种新的句法。将lambda换为->;将参数放到花括号外边,小括号里边;花括号中只保留表达式。(可以与.net3.*中的lambda比较:()=>{}) #1.8k中 d1=lambda{|x|x+1} puts d1.call(5) #1.9中,将lambda换为->;参数放到花括号外边,小括号里边;花括号中只保留表达式 #与.net3.*中的lambda表达式很像()=>{} d2=->(x){x+1} d2.call(5) 这种新句法,使代码简洁,并使代码段与ruby方法的统一,例如:设置参数默认值。 (三)闭包 ruby中的proc和lambda都是闭包(closure)。闭包表示一个对象既是一个可调用的函数,同时也是绑定在这个函数上的一个变量。从表面上解释闭包就是:方法A内的方法B调用方法A的变量,并返回结果。方法B就是闭包。它的作用:一是B方法的变量可以在方法外使用;二是A方法中的变量可以安全使用;三是对B方法中的值进行缓存。 (关于闭包,可以查看相关文档,我这里只是粗浅的认识。) 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)条件判断 条件判断为基本式: if expression code end 表达式如果不是false或nil,则可执行code部分,所以对于else来说,如果为false或nil,则else下的代码是要执行的。要注意的是表达式与执行代码之间要有分界符:例如换行或分号或是then关键字。 多条件分支情况下,除了可以用else if之外,可以用省略式:elsif if expression1 code1 elsif expression2 code2 else code3 end 如果通过then做为分界符: if expression then code end,那么可以把if直接做为分界符:code if expression,这时,if称为表达式修饰符。先写执行再写执行条件。要注意的是:如果成为表达式修饰符,则执行代码与if之间不能带换行符。虽然这种方式也是一种条件句,但我觉得它更像一种修饰。 对于if判断来说,它可以返回执行代码后的值。 与if条件类似,还有unless判断,它和if相反。 在C#中,多分支语句有个switch case开关,在ruby中,则是case when。case when非常灵活。 (二)循环 循环条件是while do或until do,而位于它们之间的是循环体。 现条件if相似,循环语句也可以用分界符的形式做紧凑循环,这时,省略了换行符和end。 x=1 puts x,x+=1 while x<10 while x<20 do puts x x+=1 end 在C#中有for循环应用非常广泛,在ruby中,for循环与foreach的作用类似,用于对可枚举对象进行迭代。 for var in collection do code end 其中collection是带有each迭代方法的对象;do关键字可选(可以用换行符或分号替代)。 arr=%w[1 2 3 4 5 6] for item in arr print item<<" " end hash1={:a=>1,:b=>2,:c=>3} for key,value in hash1 print "#{key}=#{value}"<<" " end (三)迭代器和枚举 除了循环控制while、for、until外,有种特殊的方法用于循环,也就是迭代器,它是ruby最重要的特性之一。 (1)数值迭代器 upto:为一个区间内的所有整数调用其关联的代码块。左边为起始,右边为结束。 downto:与upto相反。 times:调用关系的代码一定的次数,且把0到n-1传递给代码。 step:以一定的步长,迭代增加到指定数值。第二个参数为步长。 1.upto(5){|x|print "#{x} "} puts 5.downto(1){|x|print "#{x} "} puts 3.times{|x|print "#{x} "} puts 3.step(5,0.5){|x|print "#{x} "} (2)枚举迭代器 each:将迭代元素传递给代码。 collect:为调用它的枚举的每个元素执行关联代码后,组合到一起做为一个数组返回。 select:为调用它的枚举的每个元素执行关联代码后,如果为真,组合到一起做为一个数组返回。 reject:与select相反。它把为false或nil的元素做为一个数据返回。 inject:求累积值迭代。通过两个参数调用关联代码块,第一个参数是前次调用代码块的累积值,第二个参数指调用进行迭代的下一个元素。如果有传递参数,则做为第一个参数的初始值,如果没有,则把第一次进行迭代的元素值做为初始值。 a1=[1,2,3] a2=a1.map{|x| x+1} a3=a2.collect{|x| x+1} print a2,a3 #o偶数 a5=a1.select{|x|x%2==0} print a5 a6=a1.reject{|x|x%2==0} print a6 puts a7=a1.inject{|sum,x|x+sum} puts a7 a8=a1.inject(10){|sum,x|x+sum} puts a8 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一) 加密服务提供程序 缩写为:CSP,全称为:Cryptographic Service Provider 即加密服务提供者。它是windows系统中提供的用于加密的软硬件组件。通过对微软的加密应用程序接口即CrytoAPI(或CAPI)的编程访问它来提供身份验证、编码和各种的加密、签名算法。 如下图: 应用程序不是直接与某个CSP通信。它会调用CAPI的函数,然后操作系统通过CrytoSPI(一个系统接口)筛选这些调用并且把这些调用传递到适合的CSP函数。然后CSP通过对参数的解析调用适当的CSP函数,最后向操作系统返回期盼值(加密操作是由特定的CSP来完成的)。 每个CSP是实现全部加密操作的独立模块。每个应用程序中至少需要一个CSP完成加密操作。对于使用多个CSP的情况下,在加密函数调用时需要指定CSP。当然基本加密服务是默认绑定到CAPI的。每一个CSP对CAPI提供不同的实现,例如某些实现了对硬件的支持。 CSP有多种类型,且数目在增加中,以下列举几种: Microsoft Base Cryptographic Provider Microsoft Strong Cryptographic Provider Microsoft Enhanced Cryptographic Provider Microsoft AES Cryptographic Provider Microsoft DSS Cryptographic Provider …… 更多内容可见:http://msdn.microsoft.com/en-us/library/aa386983.aspx CAPI的功能:向应用开发人员提供对基于windows的应用程序添加验证、编码和加密的能力。 CryptoSPI的功能:将CryptoAPI的调用传递给CSP,可以理解为使用CSP。 (二) CryptoAPI 全称:(Microsoft)Cryptography Application Programming Interface即(微软)加密应用编程接口,也缩写为:CAPI。 CAPI提供一套安全相关的函数集合,用于加密、摘要、数字签名等。 (三) 密钥存储区 密钥可以保存在磁盘、内存或硬件密钥存储区中。 例如:可以以文件的形式保存在磁盘中或保存在密钥容器中;也可以保存在例如智能卡硬件的密钥存储区中。 (四) 公钥加密Windows密钥库 Windows系统提供两种密钥存储类型(级别): 用户级别:User key store 存储在特定用户的 Windows 用户配置文件中 计算机级别:machine key store 对于所有可以登录到计算机的用户都可用,而且可以使用 ACL 限制对加密密钥信息的访问权限 (五) 密钥容器 Key container。密钥容器是保存密钥的最小单位,包含了密钥和其它信息。 (六) .net中的CSP 在.net中,以CryptoServiceProvider结尾的类是相应的 CSP 的托管代码包装类,即实现加密服务的提供者,名字空间:System.Security.Cryptography。 以TripleDESC私钥加密为例: public void Test_7_TripleDES(){ string strContent = "123aaa"; string strKey = "qwuduII*6&*%^&(90))"; TripleDESCryptoServiceProvider DES = new TripleDESCryptoServiceProvider(); MD5CryptoServiceProvider hashMD5 = new MD5CryptoServiceProvider(); DES.Key = hashMD5.ComputeHash(Encoding.UTF8.GetBytes(strKey)); DES.Mode = CipherMode.ECB; ICryptoTransform DESEncrypt = DES.CreateEncryptor(); byte[] Buffer = Encoding.UTF8.GetBytes(strContent); byte[] sipherbytes= DESEncrypt.TransformFinalBlock(Buffer, 0, Buffer.Length); Console.WriteLine(Encoding.UTF8.GetString(sipherbytes)); ICryptoTransform DESdecrypt = DES.CreateDecryptor(); byte[] plainbytes = DESdecrypt.TransformFinalBlock(sipherbytes, 0, sipherbytes.Length); Console.WriteLine(Encoding.UTF8.GetString(plainbytes));} (七) .net中的CspParameters类 CspParameters类,名字空间为:System.Security.Cryptography,表示可从非托管CrytoAPI传递到内部使用加密服务提供程序CSP的托管加密类的参数,即向CSP传递执行加密操作的参数。 它的作用: 1 指定特定的CSP。 2 管理密钥容器 3 指定签名密钥或交换密钥 重要成员: ProviderType:指定提供程序的类型(按数值)。如下表 ProviderName:指定提供程序的类型(按名字)。如下表 提供程序类型 数值 PROV_RSA_FULL 1 PROV_RSA_SIG 2 PROV_DSS 3 PROV_FORTEZZA 4 PROV_MS_EXCHANGE 5 PROV_SSL 6 PROV_RSA_SCHANNEL 12 PROV_DSS_DH 13 PROV_EC_ECDSA_SIG 14 PROV_EC_ECNRA_SIG 15 PROV_EC_ECDSA_FULL 16 PROV_EC_ECNRA_FULL 17 PROV_DH_SCHANNEL 18 PROV_SPYRUS_LYNKS 20 PROV_RNG 21 PROV_INTEL_SEC 22 PROV_REPLACE_OWF 23 PROV_RSA_AES 24 以PROV_RSA_FULL类型为例: 用途 算法支持 KeyExchange RSA Signature RSA Encryption RC2 RC4 Hashing MD5 SHA 更多内容可见: http://msdn.microsoft.com/en-us/library/aa380244.aspx KeyNumber:指定密钥用作签名密钥还是交换密钥。 交换密钥是一个不对称密钥对,用于加密会话密钥,以便可以安全地存储会话密钥并与其他用户交换会话密钥。通过Exchange值(1)指定交换密钥。与CAPI中使用的AT_KEYEXCHANGE 值对应。 签名密钥是不对称密钥对,用于对数字签名的消息或文件进行身份验证。通过Signature值(2)指定签名密钥。与CAPI中使用的AT_SIGNATURE 值对应。默认情况下,KeyNumber字段指定交换密钥。 对这个属性对应的有一个同名的枚举KeyNumber: Exchange 一个交换密钥对,用于加密会话密钥以使它们可以安全存储并与其他用户交换。 Signature 一个签名密钥对,用于对数字签名的消息或文件进行身份验证。 KeyContainerName:密钥容器名。通过它可以管理密钥容器:创建、获取、释放。 示例:密钥容器创建 public void Test_9_keycontainer(){ Encoding _encoding=Encoding.UTF8; string strPlaintext = "这是abc123!"; byte[] bPlaintext = _encoding.GetBytes(strPlaintext); byte[] bEncrypt; byte[] bDecrypt; //创建容器 CspParameters cspParams = new CspParameters(); cspParams.KeyContainerName = "selfkeys"; cspParams.ProviderType = 1;//PROV_RSA_FULL //创建密钥对,并添加到容器中 RSACryptoServiceProvider provider = new RSACryptoServiceProvider(cspParams); //公钥加密 RSACryptoServiceProvider _provider1 = new RSACryptoServiceProvider(cspParams); bEncrypt = _provider1.Encrypt(bPlaintext, true); Console.WriteLine(_encoding.GetString(bEncrypt)); //私钥解密 RSACryptoServiceProvider _provider2 = new RSACryptoServiceProvider(cspParams); bDecrypt = _provider2.Decrypt(bEncrypt, true); Console.WriteLine(_encoding.GetString(bDecrypt));} 未完待续…… 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)javascript表达式 表达式是什么?表达式是对变更进行赋值、更改或计算等操作的语句。它是变量、常量、操作符的综合。根据操作符的不类型,可以分为字符操作表达式、赋值表达式、逻辑表达式、关系表达式、自增自减表达式、位表达式等。 例如: x+y 5>0 A++ Z=1+1 另外还有一种逗号表达式:x+y,z++ (二)小括号 最常见的小括号的用法有好多种,并且常用。 (1)函数的输入参数部分 例如:function fun1() (2)语句的辅助 例如:while();if();for() (3)类对象的构造方法 Var now=new Date() (4)强制表达式运算 这个作用其实也在用,但没有前边3种用得明晰。 它意思就是指:把其中的内容作为表达式强制运算后得到结果。 例如: function test(){ var x = 0; var y = 1; alert(++x, ++y); alert((++x, ++y));} (5)函数调用 一般情况下调用函数为函数名+参数,例如:doTest(1) 还有一种方式就是把函数名也用小括号包围起来,例如:(doTest)(1) 这也是两个小括号的意义。 (三)中括号 中括号主要用于数组 (1)可以声明空数组或赋值,例如: var list1 = []; var list2 = [1, 2, 3]; (2)通过下标访数组元素 alert(list2[1]); 其次就是用于kv对的对象,例如: 对于customer对象,有name属性,那么取对象的name值为: Customer[‘name’] (四)大括号 常用的就是语句块标识符。例如函数体,循环体等。另一种就是定义js类。 例如:var customer = {}; 可见:http://www.cnblogs.com/jams742003/archive/2011/03/04/1970506.html (五)Eval 它的作用就是把字符串做为表达式进行运算,并返回值。 详细可见:http://www.cnblogs.com/jams742003/archive/2009/12/29/1634736.html (六)Void 计算表达式,但不返回值。 例如: var x = 1; void (x++); alert(x); alert(typeof (void (x++))); alert(x); 详细请见:http://www.cnblogs.com/jams742003/archive/2010/01/13/1646631.html (七) Jquery库定义的原理分析 (1)如何通过一个变量进行访问所有成员 所有的成员,例如函数,变量等通过一个固定的变量访问,可以通过静态实现。那么就要创建一个类,且,类中的成员要定义为静态。所以,先试着创建一个jQuery类,它的成员为静态。如下: function doClick(){ jQuery.showMessage();} function jQuery(){ }jQuery.showMessage = function(){ alert('ok');} (2)jQuery类可以通过另一种方式实现 定义js类有好几种方式,其中一种叫字面量的实现方式: var jQuery = function() { };jQuery.showMessage= function(){ alert('ok');} (3)包装 为了避免命名冲突,现在把这个jQuery类包装起来。可以考虑再放到一个类中或者通过匿名函数进行包装(其实,本义是相同的)。在现在这种情况下,要通过后者方式进行,即以匿名函数进行包装。如下: function(){ var jQuery = function() { }; jQuery.showMessage = function() { alert('ok'); }} (4)设置全局访问并添加$ 如下: function(){ var jQuery=window.jQuery=window.$= function() { }; jQuery.showMessage = function() { alert(1); }} (5)让匿名函数运行 调用函数的方法可以通过函数名和输入参数。其中函数名也可以加小括号。那么: ( function() { var jQuery = window.jQuery = window.$ = function() { }; jQuery.showMessage = function() { alert(1); } })(); (6)完成并调用 function doClick(){ jQuery.showMessage(); $.showMessage();} 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
准备工作: (一)js函数 Js函数基本的定义语法为: function 函数名(arg1,arg2,……) {} 和通常意义上的函数(方法)相似。可以有返回值。 例如: <script>function noReturnValue(){ } function hasReturnValue(){ return 'ok';} function doClick(){ alert(noReturnValue()); alert(hasReturnValue());}</script> 函数noReturnValue没有返回值,所以打印结果为:undefined hasReturnValue返回ok字串。 (二)Function对象 ECMAScript有一类对象为:Function对象。它表示定义的任何函数(function) var function_name = new Function(arg1, arg2, ..., argN, function_body) 示例: function doClick2(){ var fun = new Function("a1", "alert(a1);"); fun('ok');} 通过Function定义的函数对象,最后一个参数为函数体,前边的参数。两部分都以字符串类型存在。通过字符串的定义方式来定义较困难,但它指示函数是一种引用类型,函数是Function类型的一个实例,而函数名而是一个指向函数的变量。 (三)函数类型 可以通过typeof来得到js对象的类型。 示例一(类型): function doClick3(){ var fun = new Function("a1", "alert(a1);"); alert("Object:"+typeof (Object) + "\r\n" + "Date:" + typeof (Date) + "\r\n" + "Array:" + typeof (Array) + "\r\n" + "Function:" + typeof (Function) + "\r\n" + "new Function():" + typeof (new Function()) + "\r\n" + "function:" + typeof (fun) + "\r\n" + "new Date():" + typeof (new Date()) + "\r\n" + "new Array():" + typeof (new Array()) + "\r\n" + "new Object():" + typeof (new Object()) + "\r\n" );} 对于对象来说它们的类型做为一种函数存在;创建完的对象除Function外,做为对象类型(object)存在。 示例二(构造方法): function doClick4(){ var fun = new Function("a1", "alert(a1);"); alert(fun.constructor); var obj = new Array(); alert(obj.constructor); var i = 0; alert(i.constructor); var num = new Number(); alert(num.constructor);} 打印的内容除第一个外都是以function 函数名(){}。那么这些对象都是通过function定义的函数对象。函数对象可以构造对象。 而函数对象的构造方法为: function Function(){ [native code] } 示例三(函数对象构造对象): function doClick5(){ var no = new Number(); alert("对象类型:" + typeof (no) + "\r\n构造方法:" + no.constructor); alert("对象类型:" + typeof (hasReturnValue) + "\r\n构造方法:" + hasReturnValue.constructor); var conobj = hasReturnValue(); alert("对象类型:" + typeof (conobj) + "\r\n构造方法:" + conobj.constructor); var fun = new Function("a1", ""); var mess1 = new fun('ok'); alert(typeof (fun)); alert(typeof (mess1)); alert(mess1.constructor);} No是一个对象;hasReturnValue是一个函数对象;fun是一个函数对象。函数对象可以构造对象,所以可以有: var conobj = hasReturnValue(); var mess1 = new fun('ok'); 类定义 在java、c#中,类可以存放数据,其中最基本的类型有属性、方法、域。在js中创建(js)类有相似的功能,它的类强调的是一种复合类型。它允许通过变量名进行读取(例如js函数名可做为变量名使用)。而进行访问时,通过键对值方式进行。在C#中,访问属性其实也是这样的,例如,Customer类有公有的Name属性性,其中Name属性有读写两个属性器。假设Name属性为静态属性,则可以通过Customer.Name来访问,赋值或得到值。从大的方面讲,是一种以KV对方式进行访问。 (一) 以大括号方式定义 例如: var customer = {};var product = { name:'mp3', price:0};function doClick(){ alert(typeof (customer)); alert(typeof (product)); alert(typeof (Date));} 定义了两个js类:customer和product。通过测试它们的类型(typeof),它们做为一种object类型存在。那么它们不可以通过new方式创建(函数对象可以创建对象,例如Date)。 (二)函数对象 示例一(类型): 函数对象可以创建对象,那么可以通过function来模拟类类型。 function user(){ this.username = ''; userage = 0;} function doClick2(){ alert(typeof (user)); var user1 = new user(); user1.username = "宋江"; alert(user1.username);} 这里定义了一个user类(函数) 在doClick事件(函数)中,可以得到user的类型为function,它可以用于创建对象,并可以访问。在user类中,有两个属性(也可以看做是域),其中username通过this关键字定义,userage是直接定义。创建完user对象后,即user1,可以访问username和userage var user1 = new user(); user1.username = "宋江"; alert(user1.username); 示例二(为类添加函数): function user(){ this.username = ''; userage = 0; this.showMessage = function() { alert('user类的函数'); }}function doClick3(){ var user1 = new user(); user1.showMessage();} 对于类中的函数来说,有私有和公有访问性。通过var定义的为私有函数,通过this定义的为公有函数。 示例三(属性定义与函数定义): 在通过function定义类类型时,其中的属性(区别类中的函数)可以通过三种修饰符进行,如下: function clsProperty(){ p1 = 1; var p2 = 2; this.p3 = 3;}function doClick4(){ var cls = new clsProperty(); alert(cls.p1); alert(cls.p2); alert(cls.p3); cls.p1 = 1; cls.p2 = 2; cls.p3 = 3; alert(cls.p1); alert(cls.p2); alert(cls.p3); clsProperty.p1 = 1; clsProperty.p2 = 2; clsProperty.p3 = 3; alert(clsProperty.p1); alert(clsProperty.p2); alert(clsProperty.p3);} 三种修饰中在添加this关键字时,具有良好的访问支持,但无修饰或var修饰时,不可以进行操作。Var是用于定义局部变量的关键字。在定义属性时,可以把它理解为私有访问修饰符,而this则可以理解为公有访问修饰符。 function clsProperty1(){ p1 = function() { alert('p1'); }; var p2 = function() { alert('p2'); }; this.p3 = function() { alert('p3'); };}function doClick5(){ var cls = new clsProperty1(); cls.p1(); cls.p2(); cls.p3(); clsProperty1.p1(); clsProperty1.p2(); clsProperty1.p3();} P1为无修饰符,p2为var修改符,p3为this修饰符。在doClick5中,cls.p3()和clsProperty1.p2()可以访问。所以,对于js类中的函数来讲: Var修饰符类似C#中的静态方法(函数)修饰符;this类似实例方法(函数)修饰符。 所以对于js类中的属性来讲,也可以把var和this修饰符做为相似的比较: Var修饰符用于私有属性修饰符;this类似公有属性修饰符。 (三)js类中的静态实现 function user(){}user.age = 30;user.showAge = function() { alert(user.age); } function doClick(){ user.showAge(); user.age = 20; user.showAge();} 通过在类外进行设置,可以用于静态访问。 (四)js类中的实例实现 在上一篇中,通过this关键字实现实例属性和实例方法(函数)。还有一种通过prototype关键字实现。 function user(){}user.age = 30;user.showAge = function() { alert(user.age); } user.prototype.showMessage = function(){ alert('实例方法');}function doClick1(){ var user1 = new user(); user1.showMessage(); } (五)构造方法 以上的例子中,创建类,构造方法都是无参数的。 function user(){} 下边是个带参数的: function product(id, name, price){ this.id = id; this.name = name; this.price = price;} 在调用进,分为两种情况: 情况一:正好符合参数个数,正常调用;情况二:少参数,则从左到右匹配,没有匹配的为未定义;情况三:多参数,按正常匹配。 function product(id, name, price){ this.id = id; this.name = name; this.price = price; this.getString = function() { return("id:"+this.id+" name:"+this.name+" price:"+this.price); }}function doClick2(){ var pro1 = new product(1,'mp3',100); alert(pro1.getString()); var pro2 = new product(); alert(pro2.getString()); var pro3 = new product(1); alert(pro3.getString()); var pro4 = new product(1, 'mp3'); alert(pro4.getString());} 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
计时器用于到指定时间调用委托(或说执行方法),或定时调用委托。 (一) Timer类 名字空间:System.Threading 它主要提供在指定时间间隔内循环执行方法。 它的构造方法有多个,以其中一个为例: public Timer(TimerCallback callback,Object state,int dueTime,int period) 四个参数分别表示: 要执行的方法;包括回调方法中要使用的信息的对象;在调用执行的方法之前的延迟时间(毫秒);调用回调方法的时间间隔(毫秒) TimerCallback的定义为: public delegate void TimerCallback(Object state) 示例: static void Main(string[] args) { int k = 19; Timer timer1 = new Timer(new TimerCallback(PrintMessage), k, 2000, 5000); Console.Read(); } static void PrintMessage(object objState) { Console.WriteLine("ok1" + " " + objState.ToString()); } 设置2000毫秒后开始调用执行方法,每5000毫秒执行一次,并把k传递到执行方法(callback委托) 说明:2000毫秒后首次执行一次回调方法,然后每5000毫秒后再执行。 回调委托类型已经给出,在使用时可以Action委托或lambda表达式或匿名委托来定义,如下: Action<object> ffun = delegate(object o){ Console.WriteLine("ok3"+" "+o.ToString());};Timer timer2 = new Timer(new TimerCallback(q => Console.WriteLine("ok2" + " " + q.ToString())), k, 2000, 5000);Timer timer3 = new Timer(new TimerCallback(ffun), k, 2000, 5000); 如果终止计时器,可以使用public void Dispose()方法 例如:设置定时器,2秒钟后开始,每2秒调用一次,然后在调用5次(首次除外)后释放计时器。 static int k = 1;static Timer timer1;static void Main(string[] args){ timer1 = new Timer(new TimerCallback(PrintMessage), k, 2000, 2000); Console.Read();} static void PrintMessage(object objState){ Console.WriteLine("ok1" + " " + k.ToString()); if (k > 5) timer1.Dispose(); k++;} (二) System.Timers.Timer类 在应用程序中生成定时事件。它有一个属性:AutoReset public bool AutoReset { get; set; } 用于设定每次指定的间隔结束时引发 Elapsed 事件,还是仅在指定的间隔第一次结束后引发该事件。 重要成员说明: Interval:设置时间间隔,单位毫秒 Enabled:是否引发Elapsed事件 Elapsed:事件 public event ElapsedEventHandler Elapsed 其中事件类型: public delegate void ElapsedEventHandler(Object sender,ElapsedEventArgs e) sender为Timer,eventargs的重要属性为:public DateTime SignalTime { get; } 用于触发计时事件的时间。 示例: static void Main(string[] args){ System.Timers.Timer timer1 = new System.Timers.Timer(); timer1.Elapsed += new System.Timers.ElapsedEventHandler(OnTimedEvent); timer1.Interval = 2001; timer1.Enabled = true; timer1.AutoReset = true; Console.Read();} static void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e){ System.Timers.Timer tempTimer = source as System.Timers.Timer; Console.WriteLine(tempTimer.Interval); Console.WriteLine(e.SignalTime);} 打印内容略。 (三) System.Windows.Forms.Timer 用在winform中,且必须用于窗口。略。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
把所有的配置完成后,就可以进行sl开发了。 新建立silverlight项目,在建立的过程中,会弹出选项建立引用此silverlight项目的web项目。确定后,建立完毕。 我现在建立的sl项目名称为:SLApp 此项目下自动生成一些文件,其中的两个文件: App.xaml App.xaml.cs MainPage.xaml MainPage.xaml.cs 发现与aspx文件规则很相似。 其中的App.xaml文件用于应用程序声明共享的资源,隐藏的代码文件中可以处理像gllbal.asax文件功能相似的事情。例如:Application_Startup事件 另一个文件MainPage.xaml文件是一个默认的xaml文件,是一个开始页面。 加载这个MainPage.xaml控件的方法是: App.xaml.cs中的: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->private void Application_Startup(object sender, StartupEventArgs e){ this.RootVisual = new MainPage();} 现在做一个简单的长方形。 (一) 在sl项目中添加sl用户控件,名字为SelfRectangle.xaml 现在xaml文件暂时叫做视图文件,cs文件叫代码文件。 添加了用户控件后,视图文件结构为: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><UserControl x:Class="SLApp.SelfRectangle" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Grid x:Name="LayoutRoot" Background="White"> </Grid></UserControl> 这里边包括两个结点: ·UserControl,根元素,做为视图文件中最高级别的元素。所有的UI元素都要在这里边 ·Grid,布局面板控件,这种布局面板控件有三种,这是其中的一种。所有的UI元素都要放到布局中。 现在添加一个长方形,即在Grid结点内添加: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><Rectangle Fill="Red" Width="100px" Height="100px"></Rectangle> 其中的属性很好理解:填充色:Red,长宽 同时,把Grid的属性也定义一下,现在变成: 代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><UserControl x:Class="SLApp.SelfRectangle" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="400" Height="300"> <Grid x:Name="LayoutRoot" Background="Blue"> <Grid.RowDefinitions> <RowDefinition Height="120"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200"></ColumnDefinition> </Grid.ColumnDefinitions> <Rectangle Fill="Red" Width="100" Height="100" Grid.Row="0" Grid.Column="0"></Rectangle> </Grid></UserControl> (二) 设置这个控件为默认的引用控件 在App的代码文件中更改: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->private void Application_Startup(object sender, StartupEventArgs e){ this.RootVisual = new SelfRectangle();} (三) 在网站项目中添加新页RectangleTest.aspx 引入silverlight控件,aspx文件变成了: 代码 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><form id="form1" runat="server"> <div> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:Silverlight ID="slRectangle" runat="server" Source="~/clientbin/SLApp.xap"></asp:Silverlight> </div></form> 这里只贴出了form之间的代码部分。 说明,asp:Silverlight可能不存在引用。这个方法大家都知道: 1 添加System.Web.Silverlight.dll引用 2 在web项目中的Web.config文件中添加asac控件的注册,即: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><controls> <add tagPrefix="asp" namespace="System.Web.UI.SilverlightControls" assembly="System.Web.Silverlight"/></controls> 引用中的Source属性,引用的是SLApp.xap文件。 在web项目中,有ClientBin目录,其中有SLApp.xap文件。它与建立的Silverlight项目名称相同,但扩展名不同,这里是xap(读zap),是一种压缩文件。可以通过解压缩工具打开。它就是silverlight项目生成后,bin中的打包文件,包括dll文件和其它文件。 (四) 生成silverlight项目并预览RectangleTest.aspx文件 可以看到效果。 同样,Hello!的制作。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--><dataInput:Label Name="lmsg" Content="Hello!"></dataInput:Label> 总结: 建立第一个SL,其实步骤很简单。安装完Silverlight3.0后,在创建项目时,会有silverlight模板,这里就不贴图了。 1 建立Silverlight应用程序 2 在建立的过程中,添加引用此Silverlight项目的网站,这个会弹出窗口,选择就可以了 3 创建完成后,会有两个项目:一个silverligh项目,一个网站 4 然后就按照步骤做就可以了 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
开发环境:VS2008 sp1 如果要开发silverlight,需要安装3部分: 1 SDK 用来编译和生成所需要的sl控件的工具 2 Tools 3 sl运行时插件,用于在浏览器中浏览sl,即在浏览器中使用silverlight控件 我在安装的过程中,先单独安装了第三部分,即运行时。然后安装其它两部分。结果在调试时出现了异常: 在vs中进行编译时,会提示缺少托管调试包。解决方案是: 1 卸载上面安装的第三部分 2 下载silverlight4.0 tools,解压缩后,运行其中的silverlight_developer.exe文件 就可以解决。 其实在安装第二部分,即Tools时,会同时安装运行时等。所以直接安装SDK和Tools。 在3.0版本中,安装后,不会有System.Web.Silverlight.dll文件,这个还要手动下载。下载后手动添加文件即可。 3.0SDK下载地址: http://www.microsoft.com/downloads/details.aspx?familyid=1EA49236-0DE7-41B1-81C8-A126FF39975B&displaylang=zh-cn 3.0Tools For VS2008 SP1下载地址: http://www.microsoft.com/downloads/details.aspx?familyid=9442B0F2-7465-417A-88F3-5E7B5409E9DD&displaylang=zh-cn 库文件3.0(silverlight项目)下载地址: http://code.msdn.microsoft.com/aspnetprojects/Release/ProjectReleases.aspx?ReleaseId=2957 4.0Tools For VS2010下载地址: http://www.microsoft.com/downloads/details.aspx?familyid=B3DEB194-CA86-4FB6-A716-B67C2604A139&displaylang=zh-cn 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)Do(delegate) 有时候在测试过程中只返回一个静态的值是不够的,在这种情况下,Do()方法可以用来在方法调用时添加自定义的行为。一般来说,Do()方法会替换方法调用。它的返回值会从模拟的调用中返回(即使是有异常发生也是这样)。Do()的参数委托委托的方法的签名须和方法的签名匹配。只有当签名匹配时才能生效,且一个匹配生效一次。 看官方给出的例子: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class Speaker{ private readonly string firstName; private readonly string surname; private INameSource nameSource; public Speaker(string firstName, string surname, INameSource nameSource) { this.firstName = firstName; this.surname = surname; this.nameSource = nameSource; } public string Introduce() { string name = nameSource.CreateName(firstName, surname); return string.Format("Hi, my name is {0}", name); }} Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public interface INameSource{ string CreateName(string firstName, string surname);} 现在演讲者和名字分开在两个类中。然后进行自我介绍,介绍时要介绍自己的姓名,即FirstName+LastName。在介绍中要用到InameSource中的CreateName方法,接下来将会模拟这个接口,而通过其它的方法来替代。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void SayHelloWorld(){ MockRepository mocks = new MockRepository(); INameSource nameSource = mocks.DynamicMock<INameSource>(); Expect.Call(nameSource.CreateName(null, null)) .IgnoreArguments() .Do(new NameSourceDelegate(Formal)); mocks.ReplayAll(); string expected = "Hi, my name is Ayende Rahien"; string actual = new Speaker("Ayende", "Rahien", nameSource).Introduce(); Assert.AreEqual(expected, actual);}delegate string NameSourceDelegate(string first, string surname); private string Formal(string first, string surname){ return first + " " + surname;} 看上段测试的粗体部分。 Do参数是委托类型,其中这个委托类型委托的方法的签名要和模拟对象中期望的要替换的方法的签名一致,即: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->private string Formal(string first, string surname)string CreateName(string firstName, string surname); 两者相匹配。 然后当对演讲者构造时:new Speaker("Ayende", "Rahien", nameSource) 会对演讲者三个域进行赋值 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->private readonly string firstName;private readonly string surname;private INameSource nameSource; 接下来进行介绍时,调用方法: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public string Introduce(){ string name = nameSource.CreateName(firstName, surname); return string.Format("Hi, my name is {0}", name);} 而这个方法则由Do方法的委托参数委托的方法来替代: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->private string Formal(string first, string surname){ return first + " " + surname;} 返回FirstName+空格+LastName (二)With 流畅式的期望和验证语法。什么是流畅式?先看例子: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestFluent(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); string strTemp=string.Empty; With.Mocks(mocks) .Expecting( delegate { Expect.Call(customer.ShowTitle("")).Return("with 语句"); }) .Verify( delegate { strTemp = customer.ShowTitle(""); }); Assert.AreEqual("with 语句", strTemp);} 这就是所谓的流畅式。通过匿名委托来实现。如果在匿名委托中完成则会隐匿调用ReplayAll()和mocks.VerifyAll()。 如果要启用次序,则可使用:ExpectingInSameOrder,例如: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestFluent(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); string strTemp=string.Empty; With.Mocks(mocks).ExpectingInSameOrder( delegate { Expect.Call(customer.ShowTitle("")).Return("with 语句"); Expect.Call(customer.Unid).Return(1); }) .Verify( delegate { strTemp = customer.ShowTitle(""); int i = customer.Unid; }); Assert.AreEqual("with 语句", strTemp);} With语句的隐式使用 With可以隐式的创建Mock实例,并自动调用VerifyAll方法。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestWithMocker(){ With.Mocks( delegate { var customer = Mocker.Current.DynamicMock<ICustomer>(); Expect.Call(customer.ShowTitle("")).Return("with 语句"); Mocker.Current.ReplayAll(); Assert.AreEqual("with 语句", customer.ShowTitle("")); });} 这里才看出With确实很流畅。 下边说一下由显式创建Mock实例来代替隐式创建: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestWithMockerr(){ MockRepository mocks = new MockRepository(); With.Mocks(mocks, delegate { var customer = Mocker.Current.DynamicMock<ICustomer>(); Expect.Call(customer.ShowTitle("")).Return("with 语句"); Mocker.Current.ReplayAll(); Assert.AreEqual("with 语句", customer.ShowTitle("")); });} 没多大区别。在使用Mocker.Current时,不能在嵌套中使用,因为这是个全局的,而With.Mocks会重写Mocker.Current (三)Record-PlayBack Rhinomock支持一种通过Using语句来进行录制回放的方式。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestRecordPlayback(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.ShowTitle("")).Return("录制回放"); } using (mocks.Playback()) { Assert.AreEqual("录制回放", customer.ShowTitle("")); }} 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)安装结果(SetupResult) 有时候在模拟对象中需要一个方法的返回值,而不在意这个方法是否被调用。就可以通过安装结果(SetupRestult)来设置返回值,而绕开期望安装,且可以使用多次。从依赖的角度来说是这样的:方法a(或属性)被方法b使用,而在其它的位置c处方法a又会被使用,而在c处使用之前,不保证是否在b处使用且修改了方法a的返回值。意思就是保证方法a的返回结果是固定的,是忽略它的依赖,而在它该用的位置使用它恒定的值。安装结果可以达到这种效果。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class Customer{ public virtual int DescriptionId{get;set;} public virtual void PrintDescription() { DescriptionId=1; }} 属性DesriptionId被方法PrintDescription()依赖。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestSetupResult(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<Customer>(); SetupResult.For(customer.DescriptionId).Return(10); Expect.Call(delegate { customer.PrintDescription(); }).Repeat.Times(2); mocks.ReplayAll(); customer.PrintDescription(); customer.PrintDescription(); Assert.AreEqual(10, customer.DescriptionId);} 从这段测试中可以看到,对customer的DescriptionId属性进行了结果安装,只让这个属性返回10。而在随后对依赖它的方法进行了期望安装,且可以被调用2次。但DescriptionId的值仍是10。 Expect.Call(delegate { customer.PrintDescription(); }).Repeat.Times(2); 这句是对不带返回值的方法进行期望安装,当然可以使用Lambda来进行。这个匿名方法就是一个不带参数,没有返回值的委托,这个就相当于Action<>,通过lambda就是:()=>customer.PrintDescription(),完整就是: Expect.Call(()=>customer.PrintDescription()).Repeat.Times(2); 关于匿名方法和Action<T>委托可见: http://www.cnblogs.com/jams742003/archive/2009/10/31/1593393.html http://www.cnblogs.com/jams742003/archive/2009/12/23/1630737.html 安装结果有两种方法: For和On,For在上边已经使用,On的参数是mock object。对于上边的示例中的粗体部分用On来实现为: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->SetupResult.On(customer).Call(customer.DescriptionId).Return(10); 这个也可以通过期望的选项来实现。例如: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Expect.Call(customer.DescriptionId).Return(10) .Repeat.Any() .IgnoreArguments(); 其中的粗体部分,可以多次使用,且忽略参数。 (二)约束(Constraints) 约束用来对期望的参数进行规则约束。系统提供了大量内建的约束方法,当然也可以自定义。这里直接贴一张官网给出的列表,一目了然: 约束 说明 例子 接受的值 拒绝的值 Is 任何 Is.Anything() {0,"","whatever",null, etc} Nothing Whatsoever 等于 Is.Equal(3) 3 5 不等于 Is.NotEqual(3) null, "bar" 3 无 Is.Null() null 5, new object() 不为无 Is.NotNull() new object(), DateTime.Now null 指定类型 Is.TypeOf(typeof(Customer)) or Is.TypeOf<Customer>() myCustomer, new Customer() null, "str" 大于 Is.GreaterThan(10) 15,53 2,10 大于等于 Is.GreaterThanOrEqual(10) 10,15,43 9,3 小于 Is.LessThan(10) 1,2,3,9 10,34 小于等于 Is.LessThanOrEqual(10) 10,9,2,0 34,53,99 匹配 Is.Matching(Predicate<T>) 相同 Is.Same(object) 不相同 Is.NotSame(object) Property 等于值 Property.Value("Length",0) new ArrayList() "Hello", null 无 Property.IsNull ("InnerException") new Exception ("exception without inner exception") new Exception ("Exception with inner Exception", new Exception("Inner") 不为无 Property.IsNotNull ("InnerException") new Exception ("Exception with inner Exception", new Exception("Inner") new Exception ("exception without inner exception") List 集合中包含这个元素 List.IsIn(4) new int[]{1,2,3,4}, new int[]{4,5,6} new object[]{"",3} 集合中的元素(去重) List.OneOf(new int[]{3,4,5}) 3,4,5 9,1,"" 等于 List.Equal(new int[]{4,5,6}) new int[]{4,5,6}, new object[]{4,5,6} new int[]{4,5,6,7} Text 以…字串开始 Text.StartsWith("Hello") "Hello, World", "Hello, Rhino Mocks" "", "Bye, Bye" 以…字串结束 Text.EndsWith("World") "World", "Champion Of The World" "world", "World Seria" 包含 Text.Contains("or") "The Horror Movie...", "Either that or this" "Movie Of The Year" 相似 Text.Like ("rhino|Rhinoceros|rhinoceros") "Rhino Mocks", "Red Rhinoceros" "Hello world", "Foo bar", Another boring example string" 例子: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestConstraints(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); Expect.Call(customer.ShowTitle("")) .Return("字符约束") .Constraints(Rhino.Mocks.Constraints .Text.StartsWith("cnblogs")); mocks.ReplayAll(); Assert.AreEqual("字符约束", customer.ShowTitle("cnblogs my favoured"));} 它的意思就是如果参数以cnblogs开头,则返回期望值。可以比较一下Moq的参数约束设置方法: http://www.cnblogs.com/jams742003/archive/2010/03/02/1676197.html 除了上述方法外,rhinomock约束还支持组合,即与,非,或。还以上例进行: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestConstraints(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); Expect.Call(customer.ShowTitle("")) .Return("字符约束") .Constraints( Rhino.Mocks.Constraints.Text.StartsWith("cnblogs") && Rhino.Mocks.Constraints.Text.EndsWith("!") ); mocks.ReplayAll(); Assert.AreEqual("字符约束", customer.ShowTitle("cnblogs my favoured!"));} 参数的条件就是以cnblogs开头,且以!号结束。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)属性Property 属性也是一种方法。所以对于属性的期望,和方法是一样的。方法和属性的期望在前几篇随笔中已经大量使用。 通常的读或写属性器的期望 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestEvent(){ MockRepository mocks = new MockRepository(); IList<int> _list = mocks.DynamicMock<IList<int>>(); Expect.Call(_list.Count).Return(5); mocks.ReplayAll(); Assert.AreEqual(5, _list.Count);} 这个是很简单的。然后还有一种自动属性期望的,设置属性行为来达到自动属性期望安装。这个有两种方式,在前边说Mock四种类型时说过: 一是传统的一个一个的安装,还有一种方式就是通过Stub方式实现。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public interface ICustomer{ int Unid { get; set; } string CustomerName { get; set; } string Address { get; set; } string ShowTitle(string str);} 这个接口有3个属性。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestProperty(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.Unid).PropertyBehavior(); Expect.Call(customer.CustomerName).PropertyBehavior(); Expect.Call(customer.Address).PropertyBehavior(); } customer.Unid = 5; Assert.AreEqual(5, customer.Unid);} 通过这种方法要分别为mock对象设置属性行为。而通过Stub则很简单: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestPropertyStub(){ MockRepository mocks = new MockRepository(); var customer = mocks.Stub<ICustomer>(); customer.Unid = 5; Assert.AreEqual(5, customer.Unid);} 通过PropertyBehavior()方法可以为属性模拟行为。这上行为会在模拟对象的生命周期内有效。模拟对象(mock object)与模拟(mock)是两个概念。 (二)方法 属性器有读和写属性,在安装期望时,只是对写属性设置即可。在方法中有多种情况: (1)无返回值 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->void NoValues(string str); 安装期望: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Expect.Call(delegate { customer.NoValues(""); }); (2)带返回值 这个前边已经大量使用 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string ShowTitle(string str); 安装期望: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Expect.Call(customer.ShowTitle("")).Return("为空"); (3)带输出参数或引用参数的方法 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestOutPara(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); string strOut="123"; Expect.Call(customer .OutParas("", out strOut)) .Return("test").OutRef("xxx"); mocks.ReplayAll(); customer.OutParas("", out strOut); Assert.AreEqual("xxx", strOut);} 看粗体部分。带输出或引用参数的方法安装期望和正常调用的方式相似。 这里说明一下,RhinoMock3.5支持lambda和3.x扩展属性,所以在安装时可以这样: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->customer.Expect(p => p.OutParas("", out strOut)).Return("test").OutRef("xxx"); 这个要结合Action<T>和Func<Tresult>来分析,尤其是Action<T>。 (三)方法选项 可以对安装期望的方法设置选项(options),例如:安装完期望的方法的可调用次数。 设置方法是:Expect.Call或LastCall 这里分别说一下: (1)Return 当然最长见的就是返回值,为读属性或带返回值的方法返回值设置,与期望匹配。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestMethodOptions(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.Unid) .Return(10); } Assert.AreEqual(10, customer.Unid);} (2)Throw 异常抛出。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestMethodOptions(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.ShowTitle("")) .Throw(new Exception("不能为空")); } customer.ShowTitle(""); } 结果:failed: System.Exception : 不能为空 (3)方法允许使用的次数 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestMethodOptions(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.ShowTitle("")) .Return("不能为空") .Repeat.Once(); } Assert.AreEqual("不能为空",customer.ShowTitle("")); Assert.AreEqual("不能为空", customer.ShowTitle(""));} 安装期望时,允许调用1次。所以对于两次断言使用,会有异常出现。除了Once,还有Twice(),Any等 (4)忽略方法参数 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestMethodOptions(){ MockRepository mocks = new MockRepository(); var customer = mocks.DynamicMock<ICustomer>(); using (mocks.Record()) { Expect.Call(customer.ShowTitle(null)) .Return("不能为空") .IgnoreArguments(); } Assert.AreEqual("不能为空", customer.ShowTitle(""));} 请看粗体部分,忽略了字串参数。 (5)简单的自动属性 PropertyBehavior()已经说过,不再赘述 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
(一)次序(Ordered) 在rhinomock中,可以按次序调用方法。默认条件下,方法调用没有顺序。如果按次序录制,那么在调用方法时必须按录制时相同的次序进行。 请看: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public interface ICustomer{ string ShowTitle(string str); int Unid { get; set; } string CustomerName { get; set; } string Address { get; set; }} 测试: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void TestNoOrder(){ MockRepository mocks = new MockRepository(); ICustomer customer = mocks.StrictMock<ICustomer>(); //默认条件下是没顺序的 Expect.Call(customer.Unid).Return(1); Expect.Call(customer.CustomerName).Return("宋江"); Expect.Call(customer.Address).Return("山东"); mocks.ReplayAll(); Assert.AreEqual("宋江", customer.CustomerName); Assert.AreEqual(1, customer.Unid); Assert.AreEqual("山东", customer.Address);} 当使用次序时: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void TestOrder(){ MockRepository mocks = new MockRepository(); ICustomer customer = mocks.StrictMock<ICustomer>(); using (mocks.Ordered()) { Expect.Call(customer.Unid).Return(1); Expect.Call(customer.CustomerName).Return("宋江"); Expect.Call(customer.Address).Return("山东"); } mocks.ReplayAll(); Assert.AreEqual("宋江", customer.CustomerName); Assert.AreEqual(1, customer.Unid); Assert.AreEqual("山东", customer.Address);} 这时,如果调用时没有按期望时的次序进行,那就会出错,抛出异常。 这种次序可以灵活使用,例如可以次序一个mock,然后期望条件达到后,再不按次序进行。注意:在进行回播前要退出次序。 (二)模拟委托 先定义委托: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public delegate void DoThing(string strMsg); 然后模拟委托: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestDelegate1(){ MockRepository mocks = new MockRepository(); var oo = mocks.DynamicMock<DoThing>(); oo(""); mocks.ReplayAll(); oo(""); mocks.VerifyAll();} 有两个系统定义的委托Func<TResult>和Action<T> 前是带返回值的委托,后者不带返回值,现在通过Action<T>来实现上例 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestDelegate2(){ MockRepository mocks = new MockRepository(); var oo = mocks.DynamicMock<Action<string>>(); oo(""); mocks.ReplayAll(); oo(""); mocks.VerifyAll();} 再来一个Func,即带返回值的委托的例子: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestDelegateFunc(){ MockRepository mocks = new MockRepository(); var oo = mocks.DynamicMock<Func<string, string>>(); Expect.Call(oo("")).Return("abc"); mocks.ReplayAll(); Assert.AreEqual("abc", oo(""));} 再来一个例子: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public class Customer{ Func<string, string> _fun; public Customer(Func<string, string> fun) { _fun = fun; } public void DoSomething(string strMsg) { Console.WriteLine(_fun(strMsg)); }} 测试: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestDelegateFunc(){ MockRepository mocks = new MockRepository(); var oo = mocks.DynamicMock<Func<string, string>>(); Expect.Call(oo("")).Return("abc"); mocks.ReplayAll(); var customer = new Customer(oo); customer.DoSomething("");} 于对这两种委托请见:http://www.cnblogs.com/jams742003/archive/2009/10/31/1593393.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
MockRespository有四种泛型方法: ·CreateMock<T> ·CreateDynamicMock<T> ·PartialMock ·Stub 在3.5中,三种已经过时的方法分别由以下方法替代: ·StrictMock<T> ·DynamicMock<T> ·PartialMock<T> ·Stub<T> 它们各自对应静态工厂方法: ·MockRepository.GenerateStrictMock<T> ·MockRepository.GenerateMock ·MockRepository.GeneratePartialMock<T> ·MockRepository.GenerateStub<T> (1)StrictMock 通过这个方法可以创建一个具有严格语义的T类型mock对象,如果在使用过程中没有显式的对过程进行录制,则会出错误,并会抛出异常。 例如: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestStrictMock(){ MockRepository mocks = new MockRepository(); ICustomer customer = mocks.StrictMock<ICustomer>(); customer.Replay(); customer.ShowTitle(""); mocks.VerifyAll();} 这里没有对customer的ShowTitle方法显式地安装期望, 而mock对象又是具有严格语义的对象,所以这里会发生错误,而抛出异常。 (2)DynamicMock 通过这个方法可以创建一个具有动态语义的T类型mock对象,如果在使用过种中没有显式的对过程进行录制,则不会出现异常。如果方法有返回值,那么会返回null或0。 同样以上个例子来说: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void TestDynamicMock(){ MockRepository mocks = new MockRepository(); ICustomer customer = mocks.DynamicMock<ICustomer>(); customer.Replay(); customer.ShowTitle(""); mocks.VerifyAll(); } 这里同样没有进行显式的安装期望,但不会出现错误,不会抛出异常。所以当使用动态语义模拟对象时,没有显式安装期望的方法会被忽略。 (3)PartialMock 可以模拟类的一部分。可以独立测试抽象方法。它只能用于类。加一官方描述的话:如果方法上没有设置期望的值,就从一个调用类方法的默认类上去创建一个Mock对象。 现在用例子来说明一下,这个例子通过抽象类来进行,抽象类中有一模板方法,而其中的方法是个抽象的,这里通过官网提供的例子来进行: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public abstract class ProcessorBase{ public int Register; public virtual int Inc() { Register = Add(1); return Register; } public abstract int Add(int i);} Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestPartialMock(){ MockRepository mocks = new MockRepository(); ProcessorBase proc = mocks.PartialMock<ProcessorBase>(); using (mocks.Record()) { Expect.Call(proc.Add(1)).Return(1); Expect.Call(proc.Add(1)).Return(2); } proc.Inc(); Assert.AreEqual(1, proc.Register); proc.Inc(); Assert.AreEqual(2, proc.Register); mocks.VerifyAll();} (4)Stub 直接以例子进行 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public interface IAnimal{ int Legs { get; set; } int Eyes { get; set; } string Name { get; set; } string Species { get; set; } event EventHandler Hungry; string GetMood();}public class AnimalTest{ IAnimal _animal; public AnimalTest(IAnimal animal) { _animal = animal; } public void SetLegs(int count) { _animal.Legs = count; }} 测试: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void CreateAnimalStub(){ MockRepository mocks = new MockRepository(); IAnimal animal = mocks.DynamicMock<IAnimal>(); Expect.Call(animal.Legs).PropertyBehavior(); Expect.Call(animal.Eyes).PropertyBehavior(); Expect.Call(animal.Name).PropertyBehavior(); Expect.Call(animal.Species).PropertyBehavior(); AnimalTest aa = new AnimalTest(animal); aa.SetLegs(10); Assert.AreEqual(10, animal.Legs);} 设置接口属性行为,可以在实例中使用。这个属性行为可以通过Stub来设置,那就简单了: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void CreateAnimalStub(){ MockRepository mocks = new MockRepository(); IAnimal animal = mocks.Stub<IAnimal>(); AnimalTest aa = new AnimalTest(animal); aa.SetLegs(10); Assert.AreEqual(10, animal.Legs);} 当然,也可利用反射来封装对象属性行为设置mock对象的所有属性: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void SetPropertyBehaviorOnAllProperties(object mock){ PropertyInfo[] properties = mock.GetType().GetProperties(); foreach (PropertyInfo property in properties) { if(property.CanRead && property.CanWrite) { property.GetValue(mock, null); LastCall.On(mock).PropertyBehavior(); } }} 从这里看出Stub是多么的方便。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
这里介绍RhinoMock中的常用的类。 (一)MockRepository RhinoMock的主要的类,是Mock仓库,用于创建mock对象,录制,重放,验证等。 (1)创建Mock Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->MockRepository mock = new MockRepository(); (2)创建mock对象 Mock的目的就是创建mock对象,然后期望,录制重放等。创建mock对象有很多方法,以前的版本中通过: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->MockRepository fac = new MockRepository();var customer = fac.CreateMock<ICustomer>(); 来进行,CreateMock方法已经过时,工厂方法来进行: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->MockRepository fac = new MockRepository();var customer = fac.StrictMock<ICustomer>(); 也可以通过静态工厂方法来创建: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var customer = MockRepository.GenerateMock<ICustomer>(); (3)录制 所有的mock框架都使用Record/Replay模式,但有些是显示的,有些是隐式的,而RhinoMock就是显示的。 ·Record/Replay传统的录制与重放: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestRR(){ MockRepository fac = new MockRepository(); var customer = fac.StrictMock<ICustomer>(); customer.Expect(p => p.ShowTitle("")).Return(""); customer.Replay(); Assert.AreEqual("", customer.ShowTitle(""));} ·Record,Using方式 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void TestRR(){ MockRepository fac = new MockRepository(); var customer = fac.StrictMock<ICustomer>(); using (fac.Record()) { customer.Expect(p => p.ShowTitle("")).Return(""); } Assert.AreEqual("", customer.ShowTitle(""));} ·通过静态工厂方法创建mock对象: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public void TestRR(){ var customer = MockRepository.GenerateMock<ICustomer>(); customer.Expect(p => p.ShowTitle("")).Return(""); Assert.AreEqual("", customer.ShowTitle(""));} (4)验证 用于验证mock对象的期望是否成立。 ·Verify,用于验证mock对象的所有期望是否满足 ·VerifyAll,用于验证所有mock对象的所有期望是否满足 (二)Expect类 为有返回值的类的方法设置期望 Call:为方法或属性提供期望 ·Call<T>(T ignored) ·Call(Action actionToExecute) 例: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestCall(){ MockRepository mock = new MockRepository(); var mockObject = mock.StrictMock<ICustomer>(); Expect.Call(mockObject.ShowTitle(string.Empty)).Return("不能为空"); Expect.Call(mockObject.Unid).Return(30); mock.ReplayAll(); Assert.AreEqual("不能为空", mockObject.ShowTitle("")); Assert.AreEqual(30, mockObject.Unid);} 看lambda表达式方式 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestCall(){ MockRepository mock = new MockRepository(); var mockObject = mock.StrictMock<ICustomer>(); mockObject.Expect(p => p.ShowTitle("")).Return("不能为空"); mockObject.Expect(p => p.Unid).Return(30); mock.ReplayAll(); Assert.AreEqual("不能为空", mockObject.ShowTitle("")); Assert.AreEqual(30, mockObject.Unid);} 再来看Using方式 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->[Test]public void TestCallUsing(){ MockRepository mock = new MockRepository(); var mockObject = mock.StrictMock<ICustomer>(); using (mock.Record()) { mockObject.Expect(p => p.ShowTitle("")).Return("不能为空"); mockObject.Expect(p => p.Unid).Return(30); } Assert.AreEqual("不能为空", mockObject.ShowTitle("")); Assert.AreEqual(30, mockObject.Unid);} 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
RhinoMock用来模拟对象,安装期望然后测试。3.5版本支持Lambda和.net3.X扩展。 下载地址:http://www.ayende.com/projects/rhino-mocks/downloads.aspx rhinomock可以很容易的创建模拟对象。然后通过对其它安装期望然后可以测试相关的业务。mock测试通过强类型标记代替字符串。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
对于Linq查询有几个要点,数据源,元素,投影。把握这几个要点,那么运用起来得心应手。 (一)查找句子 这里指的句是是英文句子,英文句子以.!?结束(逗点,叹号,问号)。下面摘取《The Call of the Wild》一段来进行介绍。 先来一大段: string str=@"There he lay for the remainder of the weary night, nursing his wrath"+ @"and wounded pride. He could not understand what it all meant. What"+ @"did they want with him, these strange men? Why were they keeping"+ @"him pent up in this narrow crate? He did not know why, but he felt"+ @"oppressed by the vague sense of impending calamity. Several times"+ @"during the night he sprang to his feet when the shed door rattled open,"+ @"expecting to see the Judge, or the boys at least. But each time it was"+ @"the bulging face of the saloon-keeper that peered in at him by the sickly"+ @"light of a tallow candle. And each time the joyful bark that trembled in"+ @"Buck's throat was twisted into a savage growl."; 然后,查找带有“Buck”的句子,并分别打印出来。 首先,这里的数据源就是这个字符串(或者说这个段落),然后元素就是想要的每个句子,投影也是句子,就是要段落中的所有句子。 获取数据源:数据源应该是句子集合,对于字符串来说,直接的进行Linq查询,那么它的元素将是一个一个的字符,所以这里要得到数据源: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string[] sentences=str.Split(new char[]{'.','?','!'}); 用来分隔句子。现在的结果是: There he lay for the remainder of the weary night, nursing his wrathand wounded pride 然后,对于含有“Buck”的句子就是其中的元素,它的类型是字符串;而所需要就是这些句子。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var q1=from p in sentences where p.Contains("Buck") select p; 上边查出的句子前边有空格,现在去除空格,这里用正则表达式来完成。 这个相对比较简单: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var q1=from p in sentences where p.Contains("Buck") select Regex.Replace(p,@"^\ ",""); And each time the joyful bark that trembled inBuck's throat was twisted into a savage growl (二)遍历文件 遍历目录下的所有文件并打印: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DirectoryInfo path = new DirectoryInfo(@"L:\css");FileInfo[] fileinfos = path.GetFiles(); var q = from p in fileinfos elect new { 文件名 = p.Name, 文件大小 = p.Length / 1024 };q.Dump(); 我现在打印的是目录下的文件: 文件名 文件大小 1.jpg 247 float.htm 0 …… …… 链接.htm 0 上边只是遍历了目录下的文件,而对于其中的文件夹中的文件没能查询到,下边实现遍历全路径,方法很简单: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DirectoryInfo path = new DirectoryInfo(@"L:\css");FileInfo[] fileinfos = path.GetFiles("*", SearchOption.AllDirectories); var q = from p in fileinfos select new { 文件名 = p.Name, 文件大小 = p.Length / 1024, 文件路径 = p.FullName };q.Dump(); 文件名 文件大小 文件路径 1.jpg 247 L:\css\1.jpg float.htm 0 L:\css\float.htm …… …… …… qq.gif 25 L:\css\pic\qq.gif t.png 15 L:\css\pic\t.png 找到数据源,明确其中的元素单位,并按所需进行投影设置。 (三)查找最大的文件 按文件大小排序,并通过元素选择来得到文件 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DirectoryInfo path = new DirectoryInfo(@"L:\css");FileInfo[] fileinfos = path.GetFiles("*", SearchOption.AllDirectories);var q = (from p in fileinfos orderby p.Length select p).Last();Console.Write(q.FullName); (四)在文本文件中查找字符串 查找带有指定字符串的文件 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var q = from p in fileinfos where p.Extension == ".htm" select p.FullName; 先指定筛选条件文件文件,扩展名为htm的文件,然后投影选择文件全名。然后在投影之前添加对文本文件中的字符串的查找: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->DirectoryInfo path = new DirectoryInfo(@"L:\css");FileInfo[] files = path.GetFiles("*", SearchOption.AllDirectories); var q = from p in files where p.Extension == ".htm" && GetFileContent(p.FullName).Contains("导航") select p;foreach (var info in q) Console.WriteLine(info.FullName); GetFileContent方法是通过路径读取文本文件内容。 结果略。 这里提一下let关键字。Let类似mssql中的declare,用于声明局部变量 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var q = from p in files where p.Extension == ".htm" let strContent= GetFileContent(p.FullName) where strContent.Contains("导航") select p; 这里设置strContent变量,然后再通过另一个where来与上过滤条件。Let非常有用。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
LINQ,语言级集成查是Language INtegrated Query的意思,它是连接对象领域和数据领域的一座桥梁。可以通过C#,对各种数据源进行Linq查询: sql数据库(LinqToSql),xml文档(LinqToXml),ado.net的数据集,以及支持IEnumerable或IEnumerable<T>接口的任意对象集合。 Linq查询一般分三个步骤进行:得到数据源——建立查询——执行查询。下面详细介绍查询。这些查询结合LinqPad来测试,关于LinqPad介绍请见: http://www.cnblogs.com/jams742003/archive/2010/05/05/1728124.html (一)基本查询 基本查询包括,得到数据源,查询条件,分组,排序,投影。 (1)通过字符串来演示 在LinqPad上在statements模式下进行以下测试: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string strTemp = "Oh,What a pity!";string[] strTemps = strTemp.Split(new char[] { ' ', ',', '!' }); var query = from st in strTemps where st.Contains('a') orderby st.Length descending select st;query.Dump(); 可以得到结果: What a 说明: ·数据源strTemps ·定义变量st ·条件,where,用来查找单词中含有字母a的单词 ·结果排序,按单词长度降序 ·投影,把单词添充到结果query 现在通过匿名类型对投影进行设置:将单词和单词的长度做为结果进行填充 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string strTemp = "Oh,What a pity!";string[] strTemps = strTemp.Split(new char[] { ' ', ',', '!' }); var query = from st in strTemps where st.Contains('a') orderby st.Length descending select new { 含有字母a的单词 = st, 单词的长度 = st.Length };query.Dump(); 结果: 含有字母a的单词 单词的长度 What 4 a 1 上边两种都是以语句方式进行的查询,也可以以方法方式进行查询: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string strTemp = "Oh,What a pity!";string[] strTemps = strTemp.Split(new char[] { ' ', ',', '!' }); var query = strTemps .Where(st => st.Contains('a')) .OrderBy(st => st.Length) .Select(st => st);query.Dump(); 结果同上, Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string strTemp = "Oh,What a pity!";string[] strTemps = strTemp.Split(new char[] { ' ', ',', '!' }); var query = strTemps .Where(st => st.Contains('a')) .OrderBy(st => st.Length) .Select(st=>new{含有字母a的单词 = st, 单词的长度 = st.Length});query.Dump(); 通过方法进行查询时需要Lambda表达式,关于Lambda请见: http://www.cnblogs.com/jams742003/archive/2009/12/23/1630737.html 其中的方法指的是扩展方法 下面,实现分组,为了演示分组功能,现在通过数据库来实现。 Unid Name Version 2 宋江 5 38 张青 1 40 张清 1 41 武ww 0 这张表是数据,现在以Version来分组: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->from c in Customersgroup c by c.Version into selfGroupwhere selfGroup.Count()>1select selfGroup 查询以Version进行分组,且通过into selfGroup来对分组进行后续筛选,选择组内成员数在大于1的,结果是: Key= 1 Unid FirstName LastName CreateTime Address Version 38 张 青 2010-1-6 9:40:47 清河县1 1 40 张 清 2010-1-6 10:00:32 未知1 1 78 2 以下通过数据功能来介绍查询方法 (二)排序 方法OrderByDescending,OrderBy,ThenBy,ThenByDescending 降序排列 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int[] ii = new int[] { 1, 90, 12, 25, 55 };var q=ii .OrderByDescending(p=>p) .Select(p=>p);q.Dump(); 结果: 90 55 25 12 1 (三)结果集操作 ·Distinct:去重 ·Except:返回一集合中存在,另一集合不存在的结果 ·Intersect:交集 ·Union:并集 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string str1="abc123aab";string str2="abc456"; //Distinctvar q1=str1.Distinct();q1.Dump(); a b c 1 2 3 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->//Exceptvar q2=str1.Except(str2);q2.Dump(); 1 2 3 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->//Intersectvar q3=str1.Intersect(str2);q3.Dump(); a b c Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->//Unionvar q4=str1.Union(str2);q4.Dump(); a b c 1 2 3 4 5 6 (四)限定符 Any,All,Contains Any用于判断是否存在元素 All 用于判断数据源中的元素是否全部满足条件 Contains用于判断数据源是否包含指定的元素 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string str = "Hello world!"; Console.WriteLine("是否含有元素:{0}", str.Any() == true ? "是" : "否"); Console.WriteLine("是否全是字母:{0}", str.All(p => Char.IsLetter(p)) == true ? "是" : "否"); Console.WriteLine("是否包含He:{0}", str.Contains("He")); (五)数据分区 Skip,SkipWhile,Take,TakeWhile Take:取前n个 Skip:跳过前n个 TakeWhile:返回满足条件的 SkipWhile:跳过满足条件的,返回剩余的 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string str = "0123456789"; //得到前5个var q1 = str.Take(5);q1.Dump(); //得到数据直到数字大于6var q2 = (from p in str select p).TakeWhile(p => Convert.ToInt32(p.ToString()) < 6);q2.Dump(); var q22 = str.TakeWhile(p => Convert.ToInt32(p.ToString()) < 6);q22.Dump(); //跳过前5个var q3 = str.Skip(5);q3.Dump(); //大于6的跳过var q4 = str.SkipWhile(p => Convert.ToInt32(p.ToString()) < 6);q4.Dump(); 结果略。 (六)生成新值序列 DefaultIfEmpty,Empty,Range,Repeat DefaultIfEmpty:返回序列,如果序列为空,则返回集合中元素的默认值。例如,如果是整型数组,如果为空,那会返回整型的默认值:0;如果是对象数组,那会返回:null Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->List<int> numbers = new List<int>();var q1=numbers.DefaultIfEmpty();q1.Dump(); List<Customer> list=new List<Customer>();var q3=list.DefaultIfEmpty();q3.Dump(); 结果: 0 null 后边几个找不到。 (七)元素操作 First,Last,FirstOrDefault,LastOrDefault,ElementAt,ElementAtOrDefault,Single,SingleOrDefault 可以从字面上理解意思,每种都有两种类型,例如First和FirstOrDefault,用于返回第一个元素,如果第一个元素超出索引,则返回元素类型的默认值。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int[] ii=new int[]{1,21,0,36};int _first=ii.First();Console.WriteLine(_first); int _last=ii.Last();Console.WriteLine(_last); int _top2=ii.ElementAt(1);Console.WriteLine(_top2); int _top6=ii.ElementAtOrDefault(5);Console.WriteLine(_top6); 结果: 1 36 21 0 其中第4个0,是索引处不存在元素,所以返回元素类型的默认值,即整型的默认值:0 (八)数据转换 AsEnumberable:返回IEnumeralbe<T>类型 AsQueryable:将IEnumerable<T>转化为IQueryable Cast:强制转换 ToArray:转换为数组(还有一个作用就是强制执行查询,因为查询是懒惰的(延迟)) ToList:转换为List<T>,另一个作用同上 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int[] ii=new int[]{1,21,0,36};var q=ii.ToList();foreach(int i in q) Console.Write(i+" "); (九)聚合 聚合类方法包括数量,平均数,和值,最大最小值等。 Average,Count,LongCount,Max,Min,Sum Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int[] ii=new int[]{1,21,0,36};int k=ii.Sum(p=>p);Console.Write(k); 结束之前推导一下Lambda,Func<T>,Sum扩展方法: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int[] ii=new int[]{1,21,0,36}; 计算这个集合的和值 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int sum1=ii.Sum(p=>p);Console.WriteLine(sum1); 其中Sum方法的参数部分是一个Func<T,TResult>参数,这个参数是一个委托类型,关于Func<T>请见: http://www.cnblogs.com/jams742003/archive/2009/10/31/1593393.html Func<T,TResult>,表示一个T类型的参数,且返回Tresult类型的一个方法委托。现在由Func委托来实现: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Func<int,int> fun=p=>p;int sum2=ii.Sum(fun);Console.WriteLine(sum2); 接下来以匿名方法来实现: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Func<int,int> funn=delegate(int i){ return i;};int sum3=ii.Sum(funn);Console.WriteLine(sum3); 然后通过明确方法来实现: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->Func<int,int> funnn=SelfSum;int sum4=ii.Sum(funnn);Console.WriteLine(sum4); static int SelfSum(int i){ return i;} 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
介绍一款用于Linq运算和测试的工具,LinqPad。我感觉这个工具非常优秀,不只是功能上优秀,在使用上也非常优秀,让我爱不释手。 LinqPad官方地址:http://www.linqpad.net/ 页面右侧有下载地址,LinqPad分两种运行模式,一是集成为一个可执行程序的模式,另一种是安装模式。 这里给出一张截图。说明一下。 界面中可大致分为四个分隔窗格: (一) 左上部Connection部分,用于连接可Linq的数据源,数据源种类很多,通过添加连接(Add Connection)可以看到可以添加LinqtoSql,也可以WCF服务,也可以是其它数据源。 (二) 右上部分就是查询窗口,是写代码的部分。这里可以选择语言(Language),可以选择数据库。和数据库查询分析器类似。 在语言列表中可选择: 都以查询Customers表中的数据为例 (1)C# Expression 用于进行LinqSql查询 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->from c in Customers select c (2)C# Statement 用于C#语句查询 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->var p=from c in Customers select c;p.Dump(); 其中的Dump可以显示查询结果。 (3)C# Program 用于支持方法和类型的查询,须定义Main方法 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->void Main(){ var p=from c in Customers select c; p.Dump();} (4)SQL 传统的sql查询 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->select * from customer 其中在前3种方法中,查询的数据库表的表名默认的在连接窗口设置为表名+s,这点在linqPad中要注意。 (5)Esql,VB下的几种忽略 (三)左下部分是一些教学示例和保存的查询 LinqPad的一些参数可以在这里设置,也可以使用其中的一些示例。 (四)右下部分就是查询结果窗口 这里的选项卡有4项,可以查看结果,可以查看sql,可以查看IL 在使用时推荐使用语句模式(C# Statement)。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
启用策略之用户令牌(UsernameToken) 实现一下用户令牌策略(UsernameToken) 这里详细的说一下: 服务端配置 (一)建立web网站(略) (二)配置WSE (1)在常规(general)选项卡上勾选其中的两项 (2)策略设置 在策略(Policy)选项卡中设置,这个设置很容易,勾选Enable Policy,添加策略文件:名字为ServicePolicy 步骤(上传图片比较麻烦,所以直接文字表达): 服务器策略——客户端验证方法选择Username——用户和角色这里不勾选——消息保护级别默认就行——服务器证书这里不勾选在代码中指定证书,然后选择证书——完成。 (3)用户令牌设置 这里设置比较简单,添加从UsernameTokenManager派生的类CustomUsernameTokenManager 这里给个全的: protected override string AuthenticateToken(UsernameToken token) { return "111"; } 它就返回密码是:111。如果客户端的的用户令牌密码与这个不符合,那么服务就会拒绝客户访问。这个验证令牌的方法就是这个意思。它就是验证用户令牌中的密码,而用于比较的密码就是这个方法返回的字符串。 客户端配置 (1)策略设置方法与服务端设置相似,但该选择客户端的地方要选择客户端选项(这个应该容易理解吧)。 (2)请求服务时,要设置用户令牌 (3)请求服务时,不要忘记激活用户策略 下边给出一段完整的代码: public void Test() { FirstInstance.WebServiceWse client = new FirstInstance.WebServiceWse(); UsernameToken token = null; token = new UsernameToken("qq","111"); client.SetClientCredential(token); client.SetPolicy("ClientPolicy"); Console.WriteLine(client.HelloWorld("x")); } 说明一下: 在这段代码中,设置的用户令牌密码为3个1。 注意:配置时要注意异常错误,这些异常错误这里不提了。只要是一步步的进行,注意其中的安全选项冲突,则没有问题。 现在对消息的保护设置为None,然后,看看令牌在soap消息中的情况,这里只给出其中的一段: <wsse:UsernameToken> <wsse:Username>qq</wsse:Username> <wsse:Password Type="…PasswordDigest"> 0nAwMqEiHeF+uCbMTRL0qfQG2Mo= </wsse:Password> <wsse:Nonce>fBduFda3nmZboNcsQDBWig==</wsse:Nonce> <wsu:Created>2010-04-23T09:19:38Z</wsu:Created> </wsse:UsernameToken> 这里可以看到在无消息保护级别时(消息保护选择None):用户令牌其中的两个元素:用户名和用户密码,用户名是qq,而密码以数字签名格式存在。有关签名(数字签名)及散列相关内容可见我的随笔: http://www.cnblogs.com/jams742003/archive/2010/04/09/1708012.html http://www.cnblogs.com/jams742003/archive/2010/04/09/1708315.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
在wse视图工具中,其中的两个选项卡:策略(Policy)和安全(Secury) 1 策略是对安全的控制 2 安全选项卡中可以设置对于安全控制的策略组件的设置,例如用户令牌管理,证书管理 策略分两种,服务端和客户端。在添加策略文件时,验证项(用户的授权)可以有四种选择: Anonymous(匿名用户):这种类型不要求有客户凭证 Username(用户):这种类型要求客户端传递一个用户令牌凭证,凭证中包含用户名和用户密码 Certifacate(证书):这种类型要求客户端传递一个证书令牌,令牌中包含x.509证书 Windows(windows):这种类型要求客户传递kerberos令牌,其中包括一个windows标识。 对soap消息进行保护的类型也有四种: None(无):由SSL传输提供soap消息的保护。WSE不强求 Sign-Only(只签名):只对soap消息进行签名 Sign and Encrypt(签名并加密):对soap消息进行签名且加密 Sign,Encrypt,Encrypt Signature(签名,加密,加密签名):对soap消息进行签名,且对soap包体和签名进行加密。 在程序中以MessageProtectionOrder枚举来表示: ·EncryptBeforeSign:在签名前加密soap消息 ·SignBeforeEncrypt:加密前要签名,但签名不加密 ·SignBeforeEncryptAndEncryptSignature:加密前要签名,然后签名也要加密 Wse配置工具是对config文件的界面操作工具,在xml中,属性值与这个枚举值相同: messageProtectionOrder="SignBeforeEncrypt" 现在说说策略断言(Policy Assertion) 策略断言定义了一组在服务器和客户进行soap消息交换的要求。例如一个策略断言可能会指定一个用来对在服务器和客户之间进行的soap消息交换进行加密和签名的令牌类型。为了指定安全要求,添加为策略断言添加一个或多个turnkey或自定义的安全断言。策略断言,通过xml元素来声明,例如像下边这个例子中的<anonymousForCertificateSecurity>元素。 <anonymousForCertificateSecurity> <serviceToken></serviceToken> <protection> <reques/> </protection> </anonymousForCertificateSecurity> (一)不启用策略 不启用策略时,就是不配置WSE的Policy部分。只是对ws启用WSE增强。这个很简单。 ·服务端:在可视工具上的常规选项卡上勾选2项(如果不是网站项目,则不能添加soap protocol factory) ·客户端(例如类库项目):在可视工具常规选项卡上勾选1项,这项的主要作用就是添加对Microsoft.Web.Services3的引用。 在客户端使用时,使用带WSE后辍的代理类实例来实现对服务的请求: FirstInstance.WebServiceWse client = new FirstInstance.WebServiceWse(); (二)启用策略之匿名用户 匿名用户anonymous不要求有客户凭证,而对消息的保护如果要有签名或加密,须启用证书,有关证书的相关内容请见: http://www.cnblogs.com/jams742003/archive/2010/04/12/1710124.html http://www.cnblogs.com/jams742003/archive/2010/04/20/1716253.html 这里启用匿名用户: ·服务端策略选项卡上添加服务端策略,添加完毕后,在服务上以修饰标签的形式启用策略。 ·客户端启用相同的策略,但策略名不同。在应用时,可在代码中启用策略 ·在消息保护项有3项可选择,所以这里需要一张证书 服务端: 1 策略选项卡——添加策略——策略名(ServerPolicy)——匿名——签名——选择证书——完成 2 在服务类添加修饰以启用策略 [Microsoft.Web.Services3.Policy("ServerPolicy")] public class WebService : System.Web.Services.WebService 客户端: 1策略选项卡——添加策略——策略名(ClientPolicy)——匿名——签名——选择证书——完成 2 在代码中启用策略: client.SetPolicy("ClientPolicy"); 这里给出一段启用匿名用户策略且对消息进行签名的soap包部分: <Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> <SignedInfo> <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" /> <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1" /> <Reference URI="#Id-c7c03d5c-6e4b-493e-b3e8-c50ba9c27088"> <Transforms> <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /> </Transforms> <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" /> <DigestValue>1tzz6zotjDSBNQf0UhyrqfA2G6I=</DigestValue> …… 在匿名用户策略下,消息保护可选择3种方式: Sign-Only(只签名) Sign and Encrypt(签名并加密) Sign,Encrypt,Encrypt Signature 在上段例子中用的是第一种。在包中的包体部分可以看到: <soap:Body> <HelloWorld xmlns="http://tempuri.org/"> <strName>Songjiang</strName> </HelloWorld> </soap:Body> Web方法传入参数是明文形式。这个部分由策略断言设置,在原设置中,请求部分的包体加密属性为false: encryptBody="false" 现在改为要求加密包体,然后看包体中的参数部分: <soap:Body> <xenc> <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc" /> <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#"> <wsse:SecurityTokenReference> <wsse:Reference URI="#SecurityToken-2bf9dfb3-9f7d-42ac-9bfc-e12919fb2202" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/dk" /> </wsse:SecurityTokenReference> </KeyInfo> <xenc:CipherData> <xenc:CipherValue> Ig7MdF2oZZS625taru1b/RwH5IDePxBUwIWDw 0Lw2Y/E8HetOG64sdpX6J7bZeLXf8XWA3TmyC gCmqBgQJn/UD73ZlVdAufVv3pzTAeew62GcnH Esj3AZOYNcDI90wVZUZ4Dm7LMn4T9K6SBJO7qgw== </xenc:CipherValue> </xenc:CipherData> </xenc:EncryptedData> </soap:Body> 可以看到包体中的明文已经变成了密文CipherValue 从这段代码来看,它的加密方法是AES,256位CBC格式。关于更多对称加密的内容可见: http://www.cnblogs.com/jams742003/archive/2010/04/07/1706471.html 对于soap消息的加密保护可在策略断言中设置,下面给出一段完整的策略断言: <policy name="ClientPolicy"> <anonymousForCertificateSecurity establishSecurityContext="false" renewExpiredSecurityContext="true" requireSignatureConfirmation="false" messageProtectionOrder="SignBeforeEncrypt" requireDerivedKeys="true" ttlInSeconds="300"> <serviceToken> <x509 storeLocation="CurrentUser" storeName="AddressBook" findValue="CN=soapEncrypt" findType="FindBySubjectDistinguishedName" /> </serviceToken> <protection> <request signatureOptions="IncludeAddressing,IncludeTimestamp,IncludeSoapBody" encryptBody="true" /> <response signatureOptions="IncludeAddressing,IncludeTimestamp,IncludeSoapBody" encryptBody="false" /> <fault signatureOptions="IncludeAddressing,IncludeTimestamp,IncludeSoapBody" encryptBody="false" /> </protection> </anonymousForCertificateSecurity> </policy> 策略断言可以使用 turnkey断言或自定义。 turnkey安全断言的作用: ·消息级别或传输级别的安全 ·证明客户或服务或两者的安全凭证 ·保护soap消息的安全凭证 它有很多种类,下边列举一下: ·<anonymousForCertificateSecurity> ·<kerberosSecurity> ·<mutualCertificate10> ·<mutualCertificate11> ·<usernameOverTransportSecurity> ·<usernameForCertificateSecurity> 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
WSE,是Web Service Enhancement的简称。是Web服务增强。现在的版本是3.0。WSE对.net平台开发ws提供了有效的支持。其实对wse的理解可以这样:.net中的web服务是微软对web服务的处理工具,而wse是对工具的增强。增强有多个方面,例如安全和传输。 如果要了解安全增强的话,还需要一些额外的知识点,例如散列,加密,证书等。这些可见我的随笔: http://www.cnblogs.com/jams742003/category/241179.html (一)WSE的安装 现在的3.0版本可以到: http://www.microsoft.com/downloads/details.aspx ?FamilyID=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en 下载 下载后,进行安装。安装过程中以插件形式安装,安装到vs2005中。在vs2008中安装请找其它文档。安装完毕后,新建立网站项目,然后在网站项目上右键可以看到:WSE Settings 3.0选项了。WSE安装成功。 (二)WSE配置 打开WSE Settings 3.0配置视窗,可以进行WSE配置。这个可视工具是对config文件的配置。它有以下选项卡: 常规(General);安全(secuity);路由(routing);策略(policy);令牌分发(tokenissuing);调试(diagnostics);消息(messaging) (三)WSE对WS的安全增强 在ws中,如果要进行用户验证,可以通过在soap header中添加用户信息,然后客户端设置soap头信息,服务端读取这些信息,进行验证,以判断用户的身份和权限。具体的做法可见: http://www.cnblogs.com/jams742003/archive/2010/03/31/1701555.html 通过对soapheader的派生类的一些操作可以实现在soap封套中的头部分添加额外的信息,这些信息是明文格式,当然可以通过一些安全技术来进行保护,例如加密,这个这里不再赘述。 WSE大纲参考请见老徐(男性)的WSE四篇文章: http://www.cnblogs.com/frank_xl/archive/2009/02/28/1400007.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
WCF服务要通过终结点来进行通信,终结点三大构成元素:ABC,其中的B,binding是重中之重,它解决了在消息交换过程中的编码,传输协议,安全等问题。 绑定是分层的,一个绑定对象对应一组有序的绑定元素的集合。每层的元素专门处理其中某领域内的事务,来负责终结点通信中的其中一方面。这里给出绑定对象的绑定元素的层表: 层 选项 是否必需 事务流 TransactionFlowBindingElement 否 可靠性 ReliableSessionBindingElement 否 安全性 对称、非对称、传输级 否 形状更改 CompositeDuplexBindingElement 否 传输升级 SSL 流、Windows 流、对等解析程序 否 编码 文本、二进制、MTOM、自定义 是 传输 TCP、命名管道、HTTP、HTTPS、MSMQ、自定义 是 其中的编码层和传输层是必须的。系统提供了足够多的绑定类型: 绑定 互操作性 安全模式(默认) 会话(默认) 事务 双工 BasicHttpBinding Basic Profile 1.1 (无)、传输、消息、混合 无、(无) (无) n/a WSHttpBinding WS 无、传输、(消息)、混合 (无)、传输、可靠会话 (无)、是 n/a WS2007HttpBinding WS-Security、WS-Trust、WS-SecureConversation、WS-SecurityPolicy 无、传输、(消息)、混合 (无)、传输、可靠会话 (无)、是 n/a WSDualHttpBinding WS 无、(消息) (可靠会话) (无)、是 是 WSFederationHttpBinding WS-Federation 无、(消息)、混合 (无)、可靠会话 (无)、是 否 WS2007FederationHttpBinding WS-Federation 无、(消息)、混合 (无)、可靠会话 (无)、是 否 NetTcpBinding .NET 无、(传输)、消息、混合 可靠对话、(传输) (无)、是 是 NetNamedPipeBinding .NET 无、(传输) 无、(传输) (无)、是 是 NetMsmqBinding .NET 无、消息、(传输)、两者 (无) (无)、是 否 NetPeerTcpBinding 对等 无、消息、(传输)、混合 (无) (无) 是 MsmqIntegrationBinding MSMQ 无、(传输) (无) (无)、是 n/a 从这个表中可知:BasicHttpBinding类型只有两层绑定元素。 验证一下: public void TestBindingElements() { BasicHttpBinding _binding = new BasicHttpBinding(); BindingElementCollection eles= _binding.CreateBindingElements(); foreach (BindingElement ele in eles) Console.WriteLine(ele.ToString()); } 结果: System.ServiceModel.Channels.TextMessageEncodingBindingElement System.ServiceModel.Channels.HttpTransportBindingElement 因为每种绑定类型的绑定元素构成的不同,在应用中会有很大不同。例如基础绑定类型:它不支持双工通信,不支持事务和会话服务。 (一) 通过绑定对象的属性进行消息的控制 绑定对象有多个属性用于控制消息。例如基础绑定: ·EnvelopeVersio:获取此绑定处理的消息将要使用的 SOAP 版本。 ·MessageEncoding:获取或设置是使用 MTOM 还是文本对 SOAP 消息进行编码。 ·MessageVersion:获取由绑定所配置的客户端和服务使用的消息版本。 ·Name:获取或设置绑定的名称。 ·Namespace:获取或设置绑定的 XML 命名空间。 ·Security:获取与此绑定一起使用的安全类型。 ·TextEncoding:获取或设置用于消息文本的字符编码。 设置绑定的名字:Name。设置可以在配置文件中配置也可以在自托管宿主程序中通过程序设置。 (二) 契约 契约提供消息的标准,交换的消息的规则。在WCF中,契约分4类: ·服务契约 ·数据契约 ·异常契约 ·消息契约 (1)服务契约 服务契约分2种, 一种是用在接口或类上的服务契约, [ServiceContract(Namespace="www.self001.com")] public interface IFirstService 例如:CallbackContract用于在支持双工交换的绑定里设置回调类型 SessionMode,用于设置或取得会话的状态 Name用于设置或取得wsdl上的<portType>元素的名称 另一种是用在方法上的操作契约: [OperationContract(Action="selfAction")] void DoWork(string strContent); 例如:设置 IsOneWay,用于设置或取得是否进行单向消息交换 Name,用于设置或取得操作的名称 通过对它们的属性修饰达到对服务的约束和设置。 在BasicHttpBinding中,默认的消息交换模式为RR。现在为DoWork添加单向交换标志: [OperationContract(IsOneWay=true)] void DoWork(string strContent); 那么在请求服务时,只有去的消息,而不返回消息。 (2)数据契约 主要用于数据的序列化。对于基元类型,wcf自动序列化并默认拥有数据契约。对复杂类型要进行序列化。(在WCF对复杂类型不用显示添加数据契约,但如果添加了,那么所有的数据契约都要添加) 数据的序列化为了传输和存储,可见: http://www.cnblogs.com/jams742003/archive/2010/03/31/1701184.html [DataContract] public class Customer { [DataMember] public int Unid { get; set; } [DataMember] public string UserName { get; set; } [DataMember] public DateTime CreateTime { get; set; } } 同样的,数据契约也用于不同的地方: [DataContract] public class Customer 通过属性的设置来设置数据契约的细节,例如Namespace 在没设置之前: <s:Body> <GetCustomerResponse xmlns="www.self001.com"> <GetCustomerResult xmlns:a="http://schemas.datacontract.org/2004/07/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:CreateTime>2010-04-01T00:00:00</a:CreateTime> <a:Unid>2</a:Unid> <a:UserName>Songjiang</a:UserName> </GetCustomerResult> </GetCustomerResponse> </s:Body> 设置: [DataContract(Namespace="selfdata.com")] public class Customer 之后的情况是: <s:Body> <GetCustomerResponse xmlns="www.self001.com"> <GetCustomerResult xmlns:a="selfdata.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:CreateTime>2010-04-01T00:00:00</a:CreateTime> <a:Unid>2</a:Unid> <a:UserName>Songjiang</a:UserName> </GetCustomerResult> </GetCustomerResponse> </s:Body> 可以看到其中的变化。 [DataMember] public int Unid { get; set; } 通过添加DataMember标签,指定这个成员作为数据契约的一部分,且可以被序列化。它的属性例如: Order:获取或设置成员的序列化和反序列化顺序 Name:获取或设置数据成员的名称 [DataMember(Name="SelfId")] public int Unid { get; set; } <s:Body> <GetCustomerResponse xmlns="www.self001.com"> <GetCustomerResult xmlns:a="selfdata.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"> <a:CreateTime>2010-04-01T00:00:00</a:CreateTime> <a:SelfId>2</a:SelfId> <a:UserName>Songjiang</a:UserName> </GetCustomerResult> </GetCustomerResponse> </s:Body> (3)异常契约 异常契约用于soap消息的异常。 [OperationContract] [FaultContract(typeof(string))] void SelfException(); public void SelfException() { throw new FaultException<string> ("error 007!","reason:Testing!"); } 客户端: public void TestFault() { FirstInstance.FirstServiceClient client = new FirstInstance.FirstServiceClient(); try { client.SelfException(); } catch (FaultException<string> ex) { Console.WriteLine(ex.Reason+" "+ ex.Detail.ToString()); } } 打印信息:reason:Testing! error 007! 包信息: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <s:Fault> <faultcode>s:Client</faultcode> <faultstring xml:lang="zh-CN"> reason:Testing! </faultstring> <detail> <string xmlns="http://schemas.microsoft.com/2003/10/Serialization/"> error 007! </string> </detail> </s:Fault> </s:Body> </s:Envelope> (4)消息契约 通过消息契约可以设置消息包体的多个选项,用于对包体中的类型进行强制设置。 [MessageContract(WrapperName="SelfCustomer")] public class Customer { [MessageBodyMember] public int Unid { get; set; } [MessageBodyMember] public string UserName { get; set; } [MessageBodyMember] public DateTime CreateTime { get; set; } } 客户端的用法就不同了: public void TestCustomer() { FirstInstance.Customer customer = new FirstInstance.Customer(); FirstInstance.FirstServiceClient client = new FirstInstance.FirstServiceClient(); int iUnid; string strUserName; client.GetCustomer(out iUnid,out strUserName); } 看包: <s:Body> <SelfCustomer xmlns="www.self001.com"> <CreateTime>2010-04-01T00:00:00</CreateTime> <Unid>2</Unid> <UserName>Songjiang</UserName> </SelfCustomer> </s:Body> 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
构建一个简单的WCF服务。 以Web服务类似的步骤由IIS进行宿主服务。建立的步骤: 1 新建3.5网站 2 添加WCF服务,自动生成契约接口与实现,这里改动一下,添加个字串参数: [ServiceContract] public interface IFirstService { [OperationContract] void DoWork(string strContent); } 服务中的方法什么都不用做。 public class FirstService : IFirstService { public void DoWork(string strContent) { } } 在添加WCF服务时,会自动在配置文件中添加必要的章节,例如绑定和元数据发布。 <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="FirstServiceBehavior"> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="FirstServiceBehavior" name="FirstService"> <endpoint address="" binding="basicHttpBinding" contract="IFirstService"> <identity> <dns value="localhost" /> </identity> </endpoint> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> </system.serviceModel> 这里把绑定改一下,改为basicHttpBinding。 然后在测试端:新建立类库项目,由发布的元数据生成代理,然后进行服务请求: [Test] public void Test() { FirstInstance.FirstServiceClient client = New FirstInstance.FirstServiceClient(); client.DoWork("this is a test!"); } 现在看一下消息包的情况: 这是客户端请求的信息: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <DoWork xmlns="http://tempuri.org/"> <strContent>this is a test!</strContent> </DoWork> </s:Body> </s:Envelope> 这是服务端回应的信息: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <DoWorkResponse xmlns="http://tempuri.org/"/> </s:Body> </s:Envelope> 对于BasicHttpBinding来说,它通过http来发送soap1.1的消息。这个绑定用于配置和公开能够与基于asmx的web service和客户端进行通信的终结点,以及符合ws-i basic profile 1.1标准的其它服务。 通过设置WCF绑定的消息编码格式来设置传输过程中所使用的编码: <basicHttpBinding> <binding name="firstBinding" messageEncoding="Text"> </binding> </basicHttpBinding> 现设置BasicHttp绑定的消息编码为文本,当传输二进制附件时,会怎么用base64编码: <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <SaveImage xmlns="www.self001.com"> <bb>GMDggAOw==</bb> </SaveImage> </s:Body> </s:Envelope> 其中附件部分我省略了大部分,只留一小段。 当使用MTOM编码格式时: --uuid:deef670a-dfd7-4a71-8d89-face6ac975dd+id=1 Content-ID: <http://tempuri.org/0> Content-Transfer-Encoding: 8bit Content-Type: application/xop+xml;charset=utf-8;type="text/xml" <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <SaveImage xmlns="www.self001.com"> <bb> <xop:Include href="cid:http%3A%2F%2Ftempuri.org%2F1%2F634057273450156250" xmlns:xop="http://www.w3.org/2004/08/xop/include"/> </bb> </SaveImage> </s:Body> </s:Envelope> --uuid:deef670a-dfd7-4a71-8d89-face6ac975dd+id=1 Content-ID: <http://tempuri.org/1/634057273450156250> Content-Transfer-Encoding: binary Content-Type: application/octet-stream GIF89ad……省略 --uuid:deef670a-dfd7-4a71-8d89-face6ac975dd+id=1-- 这与WSE3中使用的MTOM是相同的。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
WSE是什么? 它是Web Service Enhancements,是微软发布的构筑Web服务应用程序的附加套件。这个套件可以使web服务应用程序支持WS-Security、WS-Routing、WS-Attachments等规格。 (1)安全性:支援对平台安全的xml web服务的构筑,具有用于对soap信息进行电子签名和加密的功能。 (2)路由功能,对soap信息进行中继,并负责xml web服务的路由 (3)添加功能,可以向xml web服务soap信息中添加二进制数据。 在.net Web服务中,不支持MIME格式的soap附件,但在WSE2.0中却支持DIME附件,现在的WSE版本为3.0,在2.0中的DIME附件,在3.0中由MTOM模型代替。 关于wse更多内容可以参见老徐的系列: http://www.cnblogs.com/frank_xl/admin/EditPosts.aspx?catid=177055 现在通过WSE3.0构建附件Soap消息包。 (一) WSE服务端 WSE3.0可以到: http://www.microsoft.com/downloads/details.aspx? familyid=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en 下载 支持vs2005,其它版本的支持请参考其它文档。安装时以插件形式安装。 建立网站,并添加web 服务: [WebMethod] public void SaveImage(byte[] bb) { FileStream fs = File.Create(@"k:\soapPicBinary2.gif"); fs.Write(bb, 0, bb.Length); fs.Close(); } 在网站项目上右键,然后在右键菜单中可以看到WSE设置:WSE settings 3.0 这个可视化的工具是对Web.config文件的配置工具,WSE配置选项是放在web.config文件中的。 ·在常规选项卡上,把增强选项和WSE soap协议工厂选中。 ·在消息选项卡上,在MTOM设置中把客户模式选择打开。 然后就可以发布了。 (二) 客户端 在客户端添加web服务引用,生成代理,同时也要进行wse设置,这个比较简单,只要在消息选项卡上,把MTOM设置中的客户模式打开就行。如果是网站项目,会自动在Web.config文件中添加必要的章节,如果是应用程序,会在app.config文件中添加章节。 例如,现在在类库项目中添加wse服务引用,设置3.0后,app.config内容如下: 添加 <section name="microsoft.web.services3" type="Microsoft.Web.Services3. Configuration.WebServicesConfiguration, Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> 章节,此节定义消息模式: <microsoft.web.services3> <messaging> <mtom clientMode="On" serverMode="always" /> </messaging> </microsoft.web.services3> 测试代码: public void TestWse() { FirstInstance.FirstServiceWse client = new UTest.FirstInstance.FirstServiceWse(); FileStream fs = new FileStream(@"k:\x1.gif", FileMode.Open); byte[] bb = new byte[fs.Length]; fs.Read(bb, 0, bb.Length); client.SaveImage(bb); } 现在客户端通过的就是WSE服务代理实例来实现WSE服务请求。会了区分WSE下的附件MTOM传输优化,现在先通过WS服务代理实例来实现附件传输: 只要改到实例就可以: FirstInstance.FirstService client = new UTest.FirstInstance.FirstService(); 查看soap包: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <SaveImage xmlns="http://tempuri.org/"> <bb>R0lGODlhZGMDggAOw==</bb> </SaveImage> </soap:Body> </soap:Envelope> 在包体的附件部分,我省略了大部分内容,但它是base64编码,与我在http://www.cnblogs.com/jams742003/archive/2010/03/31/1701555.html 中的附件的格式相同。 下面是启用了MTOM传输的WSE服务代理实例下的soap包: ----MIMEBoundary634057127108593750 content-id: <0.634057127108593750@example.org> content-type: application/xop+xml; charset=utf-8; type="text/xml; charset=utf-8" content-transfer-encoding: binary <soap:Envelope xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsse="http://docs.oasis-open.org/wss/2004/ 01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/ oasis-200401-wss-wssecurity-utility-1.0.xsd"> <soap:Header> </soap:Header> <soap:Body> <SaveImage xmlns="http://tempuri.org/"> <bb> <xop:Include href="cid:1.634057127108906250@example.org" /> </bb> </SaveImage> </soap:Body> </soap:Envelope> ----MIMEBoundary634057127108593750 content-id: <1.634057127108906250@example.org> content-type: application/octet-stream content-transfer-encoding: binary GIF89ad ----MIMEBoundary634057127108593750— 可以看到所谓的MTOM优化传输包的形式。 Web service,或是WSE以soap协议(包)来传递消息,消息是重要的,也是目的所在。精确的控制消息,或说精准的控制包的内容是最主要的任务。 Soap协议版本从1.1到1.2有一些改进,消除了以前所存在的歧义,使解析更明确。.net web 服务及WSE就是遵循soap协议规则而用于控制soap包的简便工具。 现在实现传递附件和客户实例 服务端: [WebMethod] public void SaveImage(byte[] bb) { FileStream fs = File.Create(@"k:\soapPicBinary3.gif"); fs.Write(bb, 0, bb.Length); fs.Close(); } [WebMethod] public void SaveAttachments(byte[] bb,Customer customer) { SaveImage(bb); CreateText(customer); } private void CreateText(Customer customer) { StreamWriter writer= File.CreateText(@"k:\customer.txt"); writer.Write(customer.Unid.ToString() + ":" + customer.UserName + " " + customer.CreateTime.ToString()); writer.Close(); } 其中加粗的方法是进行测试的方法。 在客户端: public void TestCustomer() { FirstInstance.Customer customer = new UTest.FirstInstance.Customer(); customer.Unid = 2; customer.UserName = "Songjiang"; customer.CreateTime = Convert.ToDateTime("2010-4-1"); FileStream fs = new FileStream(@"k:\x1.gif", FileMode.Open); byte[] bb = new byte[fs.Length]; fs.Read(bb, 0, bb.Length); fs.Close(); FirstInstance.FirstServiceWse client = new UTest.FirstInstance.FirstServiceWse(); client.SaveAttachments(bb, customer); } 包的形式大概也能猜出来,现在只取包体一段内容: <soap:Body> <SaveAttachments xmlns="http://tempuri.org/"> <bb> <xop:Include href="cid:1.634057142194062500@example.org" /> </bb> <customer> <Unid>2</Unid> <UserName>Songjiang</UserName> <CreateTime>2010-04-01T00:00:00</CreateTime> </customer> </SaveAttachments> </soap:Body> 其中的客户还是以原来的格式存储,而附件(图片)是以MTOM形式存在发送。 消息是主体,消息就是包,精准的控制包的各个部分及命名要根据工具的控制力和自己的深入程度来达到。.net ws做为微软的ws工具,是不支持MIME附件soap包的,但在WSE2中就支持了对DIME附件的控制,在3.0中又支持了MTOM传输,这是工具的控制力;在WCF中,对于信道,信道元素,绑定模型等深层内容的掌握就是程序员深入理解和构建包的有力条件。尽管在我们的应用层面上,WCF对我们的大部分应用已经胜任且绰绰有余,但更深入的研究、理解和应用只有好处。轮子并不要我们创造,但从割胶到提取加工,以至到轮胎成型,如果整个过程我们理解的话,也不是坏事的,这样最少有2个好处:对于不同路面的轮胎可以进行不同处理而提供最恰当的轮胎(尽管差一点的轮胎也能上路);谁知道什么时候要做一回鲁宾逊? 例如,为服务的方法添加名字空间: <SaveAttachments xmlns="http://tempuri.org/"> 这个可以在web服务的类中添加修饰标签来达到: [WebService(Namespace = "http://tempuri.org/")] 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
Soap包中可以存放数据的地方可以是soap头也可以是soap body部分。其中body部分是必须的,是重要的数据存放位置。Soap头中也可以放一些数据,例如Web服务中的安全中用户检测。例如: 如果添加soap头,那么先要有一个从SoapHeader派生的类: public class SelfHeader : SoapHeader { public string UserName { get; set; } } 然后在web服务中先定义一个公共成员: public SelfHeader _soapHeader; 然后在需要添加soap头的方法上添加soap头标签: [WebMethod] [SoapHeader("_soapHeader")] public string HelloWorld() { if (_soapHeader.UserName != "zhao") return "用户禁止访问!"; return "Hello World"; } 在客户端进行访问服务时,须设置header属性: public void TestSimpleSoap() { ws.SelfService client = new ws.SelfService(); ws.SelfHeader _header = new ws.SelfHeader(); _header.UserName = "zzz"; client.SelfHeaderValue = _header; Console.WriteLine(client.HelloWorld()); } 现在看一下现在的soap包的情况: 请求: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Header> <SelfHeader xmlns="http://192.168.1.105/"> <UserName>zzz</UserName> </SelfHeader> </soap:Header> <soap:Body> <HelloWorld xmlns="http://192.168.1.105/" /> </soap:Body> </soap:Envelope> 现在添加了header部分。 在.net web服务中为soap添加附件。这里为什么说.net web服务呢? 这里说明一下web服务的概念:web service是基于xml和http的一种服务,它的通信协议主要基于SOAP(这是缩写应该全大写,但大家明白就行),通过WSDL来描述服务,通过UDDI来发布服务。 Web服务不是只有.net才有,.net中提供的web服务相关的定义,发布,发现等,及生成代理,应用。这些只是.net对web服务的一种帮助,或说.net提供了一套web服务的工具,用于创建,发布和应用。 Soap包中可以存放数据的位置可以是头,更重要且必须的是包体部分。那么对于附件,可以以格式化的数据存放于包体部分。 现在以包体部分来存储一个文本文件,目的很明确,那么文本文件内容以怎样的格式存放于包体呢? 第一种:文本文件中的内容以字串格式存在 [WebMethod] public void SaveText(string strContent,string strFileName) { StreamWriter writer=File.CreateText("k:\\"+strFileName+".txt"); writer.WriteLine(strContent); writer.Close(); } 这个方法将字串保存为utf-8的文本文件 客户端: [Test] public void TestSaveText() { StreamReader sr = new StreamReader (@"k:\client.txt",Encoding.Default); string strTemp = sr.ReadToEnd(); sr.Close(); ws.SelfService client = new ws.SelfService(); client.SaveText(strTemp, "server"); } 从gb2312文本中读取内容,然后存在包体中向服务传递。可以看下现在的包的内容: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <SaveText xmlns="http://192.168.1.105/"> <strContent>杩欐槸娴嬭瘯锛乤bc</strContent> <strFileName>server</strFileName> </SaveText> </soap:Body> </soap:Envelope> 其中乱码部分就是内容(乱码其实并不乱),可以所这部分拷贝到一个utf-8编码下的记事本中,然后另存为gb2312格式,可以看到认识的汉字了。 内容就是:这是测试!abc 第二种,二进制文件例如图片以base64编码存放 其实这个文件可以直接以二进制格式来传递,但如果要保存这个包,那么须对持久化的数据进行双向的格式化,使它的数据格式不会在转化时丢失或增加,在消息1中: http://www.cnblogs.com/jams742003/archive/2010/03/30/1700605.html 给出了通过base64和支持8位字符编码的28591页的字符集ISO8859-1 (一)以base64来存放二进制图片 服务端: [WebMethod] public void SaveImages(string strBase64Content) { //ISO串 byte[] bb = Convert.FromBase64String(strBase64Content);//iso字节 Encoding encoding = Encoding.GetEncoding(28591); string strReturn = encoding.GetString(bb); FileStream fs = File.Create(@"k:\soapPic.gif"); fs.Write(bb, 0, bb.Length); fs.Close(); } 客户端: [Test] public void TestSaveImage() { //1 iso串 Encoding _encoding = Encoding.GetEncoding(28591); StreamReader sr = new StreamReader(@"k:\x1.gif", _encoding); string strTemp = sr.ReadToEnd(); sr.Close(); //2 base64串 byte[] bb = _encoding.GetBytes(strTemp); string strBase64 = Convert.ToBase64String(bb); ws.SelfService client = new ws.SelfService(); client.SaveImages(strBase64); } 看一下现在的soap包: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <SaveImages xmlns="http://192.168.1.105/"> <strBase64Content>DggAOw==</strBase64Content> </SaveImages> </soap:Body> </soap:Envelope> 其中的粗体部分就是图片的内容,我省略了大部分,只留个小段。 (二)以二进制来传递但不保存文本 在进行base64的转换过程中要有性能损失,多一步就多消耗。因为现在不用对图片进行文本保存,那么这种持久不必要考虑,只要把图片存储下来就可以了。那么现在以二进制传递来存储图片。 服务端: [WebMethod] public void SaveBinaryImages(byte[] bb) { FileStream fs = File.Create(@"k:\soapPicBinary.gif"); fs.Write(bb, 0, bb.Length); fs.Close(); } 客户端: [Test] public void TestSaveBinaryImage() { FileStream fs = new FileStream(@"k:\x1.gif",FileMode.Open); byte[] bb=new byte[fs.Length]; fs.Read(bb,0,bb.Length); fs.Close(); ws.SelfService client = new ws.SelfService(); client.SaveBinaryImages(bb); } 现在的soap包情况: <?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <SaveBinaryImages xmlns="http://192.168.1.105/"> <bb>DggAOw==</bb> </SaveBinaryImages> </soap:Body> </soap:Envelope> 这个包我也省略了一部分。从上面两个包分析,原来,在.net web服务里,对包体中的附件的二进制附件传递是以base64编码来进行的,所以soap中包体中的附件的转换带来的是性能上的损失。 在进行soap消息传递过程中,经常要把各种附件一起发送,而这些附件通过是二进制格式。在soap规范中有对附件的描述,那就是使用MIME类型以URI模式引用MIME部件。对于web服务的工具来说,并不是所有的工具都提供了对soap附件的支持,例如.net web服务。但微软推出了基于DIME的附件解决方案,并通过WSE(web服务增强)来支持web服务的MTOM消息优化传输,当然在WCF中更是支持MTOM。MTOM 是一种机制,用来以原始字节形式传输包含 SOAP 消息的较大二进制附件,从而使所传输的消息较小。 Base64编码以3个字节编码规则为4个字节,多出三分之一的容量,对于base64编码请见: http://www.cnblogs.com/jams742003/archive/2010/03/26/1696876.html soap消息包,信念xml格式的soap体消息,以及信封中未定义的但与消息有关的任意数据格式的其它实体。soap消息包通过MIME的Multipart/related媒体类型构建,每个部件都嵌入MIME边界(bundary,在Content-Type报头中定义)。每个MIME部件都有报头信息如: Content-Type:表示数据的类型 Content Transfer-encoding:表示编码 Content-ID:表示引用内容的标识符 对于MIME消息的根部件要包含soap封套,且Content-type要设置为text/xml 下边是一个带有MIME格式附件的soap包: Content-Type:text/xml; charset=UTF-8 Content-Transfer-Encoding: binary Content-ID:<SOAP-ENV:Envelop> <?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope> <soapenv:Header> </soapenv:Header> <soapenv:Body> </soapenv:Body> </soapenv:Envelope> --==_Part_20081204084150203== Content-Type:text/plain Content-Transfer-Encoding:binary Content-ID:<attachment0.txt> aaaaaaaaaaaaaaaaaaaaaaaaaaa --==_Part_20081204084150203== Content-Type:image/jpeg Content-Transfer-Encoding:binary Content-ID:<attachment1.jpg> ......JFIF.....d.d.....C... --==_Part_20081204084150203==-- 以上就是一个完整的mime附件的soap包。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
在上篇随笔中对于客户实例传递的xml实现中,手动定义了xml的数据格式,如果现在对产品实例进行传递,那么还要手动对产品实例进行xml进行数据格式化。现在有一套为数据传递定义的协议,那就是soap。其实html也是一种数据存储格式,但html更注重的是表现数据。 (一)Soap是什么? Simple Object Access Protocol 简单对象访问协议。是一种轻量的,简单的,基于xml的协议。 在W3c中对它是这样描述的: 它是一种通信协议,用于应用程序之间的能信,它是一种用于发送消息的格式,被设计用来通过互联网进行通信,它基于xml,独立于平台,语言,它简单且可以扩展,它可以允许绕过防火墙。 可以所它认为是一种协议,同时它也是一种消息发送的格式。 (二)soap版本 Soap现在有两个版本,1.1和1.2版本。1.1版本是2000年5月发布的文档中描述的,它其中包含了大量互操作性问题及歧义,导致解释时出现偏差。21.2提供了一个更严密、更可靠的规范集合,它基于对协议和xml序列化进行绑定的一个抽象模型。(可以找相关协议规范阅读) (三)soap组成 现在看一个soap包(soap是一种协议,也是一种用于发送消息的格式,所以可以叫它soap包)。现在通过WS来看看这个soap包是什么样子。 建立Web服务,发布的方法就一个方法: [WebMethod] public string HelloWorld() { return "Hello World"; } 在客户调用端: 首先生成代理,直接从web服务地址查找服务生成代理,然后调用: using ws = FirstInstance; [TestFixture] public class SoapTest { [Test] public void TestSimpleSoap() { ws.SelfService client = new ws.SelfService(); Console.WriteLine(client.HelloWorld()); } } 测试的打印是正确的,现在进行抓包分析,对Http下的数据包截取有很多现成的工具,也可以自己写。 这是客户端请求时的包(http头省略): <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <HelloWorld xmlns="http://tempuri.org/" xmlns:a="http://schemas.datacontract.org/2004/07/ UnicodeTest.FirstInstance" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"/> </s:Body> </s:Envelope> 这是服务端响应的包: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <HelloWorldResponse xmlns="http://tempuri.org/"> <HelloWorldResult>Hello World</HelloWorldResult> </HelloWorldResponse> </soap:Body> </soap:Envelope> 这就是soap包。Soap包由封套Envelope组成,也叫信封,信封由一个soap头和包体组成,其中包体是必须的。 现在对客户类实例进行传输。 [WebMethod] public Customer GetCustomer() { Customer customer = new Customer { Unid = 13, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; return customer; } 客户端: public void TestCustomerSoap() { ws.Customer customer = new ws.Customer(); ws.SelfService client = new ws.SelfService(); customer = client.GetCustomer(); Console.WriteLine(customer.CustomerName); } 从WS返回的soap包是: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <GetCustomerResponse xmlns="http://tempuri.org/"> <GetCustomerResult> <Unid>13</Unid> <CustomerName>Songjiang</CustomerName> <CreateTime>2010-03-31T11:40:52.3125+08:00</CreateTime> <Telephone> <HomeCall>3333</HomeCall> <Mobile>1111111</Mobile> <FirmCall>2222</FirmCall> </Telephone> </GetCustomerResult> </GetCustomerResponse> </soap:Body> </soap:Envelope> 从中可以看到: <GetCustomerResponse xmlns="http://tempuri.org/"> <GetCustomerResult> <Unid>13</Unid> <CustomerName>Songjiang</CustomerName> <CreateTime>2010-03-31T11:40:52.3125+08:00</CreateTime> <Telephone> <HomeCall>3333</HomeCall> <Mobile>1111111</Mobile> <FirmCall>2222</FirmCall> </Telephone> </GetCustomerResult> </GetCustomerResponse> 这部分与上篇中的对客户类实例的xml数据格式化是很相近的。只是在整个过程中,自己没有进行显示的xml格式化。 而由ws进行显示的xml序列化。 现在看看通过XmlSerializer序列化器进行对客户类的手动序列化 public void TestSerialize() { Customer customer = new Customer { Unid = 13, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; FileStream fs = new FileStream("xmltest.xml", FileMode.Create); XmlSerializer formatter = new XmlSerializer(typeof(Customer)); formatter.Serialize(fs, customer); fs.Close(); } 文档内容: <?xml version="1.0"?> <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Unid>13</Unid> <CustomerName>Songjiang</CustomerName> <CreateTime>2010-03-31T11:55:32.375+08:00</CreateTime> <Telephone> <HomeCall>3333</HomeCall> <Mobile>1111111</Mobile> <FirmCall>2222</FirmCall> </Telephone> </Customer> 更多XmlSerializer的内容请见: http://www.cnblogs.com/jams742003/archive/2010/03/03/1677288.html 这个文档的内容与上篇中的内容相近。然后再来看看soap序列化器 SoapFormatter public void TestSoapSerialize() { Customer customer = new Customer { Unid = 13, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; FileStream fs = new FileStream("xmlSoap.xml", FileMode.Create); SoapFormatter formatter = new SoapFormatter(); formatter.Serialize(fs, customer); fs.Close(); } 内容为: <SOAP-ENV:Envelope> <SOAP-ENV:Body> <a1:Customer id="ref-1"> <_x003C_Unid_x003E_k__BackingField> 13 </_x003C_Unid_x003E_k__BackingField> <_x003C_CustomerName_x003E_k__BackingField id="ref-3"> Songjiang </_x003C_CustomerName_x003E_k__BackingField> <_x003C_CreateTime_x003E_k__BackingField> 2010-03-31T12:01:08.8750000+08:00 </_x003C_CreateTime_x003E_k__BackingField> <_x003C_Telephone_x003E_k__BackingField href="#ref-4"/> </a1:Customer> <a1:Call> <_x003C_HomeCall_x003E_k__BackingField id="ref-5"> 3333 </_x003C_HomeCall_x003E_k__BackingField> <_x003C_Mobile_x003E_k__BackingField id="ref-6"> 1111111 </_x003C_Mobile_x003E_k__BackingField> <_x003C_FirmCall_x003E_k__BackingField id="ref-7"> 2222 </_x003C_FirmCall_x003E_k__BackingField> </a1:Call> </SOAP-ENV:Body> </SOAP-ENV:Envelope> 是一个完整的封套(为了排版好看我删除了一些名字空间样的字串,其实这样也不好看)。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
数据传输是个很大的概念。我不是从理论的角度去记录随笔,只是把互联传输中的概念简化到消息的位面。以便于对分布传输中的消息进行一下剖析,加深印象。 对处于脱机状态下的终端来说,数据的传输可以通过中间介质进行中介传输。但在联机情况下,可以不必通过第三方的介入,仅用的就是其中的数据线。这个概念很容易理解: 1 一块干电池,两根导线,一个直流电灯泡。电池和灯泡可以看做是脱机情况下的两个终端,而电流就是数据。当两者相连,电灯泡就能亮(一切状态假设成立)。两者相连就能有电流通过,这个原理是懂的。因为电压的原因,电流会有一个完整的回路。(更深的不探讨)。这里互联的两上终端如果要有电流的传输,那么至少两个必要的因素:数据线和电压。 2 在农村浇灌庄稼地。主要的步骤是:拉电闸,抽水机把地下水抽上来,然后由垄沟把水导到地里进行灌溉。这里也是有必要的因素:水泵叫水,因为水压的原因使水向前流,有通到地里的垄沟。 联机中的终端要进行数据传输要有一些必要的条件,例如其中的通道,使数据进行传输的动力等等。 协议是什么? (引百度知道)在计算机范畴内:计算机通信网是由许多具有信息交换和处理能力的节点互连而成的,要使整个网络有条不紊地工作,就要求每个节点必须遵守一些事先约定好的有关数据格式及时序等的规则。 这些为实现网络数据交换而建立的规则、约定或标准就称为网络协议。 协议是通信双方为了实现通信而设计的约定或通话规则。 协议总是指某一层的协议。准确地说,它是在同等层之间的实体通信时,有关通信规则和约定的集合就是该层协议,例如物理层协议、传输层协议、应用层协议。 其实从字面上,我们都能理解所谓的协议是什么,这种意义对计算机通信而言也是有意义的。平常所熟知的协议有Http,Tcp/Ip,UDP等。协议是要对通信双方都有效的约定,而双方也都要符合这种约定,不管是软件环境还是硬件环境。 数据传输 计算机通信为的就是数据传输,但数据有所有同,从物理上都是二进制或者说最终在物理介质上都是二进制。 在局域网内,文件共享,打印机共享是最常见的。广域网中,电子邮件,实时通信,网页预览,资源下载,Ftp上传等。 它们的终端都要遵守共同的约定。 如果现在A和B要进行数据传输,例如A要把自己的客户名单传输到B上, 那么能过Http协议: B架设网站服务器来发布Html,A通过http来访问B网站上的客户名单导入页,然后导入自己的客户名单并提交,数据通过Http协议Post或Get方式把数据传送到B,B在收到数据后进行存储 在Tcp/Ip协议下: B建立套接字监听地址和端口,A与B建立连接,然后向B传递数据,B收到数据后进行存储。 而http协议中传输的是http协议包,包中包含着传递的数据,tcp协议中传输的tcp包,可能只是一个字符串。 (1)通过Socket传递字符串 服务端: static void Main(string[] args) { IPAddress address = IPAddress.Parse("192.168.1.98"); IPEndPoint _address = new IPEndPoint(address, 65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(_address); socket.Listen(20); byte[] bb=new byte[255]; while (true) { Socket ss = socket.Accept(); ss.Receive(bb); Console.WriteLine(Encoding.UTF8.GetString(bb)); } } 客户端: public void TestSocket() { IPAddress address = IPAddress.Parse("192.168.1.98"); IPEndPoint _address = new IPEndPoint(address,65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(_address); string strMessage = "test123"; byte[] bb = Encoding.UTF8.GetBytes(strMessage); socket.Send(bb); Console.WriteLine("Sent: {0}", strMessage); } 这里传递的是最简单的字符串。 在两端进行图片传递也可以参考消息(1)中的内容来实现。 (2)传递客户信息 对于传递客户信息,这里的方法就是转换为字串,转换为可解析的字串,例如json。这里通过json.net这个第三方json序列化和反序列化工具来实现对客户信息的序列化及反序列化。 服务端: IPAddress address = IPAddress.Parse("127.0.0.1"); IPEndPoint _address = new IPEndPoint(address, 65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(_address); socket.Listen(20); byte[] bb=new byte[350]; while (true) { Socket ss = socket.Accept(); ss.Receive(bb); Customer customer = new Customer(); MemoryStream ms=new MemoryStream(); ms.Read(bb,0,bb.Length); string strJson = Encoding.Default.GetString(bb); char c = '\0'; var p = from q in strJson where q != c select q; char[] chs = p.ToArray<char>(); strJson = new string(chs); try { customer=JsonConvert.DeserializeObject<Customer>(strJson); } catch { } ms.Close(); Console.WriteLine(customer.Unid); } 其中对json.net的更多内容可见官网我的博客上的几篇随笔: http://james.newtonking.com/projects/json-net.aspx http://www.cnblogs.com/jams742003/archive/2010/03/03/1677288.html 在客户端: public void TestCustomer() { Customer customer = new Customer { Unid = 13, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; string strJson = JsonConvert.SerializeObject(customer); byte[] bb = Encoding.Default.GetBytes(strJson); IPAddress address = IPAddress.Parse("127.0.0.1"); IPEndPoint _address = new IPEndPoint(address, 65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(_address); socket.Send(bb); Console.WriteLine("发送完毕"); socket.Close(); } 因为这个客户类的原因,使得在服务端和客户端都有有这个类。 在传递简单信息的时候,能很方便的传递,因为它们都是做为基元类型存在,但对于复杂类型,例如类类型,则要对两端都要有这个类。 在这个例子中,socket传递的包是带有一个json大串的字符串。通过抓包的工具,拿下这个包的内容是: {"Unid": 13,"CustomerName ":"Songjiang","CreateTime":" \/Date(1269998944484 +0800)\/","Telephone": {"HomeCall ":"3333","Mobile ":"1111111", "FirmCall":"2222"}} 现在有这么一种情况,在.net环境下这个数据传递是可以实现,但如果用到别的环境还能这样吗? 其实对于这个例子,它是可以的,因为json也是一种数据交换格式,是一种轻量级的。同时,可以利于机器的生成和解析。 还有一种格式就是xml格式,通过它也可以进行数据交换。这边是一个对客户信息进行xml化进行传输。 客户端: public void TestCustomerXml() { Customer customer = new Customer { Unid = 13, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; //创建xml XDocument doc = new XDocument(); doc.Add(new XElement("Customers")); doc.Element("Customers").Add(new XElement("Customer")); doc.Element("Customers").Element("Customer").Add( new XElement("Unid",customer.Unid), new XElement("CustomerName",customer.CustomerName), new XElement("CreateTime",customer.CreateTime), new XElement("Telephone") ); doc.Element("Customers").Element("Customer").Element("Telephone"). Add( new XElement("Mobile",customer.Telephone.Mobile), new XElement("FirmCall",customer.Telephone.FirmCall), new XElement("HomeCall",customer.Telephone.HomeCall) ); string strXml = doc.ToString(); byte[] bb = Encoding.Default.GetBytes(strXml); IPAddress address = IPAddress.Parse("192.168.1.105"); IPEndPoint _address = new IPEndPoint(address, 65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(_address); socket.Send(bb); Console.WriteLine("发送完毕"); socket.Close(); } 客户端创建一个描述客户数据的xml文档,然后向服务器提交。 服务端: static void Main(string[] args) { IPAddress address = IPAddress.Parse("192.168.1.105"); IPEndPoint _address = new IPEndPoint(address, 65000); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Bind(_address); socket.Listen(20); byte[] bb = new byte[350]; while (true) { Socket ss = socket.Accept(); ss.Receive(bb); Customer customer = new Customer(); MemoryStream ms = new MemoryStream(); ms.Read(bb, 0, bb.Length); string strXml = Encoding.Default.GetString(bb); char c = '\0'; var p1 = from q in strXml where q != c select q; char[] chs = p1.ToArray<char>(); strXml = new string(chs); XDocument doc=XDocument.Parse(strXml); var p = from q in doc.Element("Customers").Element("Customer").Elements() select q; customer = new Customer { Unid = Convert.ToInt32( p.Where(x => x.Name == "Unid").First<XElement>().Value), CustomerName = (p.Where(x => x.Name == "CustomerName"). First<XElement>().Value).ToString(), CreateTime = Convert.ToDateTime(p. Where(x => x.Name == "CreateTime"). First<XElement>().Value), Telephone = new Call { Mobile = p.Where(y => y.Name == "Telephone"). First<XElement>().Elements().Where(x => x.Name == "Mobile"). First<XElement>().Value, FirmCall = p.Where(y => y.Name == "Telephone"). First<XElement>().Elements().Where(x => x.Name == "FirmCall"). First<XElement>().Value, HomeCall = p.Where(y => y.Name == "Telephone"). First<XElement>().Elements().Where(x => x.Name == "HomeCall"). First<XElement>().Value } }; ms.Close(); Console.WriteLine(customer.Unid); } } 服务端接收到xml后进行解析,分离数据。 服务端与客户端都以xml格式进行数据的解析和生成,xml就是他们共同的约定。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
数据怎么传递? 在两个终端之间A和B。没有直接的网络连接,那么如果要进行A到B的数据传递,可以通过中间介质来进行中介传递:通过移动U盘把数据从A上拷贝下来,然后再备份到B上,这时,两者通过间接的连接达到传递的目的。而这些数据在系统中是具体的,例如一个文本文件,文本中内容是一份人员表单,其中记录着一次聚会的人员名单,及聚会的时间,地点等信息。对于计算机来说这些数据就是二进制数据,而对于计算机应用者来说,这些数据是我们可见的,字符串类型的内容。 在.net下,如何实现对文本文件的复制与保存的,是如何实现对字符串的读写的? (1)创建字符串并存在为文本 public void TestCreateText() { string strContent = "这是一个文本file。"; StreamWriter writer = File.CreateText(@"k:\myText.txt"); writer.WriteLine(strContent); writer.Close(); } 现在在文本中记录一个字串:这是一个文本file。 通过IO进行创建文本,并向文本中写入字符串。实现很简单,过程很明了。 (2)读取文本文件,并打印文本内容 public void TestReadText() { string strContent = string.Empty; StreamReader reader = File.OpenText(@"k:\myText.txt"); strContent=reader.ReadToEnd(); reader.Close(); Console.WriteLine(strContent); } (3)保存客户信息到文本文件 其中的客户信息被封装到一个客户类实例中,那么如何保存? public void TestWriteCustomerText() { Customer customer = new Customer { Unid = 1, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; StreamWriter writer = File.AppendText(@"k:\myText.txt"); writer.WriteLine(customer); writer.Close(); } 这种方法不可以的,因为StreamWriter的WriteLine方法重载的object方法向文件写入的是并不是customer的内容。如果要写入这个客户实例的内容,包括标识,姓名,创建时间等信息,只能想可以解决的方法,例如把这些信息以字符串来表示,然后就可以写入文本了。 可以这样: (1)直白的记录 writer.WriteLine("unid:"+customer.Unid); 那么在读取的时候,例如从文本中显示,还要通过一些方法来解析。如果只是单纯的读取文本,可能它并不能明确的表示意义,例如这个文本读取的内容是:unid:1(尽管这里可以看出并知道它表示unid是1)。如果可以把它还原为一个客户实例,那么可以说,它是有意义的(至少在一定范围内,它是有意义的,最起码它确实表示一个客户的unid)。 (2)通过Json来实现 Json可以明确的解析,尽管它的文本意义不能很好的表示(至少不能一目了然),但这种方法是可以满足以上的要求。 public void TestJsonWriter() { Customer customer = new Customer { Unid = 1, CustomerName = "Songjiang", CreateTime = DateTime.Now, Telephone = new Call { Mobile = "1111111", FirmCall = "2222", HomeCall = "3333" } }; DataContractJsonSerializer ds = new DataContractJsonSerializer(typeof(Customer)); FileStream fs = new FileStream(@"k:\myText.txt", FileMode.Open); ds.WriteObject(fs, customer); fs.Close(); } 能过对客户实例进行Json化(可以说Json序列化),然后生成json串,进行文本保存。 Json序列化和反序列化有很多种实现方法,在.net3.5中提供了json序列化和反序列化的实现。它的名字空间是: System.Runtime.Serialization.Json 类库文件为:System.ServiceModel.Web.dll 更多序列化和json序列化内容请见: http://www.cnblogs.com/jams742003/archive/2010/03/03/1677288.html 下边通过读取json字串,转换为json对象,最终转换为客户类实例: public void TestJsonReader() { Customer customer = new Customer(); DataContractJsonSerializer ds = new DataContractJsonSerializer(typeof(Customer)); FileStream fs = new FileStream(@"k:\myText.txt", FileMode.Open); customer=(Customer)ds.ReadObject(fs); fs.Close(); Console.WriteLine(customer.Unid); } 这种json序列化保存的客户数据(Customer类的一个实例,一个客户信息)是有规则的,这个规则对于两方(序列化和反序列化)来说是约定好的,这种约定使得双方都可以通过这种约定保存或读取数据,并使数据有意义。 (3)文本的二进制 一个文本文件对于我们来说是一个可以直接读的文本内容,一个大字符串,但在电脑上保存时(物理上),它是以二进制格式进行保存的。 例如:得到abc这个字符串的二进制 在Ascii码环境下,abc这个字符串的二进制是: 97 98 99 1100001 1100010 1100011 对于文本文件是如何读取的?例如通过记事本来打开上边用到的那个文本。 ·得到这个文本的二进制流 ·得到这个文件的字符编码,文本文件默认的是ANSI编码,在中文系统下,它就是GB2312编码。 ·解释流 ―――――――――――――――――――――――― {"CreateTime":"\/Date(1269854982203+0800)\/","CustomerName":"Songjiang","Telephone":{"FirmCall":"2222","HomeCall":"3333","Mobile":"1111111"},"Unid":1} ―――――――――――――――――――――――― 这是一长字符串,里边没有127以后的字符,即它们都属Ascii码范围。它的二进制就是分别得到这些Ascii字符的二进制。 例如:前三个{“C这三个字符的编码,通过Ascii码表:123 34 67这时10进制,在16进制下是:7b 22 43 用ultra打开这个文本文件,并查看16进制就是:7b 22 43。(在有些编码环境下,文本文件前有多余的字符,叫做BOM) 图片文件在物理上也是二进制格式存储的,但在电脑上可以看到这些二进制所表示的内容(一张图片)。用记事本打开一张gif。是一堆的乱码,但前三个gif是认识的。记事本默认ANSI编码,在中文系统下是GB2312编码。 (4)文本文件中的图文混排 现在把一张gif图与一些文本以文本格式存在一起。为简单起见,文本内容为: A然后是图片的二进制。 首先说明一下图片如果用记事本打开看到的乱码的问题:对于二进制来说,图片读到流,然后写到字节数组中。我的操作系统是win2003,记事本默认的字符编码格式为ANSI,在中文系统下代表GB2312,所以从记事本打开图片的通过GB2312字符编码对字节数组的解析。 public void TestAscii() { StreamWriter writer = File.CreateText(@"k:\MixTest.txt"); FileStream fileStream = new FileStream(@"k:\x1.gif", FileMode.Open); BinaryReader reader = new BinaryReader(fileStream, _encoding); byte[] bb=new byte[fileStream.Length]; reader.Read(bb, 0, bb.Length); Encoding encoding = Encoding.GetEncoding(936); string strTemp = encoding.GetString(bb); reader.Close(); fileStream.Close(); writer.WriteLine(strTemp); writer.Close(); } 那么图片就被保存成了GB2312字符串。这个过程不可逆,因为其中的转换可能不是包含的关系。例如:FF在Ascii码环境下就会被?号所代替,然后还有其它例如BOM等因素,所以这种在GB2312下转换图片是不可逆的。(乱码不可怕,乱码其实不乱,它只是所在的字符集中的字符而已,但未转换前可能不是这个样子) 如果要把图片保存为文本串要保证: ·对于00到FF间的字节都要有正确的表示,Ascii码就不行 ·一定要是一个字节是一个字节。意思就是整个数组不能中间添加其它的字节 解决方法: 第一步,通过找00到FF之间存在的都是一个字节的字符编码来实现,例如:ISO/IEC 8859-1字符集,它是第一个8位字符集,而ascii是7位的,只能表示0—127。8859-1的代码页为:28591。 第二步,对字节进行Base64类似的变换。让中文下的环境支持编码,不要添加或删除字节。 写入到文本中的顺序是:ISO8859-1字串——Base64字串——gb2312串(默认) 从文本到图片的顺序是:gb2312字串(默认)——Base64字串——ISO8859-1字串 写入到文本: public void TestReadPic() { //1 iso串 Encoding _encoding = Encoding.GetEncoding(28591); StreamReader sr = new StreamReader(@"k:\x1.gif", _encoding); string strTemp=sr.ReadToEnd(); sr.Close(); //2 base64串 byte[] bb = _encoding.GetBytes(strTemp); string strBase64 = Convert.ToBase64String(bb); StreamWriter writer = File.CreateText(@"k:\MixTest.txt"); writer.Write(strBase64); writer.Close(); } 保存的Base64字串(ANSI编码下,在中文系统中,ANSI指GB2312)中的一段: R0lGODlhZABkAPcAAAAAM8FvZ2ItH//pvF0qIHpQbjMzM/ LNr2tjQ4GAntKypPDp60lCRmg2KTQYUh4E …… 然后是从文本文件到图片: public void TestWritePic() { //1 base64串 Encoding _encoding = Encoding.GetEncoding(936); StreamReader sr = new StreamReader(@"k:\MixTest.txt", _encoding); string strBase64 = sr.ReadToEnd(); sr.Close(); //2 ISO串 byte[] bb = Convert.FromBase64String(strBase64);//iso字节 Encoding encoding = Encoding.GetEncoding(28591); string strReturn = encoding.GetString(bb); FileStream fs = File.Create(@"k:\MixTest3.gif"); fs.Write(bb, 0, bb.Length); fs.Close(); } 图片是正常的。 图文混排的方式也在以上的基础上实现了。 以上就是数据存放的方式,以及在.net环境下进行的字符编码以及流之间的转化工作。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
XmlDictionaryWriter,是一个抽象类,从该类中派生了WCF,以便执行序列化和反序列化。 它有4种格式书写器: CreateBinaryWriter,用于创建写入WCF二进制xml格式的实例 CreateMtomWriter,用于创建以MTOM格式mxl的实例 CreateTextWriter,用于创建写入文本xml的实例 (一)CreateTextWriter 以文本格式写入xml,工厂方法有3个重载: CreateTextWriter(Stream) CreateTextWriter(Stream, Encoding) CreateTextWriter(Stream, Encoding, Boolean) 其中第三个方法中的bool参数用于指定流操作:如果为 true,则完成时编写器关闭流;否则为 false。而字符编码Encoding默认的是utf-8。且只支持utf-8,或unicode大头或小头三种编码。 unicode大头小头就是:Big-Endian,Little-Endian(直译过来就是大头结尾,小头结尾)。其中big-endian是在低地址放高位字节,另一个则相反。例如:0x12345678这个16进制的数字 big-endian 低地址——高地址 12|34|56|78 Little-endian 低地址——高地址 78|56|34|12 (说实在的,big-endian更符合人们的习惯) 例如:“赵”字的unicode的big-endian(可以在记事本中写个赵字,然后保存时编码选择big-的,然后在ultra中打开,看它的16进制编码),只看它的BOM部分就知道了:FE FF 在程序中这个编码可以由Encoding的属性来设置:Encoding.BigEndianUnicode public void TestTextWriter() { MemoryStream ms = new MemoryStream(); using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(ms, Encoding.BigEndianUnicode, false)) { writer.WriteStartDocument(); writer.WriteElementString("UserName", "Songjiang"); writer.Flush(); } byte[] bb = ms.ToArray(); Console.WriteLine(BitConverter.ToString(bb)); ms.Position=0; Console.WriteLine(new StreamReader(ms).ReadToEnd()); ms.Close(); } 这里的工厂方法的第三个参数指定为了false,设置在完成wirter的关闭后,不自动关闭对应流,因为后边还要用到这个流。用完后再显示关闭可以了。 它的输出为: FE-FF-00-3C-00-3F-00-78-00-6D-00-6C-00-20-00-76-00-65-00-72-00-73-00-69-00-6F-00-6E-00-3D-00-22-00-31-00-2E-00-30-00-22-00-20-00-65-00-6E-00-63-00-6F-00-64-00-69-00-6E-00-67-00-3D-00-22-00-75-00-74-00-66-00-2D-00-31-00-36-00-42-00-45-00-22-00-3F-00-3E-00-3C-00-55-00-73-00-65-00-72-00-4E-00-61-00-6D-00-65-00-3E-00-53-00-6F-00-6E-00-67-00-6A-00-69-00-61-00-6E-00-67-00-3C-00-2F-00-55-00-73-00-65-00-72-00-4E-00-61-00-6D-00-65-00-3E <?xml version="1.0" encoding="utf-16BE"?><UserName>Songjiang</UserName> 字符编码可以在流字节和xml看出来:FEFF的BOM,和encoding=”utf-16be” 再看看utf-8编码下的情况(只在CreateTextWriter方法中将编码改为utf-8即可): 3C-3F-78-6D-6C-20-76-65-72-73-69-6F-6E-3D-22-31-2E-30-22-20-65-6E-63-6F-64-69-6E-67-3D-22-75-74-66-2D-38-22-3F-3E-3C-55-73-65-72-4E-61-6D-65-3E-53-6F-6E-67-6A-69-61-6E-67-3C-2F-55-73-65-72-4E-61-6D-65-3E <?xml version="1.0" encoding="utf-8"?><UserName>Songjiang</UserName> Utf-8的BOM是EF BB BF,但这里的字节却没有。可见,对于Text wirter来说,utf-8编码下,utf-8的BOM是省略的,在组包过程中,这点要注意。 (二)CreateBinaryWriter 以MTOM格式写入xml,工厂方法有2个重载: CreateMtomWriter(Stream, Encoding, Int32, String) CreateMtomWriter(Stream, Encoding, Int32, String, String, String, Boolean, Boolean) 这里说一下第一个方法: 前2个参数不用说,一个管流,一个管字符编码。然后是int参数,用于设置缓冲的最大字节数,第4个字串型用于设置soap头中的ContentType属性。(ContentType用于描述内容类型的字符串,格式通常为:类型/字类型,其中类型为常规内容范畴,而子类为特定内容类型。对于这个,可以网上找下,例如:text/html) public void TestMTOMWriter() { MemoryStream ms = new MemoryStream(); XmlDictionaryWriter _writer = XmlDictionaryWriter.CreateMtomWriter(ms, Encoding.UTF8, 1000, "Application/soap+xml"); _writer.WriteStartDocument(); _writer.WriteElementString("UserName", "Songjiang"); _writer.Flush(); byte[] bb = ms.ToArray(); Console.WriteLine(BitConverter.ToString(bb)); StreamReader sr = new StreamReader(ms); ms.Position = 0; string sx = sr.ReadToEnd(); Console.WriteLine(sx); ms.Close(); sr.Close(); } 结果: 4D-49-4D-45-2D-56-65-72-73-69-6F-6E-3A-20-31-2E-30-0D-0A-43……省略 MIME-Version: 1.0 Content-Type: multipart/related;type="application/xop+xml"; boundary="551a8456-58c9-46ff-b481-f81747b71098+id=1"; start="<http://tempuri.org/0/634052866078593750>"; start-info="Application/soap+xml" --551a8456-58c9-46ff-b481-f81747b71098+id=1 Content-ID: <http://tempuri.org/0/634052866078593750> Content-Transfer-Encoding: 8bit Content-Type: application/xop+xml;charset=utf-8;type="Application/soap+xml" <?xml version="1.0" encoding="utf-8"?><UserName>Songjiang</UserName> --551a8456-58c9-46ff-b481-f81747b71098+id=1-- 看第二个方法: CreateMtomWriter(Stream, Encoding, Int32, String, String, String, Boolean, Boolean) 前4个参数已经说过, Stream stream, Encoding encoding, int maxSizeInBytes, string startInfo, string boundary, string startUri, bool writeMessageHeaders, bool ownsStream 现在说后4个,从字面上可以看出,第5个用于设置MIME边界字串,第6个用于设置MIME部分的ID uri,第7个用于设置是否写入消息头,最后一个用于设置在完成writer的关闭时,是否关联关闭对应流。也写一个例子: XmlDictionaryWriter _writer = XmlDictionaryWriter. CreateMtomWriter(ms, Encoding.UTF8, 1000, "Application/soap+xml" ,"thisisBoundary============","startUri===1234567890",true,false); MIME-Version: 1.0 Content-Type: multipart/related;type="application/xop+xml"; boundary="thisisBoundary============"; start="<startUri===1234567890>"; start-info="Application/soap+xml" --thisisBoundary============ Content-ID: <startUri===1234567890> Content-Transfer-Encoding: 8bit Content-Type: application/xop+xml;charset=utf-8;type="Application/soap+xml" <?xml version="1.0" encoding="utf-8"?><UserName>Songjiang</UserName> --thisisBoundary============-- 其中,黑体部分标出了边界和起始标识串的位置,而斜体字部分就是消息头,这部分由这个方法的第7个布尔参数来控制。(对于边界,它以一行开始,且前2个字符为--,而总边界结束也由—结尾,还要注意起始头添加了一对尖括号,这些内容可以查阅相关文档) 对于soap中的MIME附件,这个方法可以很好的实现。 (三)CreateMtomWriter 以二进制写入xml 它有4个重载方法: CreateBinaryWriter(Stream) CreateBinaryWriter(Stream, IXmlDictionary) CreateBinaryWriter(Stream, IXmlDictionary, XmlBinaryWriterSession) CreateBinaryWriter(Stream, IXmlDictionary, XmlBinaryWriterSession, Boolean) 它的参数为: Stream stream, IXmlDictionary dictionary, XmlBinaryWriterSession session, bool ownsStream 其中,第一个与第四个就不说了,第二个表示用于压缩的XmlDictionary对象,如果不压缩则写null,第三个用于允许发送者和接收者自动创建和协调一个动态的XmlDictionary public void TestBinaryWriter() { MemoryStream ms = new MemoryStream(); XmlDictionaryWriter _writer = XmlDictionaryWriter.CreateBinaryWriter(ms, null,null); _writer.WriteStartDocument(); _writer.WriteElementString("UserName", "Songjiang"); _writer.Flush(); byte[] bb = ms.ToArray(); Console.WriteLine(BitConverter.ToString(bb)); StreamReader sr = new StreamReader(ms); ms.Position = 0; string sx = sr.ReadToEnd(); Console.WriteLine(sx); _writer.Close(); ms.Close(); sr.Close(); } 结果: 40-08-55-73-65-72-4E-61-6D-65-99-09-53-6F-6E-67-6A-69-61-6E-67 @€serName� Songjiang 更多详细内容请见: http://www.cnblogs.com/frank_xl/archive/2009/12/01/1614830.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
Base64是什么? Base64编码的用途是为了传输和传输安全(其实并不太安全)。 它的算法是:每3个字节(每字节8bit),转换为4个6bit的字节(一个字节应该是8bit,所以前2位补0),然后每个6位前2位(高位)被2个零。例如: xxxxxxxx yyyyyyyy xxxxyyyy这里转换前的3个字节,然后,每6位分到一个字节中: xxxxxx xxyyyy yyyyxx xxyyyy 然后高位补0 00xxxxxx 00xxyyyy 00yyyyxx 00xxyyyy 其中xy是二进制的0和1,然后再按base64码表进行替换(base64,基本的64个码,=号不在其内) 0 A 16 Q 32 g 48 w 1 B 17 R 33 h 49 x 2 C 18 S 34 i 50 y 3 D 19 T 35 j 51 z 4 E 20 U 36 k 52 0 5 F 21 V 37 l 53 1 6 G 22 W 38 m 54 2 7 H 23 X 39 n 55 3 8 I 24 Y 40 o 56 4 9 J 25 Z 41 p 57 5 10 K 26 a 42 q 58 6 11 L 27 b 43 r 59 7 12 M 28 c 44 s 60 8 13 N 29 d 45 t 61 9 14 O 30 e 46 u 62 + 15 P 31 f 47 v 63 / 从码表中可以得知,base64编码后的字串只包含在小写字母,数字,还有+/这2个特殊字符。 还有一些扩展的base64用法。例如用于url传递的其中把+号和左斜线换成了*和- 现在以Hello这个单词来测试一下,之前,先通过.net提供的base64转换方法得到它的base64编码。 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public static string Base64Encoding(this string selfChar,Encoding encoding){ byte[] bb = selfencoding.GetBytes(selfChar); return Convert.ToBase64String(bb);} 在Ascii编码环境下 Hello这个单词有5个字母,就是5个字节,那么可以把它分为Hel lo两部分。第一部分的Hel是3个字节,它的二进制格式为(可以查ascii表,也可以由上一篇中给出的方法求):010010000110010101101100 然后分为4个6位:010010 000110 010101 101100 然后高位补0:00010010 00000110 00010101 00101100,然后换算为10进制为18 6 21 44 然后与码表对应替换:SGVs 下边对后2个字节进行编码。因为base64要求8*3——6*4,然后才进行下一步计算。这里只有2个字节,那应该对其补,补就是用=号来替换。 开始:lo的二进制为0110110001101111 因为不够24位,所以分为3个6位字节011011 000110 111100 011011 000110 111100,10进制数为:27 6 60,然后再补一个=号就是 bG8= 与上边的连起来就是SGVs bG8= 在程序中验证,这个是正确的。 再做一个汉字语句的base64编码:赵。 在utf-8编码下,它的二进制是:111010001011010110110101 它有3个字节,分为4个字节:111010 001011 010110 110101,补0:00111010 00001011 00010110 00110101 10进制是:58 11 22 53 码表:6LW1。 在GB2312环境下的64位编码是怎样的? “赵”字的gb2312编码环境下的字节有2个,它的二进制位是:1101010111010100,分为6位的: 110101 011101 010000 对应的10进制是:53 29 16,对应码表是1dQ,然后补=号就是 1dQ= 在Ascii环境下的64位编码是怎样的? “赵”字不在Ascii码内(ascii在127之内)。那么对于编码环境下不存在的字符怎么样处理呢? 在ascii环境下,任何大于127的,即U+007F(unicode编码127的),则由ascii的问号替换。 然后,问号在unicode值下是63(Ascii下也是,这是10进制数),63的二进制是:111111。这是一个字节(按Base64编码算法,要补齐3个字节,所以最后要加2个等号),它应该是:00111111,然后每6位分一字节:001111 110000 不足的在后边补0,这个补0与6位变8位补不是一样的概念。那么这两个8位的10进制数是: 15 48,对应码表就是Pw, 然后还要加上2个等号== 最后变成了Pw== ANSI American National Standards Institute(ANSI——美国国家标准局) 为使计算机支持更多语言,通常使用 0x80—0xFF 范围的 2 个字节来表示 1 个字符。比如:汉字 '中' 在中文操作系统中,使用 [0xD6,0xD0] 这两个字节存储。 不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。在简体中文系统下,ANSI 编码代表 GB2312 编码,在日文操作系统下,ANSI 编码代表 JIS 编码。 不同 ANSI 编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
字符编码 编码是一个将一组Unicode字符转换业个字节序列的过程。而解码是将一个编码字节序列转换为一组Unicode字符的过程。 Unicode字符是什么? Unicode字符集可以简写为UCS,也就是Unicode charactor set Unicode编码是国际组织制定的可以容纳世界上所有文字和符号的字符编码方案。它通过0到0x10FFFF来映射字符,最多可容纳1114112个字符(16进制的10FFFF的值是1114111,然后加一个0x000000就是1114112个)。可以看一下1114112的二进制表示形式为:1 0001 00000000 00000000 UTF是什么? UTF是Unicode转换格式的意思,是UCS Transformation Format的缩写。 Utf-8 UTF-8以字节为单位对Unicode进行编码。utf-8特点是对不同范围的字符用不同长度的编码。从Unicode到UTF-8的编码方式如下: Unicode编码(16进制) ║ UTF-8 字节流(二进制) 000000 - 00007F ║ 0xxxxxxx 000080 - 0007FF ║ 110xxxxx 10xxxxxx 000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 例如:“赵”这个字的Unicode编码(16进制表示方法)是:8d 75 这个编码在.net中可以通过ToString()方法来实现。为了进行后边的说明。这里先给出测试用的转换方法: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public static class CharSetHelper{ public static string TransCoding(this int iValue,eTrans eType) { return Convert.ToString(iValue, (int)eType); } public static string GetCorrectCoding(this string selfChar, Encoding encoding, eTrans eType) { int iUnicode = (int)char.Parse(selfChar); return iUnicode.TransCoding(eType); }}public enum eTrans { Binary=2, Octonary=8, Decimal=10, Hexadecimal=16 } 一个枚举,用于枚举数的进制,一个从字串转换到特定的字符编码,并以指定进制表示的方法。 ·另外,可以再把系统的计算器调出来,改为科学型。 ·准备可以查看进制的编辑器,我用的是ultra。 另外说明一下:char.Parse方法:它将指定字符串的值转换为它的等效 Unicode 字符 百家姓赵钱孙李中的“赵”字,这里是简体赵字。可以查一下它的Unicode编码,并用10进制和16进制表示: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->string cc="赵";UnicodeEncoding _unicode = new UnicodeEncoding();string s1 = cc.GetCorrectCoding(_unicode, eTrans. Decimal);string s2 = cc.GetCorrectCoding(_unicode, eTrans.Hexadecimal); 10进制:36213 16进制:8d75 然后新建立记事本。写一个“赵”字,保存,保存时编码选择unicode。然后用ultra打开。切换到16进制编辑模式。可以看到:FF FE 75 8D 还有一个要说的就是这里的10进制是8D75的10进制表示法,同时也是Unicode编码表中汉字“赵”的编号。 其中8D 75是“赵”字的16进制编码。而多出来的FE FF就是字节序,byte order mark(BOM),用来判断字节流的字节序。在传输字节流前,先传输被作为BOM的字符。 下边是utf的BOM: UTF-8 ║ EF BB BF UTF-16LE ║ FF FE UTF-16BE ║ FE FF UTF-32LE ║ FF FE 00 00 UTF-32BE ║ 00 00 FE FF 这个字节序不要出现在传输中,例如:在进行组包发送数据时,当字符使用utf-8编码时,会多出BOM,所以要先截除BOM,然后进行传输,这点是要注意的。对于utf-8编码的字符,要向前截除3个字节。 下面再看一下UTF-8编码的16进制。还是“赵”字。在记事本中添加“赵”字,编码选择utf-8,然后在ultra中打开,切换到16进制模式,之前,先看一下这个字在程序中的utf-8编码下的16进制情况: 16进制:E8B5B5 现在提供一下编码的16进制查看方法: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->public static string GetRightEncodingString(this string selfChar,Encoding encoding, eTrans eType){ byte[] bb = encoding.GetBytes(selfChar); bb=bb.Reverse().ToArray(); string strTemp = string.Empty; foreach (byte b1 in bb) { strTemp += Convert.ToInt32(b1.ToString()).TransCoding(eType); } return strTemp;} 这个方法是连着上边的进行的。 在utf-8下的的记事本上,“赵”字的全16进制格式是:EF BB BF E8 B5 B5 其中后3个字节是“赵”字的utf8编码,而前三个字节就是BOM了。 再来看一下,utf-8的编码格式: 000000 - 00007F ║ 0xxxxxxx 000080 - 0007FF ║ 110xxxxx 10xxxxxx 000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 010000 - 10FFFF ║ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 在0-7F之间用的一个字节。与Ascii码对应。7F就是10进制的127。 在128-2047之间用2个字节。 在2048-65535之间用3个字节。 在65536-1114111之间用4个字节 而unicode与utf-8之间的转换怎么样的?还以上表为例子,例如:“赵”字, 它的16进制unicode编码是:8D75,它在第三行也就是2048-65535之间。 000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 然后,8D75的二进制表示为(用计算器转一下):1000110101110101 然后,用这些二进制从低位向高位(从右向械)依次取6位: 1000-110101-110101 然后替换x,如果不够位数,则高位用0补,然后得到的二进制是: 11101000-10110101-10110101 然后,这3个字节的16进制就是:E8 B5 B5 然后再以字母M来试一下,因为这个字母是Ascii码表中的值(这样说不太准确),或者说它是127之内的值,所以utf-8编码格式与ascii一样。(尽管如此,做为utf-8编码,文本串最前边还是多出3个字节,BOM) 现在再找一个字:“李”,它的unicode编码值是: 674E 它的范围在:000800 - 00FFFF ║ 1110xxxx 10xxxxxx 10xxxxxx 这个范围内,然后,这2个字节的二进制是:110011101001110,然后由低位向高位按6位取: 110-011101-001110,不足4位的高位用0补:0110-011101-001110,然后替换x就是: 11100110-10011101-10001110,16进制数是:E69D8E 在ultra中可以验证一下:utf-8全文:EF BB BF E6 9D 8E 现在通过程序来实现一下Unicode到utf-8的转换(通过移位来进行) 准备工作:参照:1110xxxx 10xxxxxx 10xxxxxx 其中的X用0替换,表示为: 11100000 10000000 10000000 然后,一个unicode编码的字符是2个字节,就是16位,而utf-8编码(这里还以汉字为例)是3个字节 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->UnicodeEncoding _unicoding=new UnicodeEncoding();string str="赵";int k = int.Parse(str.GetUnicode(new UnicodeEncoding(), eTrans.Decimal));byte[] bb = _unicoding.GetBytes(str); 先得到“赵”字的unicode值和字节。 11100000 10000000 10000000 这三个字节,会16进制表示为:0xE0,0x80,0x80 这个字的unicode的编码的二进制表示为:1000-110101-110101 先算第一个字节: 第一个字节为0xE0与上1000(二进制),而1000可以是“赵”字的unicode编码右移12位,所以: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int k1 = k >> 12;_list.Add(0xe0|k1); 第二个字节是0x80与上110101, 而110101是“赵”字的unicode编码右移6位,变为:1000-110101,然后与上111111就可以了,而111111的16进制为:0x3F,所以: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int k2 = k >> 6 &0x3F;_list.Add(0x80 | k2); 第三个字节是0x80与上110101, 110101是“赵”字的unicode的后6位,所以与上6个1就可以了,所以: Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->int k3 = k & 0x3F;_list.Add(0x80 | k3); 结果是: 232|181|181 E8|B5|B5 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
RemotingConfiguration类进行类型注册时,可以采用程序配置方式,也可以通过配置文件来进行。这个类有一个Configure方法: public static void Configure( string filename, bool ensureSecurity ) Filename就就文件名(配置文件的文件名),第二个参数用于选择安全性的启用与否 在服务端激活下用配置文件来实现: 在控制台应用程序下添加SelfService.config文件(一个文件),添加以下内容: <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application name="ServerRemoting"> <service> <wellknown mode="Singleton" type="SelfRemote.selfRemoteObject,SelfRemote" objectUri="selfRemoteObject"/> </service> <channels> <channel ref="http" port="10001"/> </channels> </application> </system.runtime.remoting> </configuration> 在这个文件中,通道注册,类型注册与程序中对应。然后把此文件的 复制到输出目录 设置为 始终复制(为的就是在生成的控制台应用程序同级目录,能够找得到这个配置文件)。 然后,在控制台可以: Console.WriteLine("http 通道remoting服务开始……"); RemotingConfiguration.Configure("SelfService.config", false); onsole.Read(); 这样就可以了。 在客户端,如果采用客户端激活方式,那么同上差不多,同时也要保证在程序同级目录能找到文件(始终复制): 新建立ClientService.config文件,文件内容为: <?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application name="ServerRemoting"> <service> <wellknown mode="Singleton" type="SelfRemote.selfRemoteObject,SelfRemote" objectUri="selfRemoteObject"/> </service> <channels> <channel ref="http"/> </channels> </application> </system.runtime.remoting> </configuration> 这里的channel不要再指定端口。 在测试中: RemotingConfiguration.Configure("ClientService.config",false); selfRemoteObject app1 = new selfRemoteObject(); Assert.AreEqual(0, app1.Unid); 这样就可以了。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
在生成远程对象时,创建和初始化新对象就是激活。远程处理系统须始终了解所需的激活类型才能将对象设置为可供客户端使用。激活有两种方式:服务器激活和客户端激活 服务器激活 服务器激活的对象是生存期直接受服务器直接控制的对象。仅当客户端对对象进行方法调用时,服务器应用程序域才会创建这些对象,而不是当客户端调用new或Activator.GetObject的时候。客户端请求服务器激活类型的实例时,会在客户端应用程序域中创建一个代理。服务器激活类型只允许使用默认构造器。如果想使用特定的采用参数的构造器创建实例的类型,可以使用客户端激活,也可以动态发布特定实例。 服务器激活的对象有两种激活模式:Singleton和SingleCall,这两种模式又叫已知对象,由枚举类型:WellKnownObjectMode来标识。 Singleton类型在任一时刻只有一个实例。所有的客户端请求都将由这个实例提供服务。如果不存在实例,则服务器创建一个,且所有的后来的客户端请求都将由这个实例提供服务。对于单件类型,会关联到默认的生存期。 SingleCall类型针对每个客户端请求创建一个实例。下一个方法调用将由其他服务器实例提供服务,即使在系统尚未回收前一个实例的引用的情况下也是这样。 类RemotingConfiguration 方法 RegisterWellKnownServiceType 将服务器上的对象类型注册为已知类型 public static void RegisterWellKnownServiceType( Type type, string objectUri, WellKnownObjectMode mode) 所有知道已注册已知对象的URI的客户都可以获取该对象的代理,方法是:用ChannelServices注册信道,然后调用new或Activator.GetObject方法来激活对象。 ·当用new来激活对象时,须先要使用RegisterWellKnownClientType方法在客户端注册该已知对象类型。调用RegisterWellKonwnClientType方法向远程处理基础结构提供远程对象的位置,使new关键字可以创建它。 ·当用Activator.GetObject方法来激活已知对象时,须将对象的URL作为参数提供给该方法,所以在这种情况下,不用预先在客户端注册。 当调用到达服务器后,系统从消息中提取URI,检查远程处理表以定位与该URI匹配的对象的引用,然后实例化该对象,将方法调用转发给该对象。如果是SingleCall,则在方法调用完成后,该对象被销毁。为每个方法创建对象的一个新实例。 Activator.GetObject与new的区别是:Activator.GetObject可以指定URL作为参数,new的时候是从配置中获取URL (1)通过Actovator.GetObject方法来获取代理 Activator类位于System下。它包含一些方法,用于在本地或从远程创建对象类型,或获取对现有远程对象的引用。其中GetObject这个方法有2个重载,方法的意义是:为已知对象或 XML Web services 创建一个代理。 GetObject(Type, String) 为指定类型和 URL 所指示的已知对象创建一个代理。 GetObject(Type, String, Object) 为指定类型、URL 和通道数据所指示的已知对象创建一个代理。 以第一个2个参数的方法为例子:第一个参数Type,是在服务端注册为已知类型的类型,第二个参数是已知对象的URL 服务端: HttpChannel _channel = new HttpChannel(10001); ChannelServices.RegisterChannel(_channel,false); Console.WriteLine("http 通道remoting服务开始……"); RemotingConfiguration.RegisterWellKnownServiceType( typeof(selfRemoteObject), "selfRemoteObject", WellKnownObjectMode.Singleton); 客户端: public void TestService() { selfRemoteObject app = (selfRemoteObject)Activator.GetObject( typeof(selfRemoteObject), "http://localhost:10001/selfRemoteObject"); Assert.AreEqual(13,app.Plus(3, 10)); } (2)通过new来创建代理 当用new来激活对象时,先要使用RegisterWellKnownClientType方法在客户端注册该已知对象类型。然后使new关键字可以创建它。 服务端还是(1)中的服务端 客户端: public void TestServiceNew() { RemotingConfiguration.RegisterWellKnownClientType( typeof(selfRemoteObject), "http://localhost:10001/selfRemoteObject"); selfRemoteObject app = new selfRemoteObject(); Assert.AreEqual(13, app.Plus(3, 10)); } 以上两种方法在创建代理后,调用方法时,服务端会管理已知类型对象的创建工作,这种生成对象是按默认的构造器(无参)来进行。 客户端激活 客户端激活的对象由调用应用程序域控制其生存期的对象,这种情况与应用程序域控制本地对象生存期一样。 创建客户端激活对象时,客户端将调用服务器。服务器实例化远程对象,并将对象引用返回给客户端。客户端使用这个引用创建远程对象的代理。每当客户端创建客户端激活对象的实例时,都会收到与远程对象的特定服务器实例进行通信的代理,直至其租约过期,内存被回收为止。 采用客户端激活方式的步骤: ·要在服务器上创建客户端激活的对象的实例,要知道它的Type ·使用RegisterActivatedServiceType方法在服务端注册它。 客户端步骤: ·客户端先向ChannelServices注册一个通道, ·通过调用new或Activator.CreateInstance激活对象。 创建客户端激活类型实例的两种方式: ·以new来进行 ·以Activator.CreateInstance的调用中传递远程对象 先准备服务端: HttpChannel _channel = new HttpChannel(10001); ChannelServices.RegisterChannel(_channel,false); Console.WriteLine("http 通道remoting服务开始……"); RemotingConfiguration.ApplicationName = "selfRemoteObject"; RemotingConfiguration.RegisterActivatedServiceType (typeof(selfRemoteObject)); Console.Read(); 服务端要通过RegisterActivatedServiceType方法来注册类型,且先要设定ApplicationName,这个就相当于URL 客户端 (1)以new来进行 public void TestClientNew() { HttpChannel _channel = new HttpChannel(); ChannelServices.RegisterChannel(_channel, false); RemotingConfiguration.RegisterActivatedClientType ( typeof(selfRemoteObject), "http://localhost:10001/selfRemoteObject"); selfRemoteObject app1 = new selfRemoteObject(); Assert.AreEqual(10, app1.Plus(1, 9)); } 通过new来激活客户端对象类型,先使用RegisterActivatedClientType方法在客户端注册该对象类型,然后new就可以了。 (2)Activator.CreateInstance 如果用CreateInstance,则要提供远程应用程序的URL作为参数,不用在客户端注册类型。如果要用到URL做参数,则URL要封装在UrlAttribute类实例中。 public void TestClientInstance() { HttpChannel _channel = new HttpChannel(); ChannelServices.RegisterChannel(_channel,false); object[] url={new UrlAttribute ("http://localhost:10001/selfRemoteObject")}; selfRemoteObject app1 = (selfRemoteObject)Activator .CreateInstance(typeof(selfRemoteObject), null, url); Assert.AreEqual(10, app1.Plus(1, 9)); } 客户端激活方式可以使用带参构造器来创建远程对象实例。 修改远程对象,添加一属性一个带参构造器: public int Unid { get; set; } public selfRemoteObject() { } public selfRemoteObject(int iUnid) { Unid = iUnid; } 添加了带参构造器,要显示声明默认的无参构造器。 通过new方法来创建对象实例。 selfRemoteObject app1 = new selfRemoteObject(); Assert.AreEqual(0, app1.Unid); selfRemoteObject app2 = new selfRemoteObject(10); Assert.AreEqual(10, app2.Unid); 激活方式,是远程对象实例创建的的管理方式。服务器端激活就是由服务端来管理远程对象的创建,在这种方式下,当客户端调用方法时,才创建实例;客户端激活由客户端来管理远程对象的创建,在这种方式下,当客户端发出调用就创建实例。 RemotingConfiguration类的类型注册方法: RegisterActivatedClientType 将客户端上的对象 Type 注册为可在服务器上激活的类型。 RegisterActivatedServiceType 将服务端上的对象 Type 注册为可根据请求从客户端激活的类型。 RegisterWellKnownClientType 将客户端上的对象 Type 注册为已知类型(“单个调用”(singlecall) 或 singleton)。 RegisterWellKnownServiceType 将服务端上的对象 Type 注册为已知类型(“单个调用”(singlecall) 或 singleton)。 断开 RemotingServices.Disconnect这个方法用于阻止对象通过注册的远程处理信道再接收消息。它的参数须是实例化的对象(要关闭的),服务端注册类型要通过Marshal方法来进行: HttpChannel _channel = new HttpChannel(10001); ChannelServices.RegisterChannel(_channel,false); Console.WriteLine("http 通道remoting服务开始……"); selfRemoteObject obj=new selfRemoteObject(); RemotingServices.Marshal(obj, "selfRemoteObject"); 断开时,通过Disconnect方法: RemotingServices.Disconnect(obj); 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
remoting提供一种允许对象通过应用程序域去与另一对象进行交互的框架。如果要通过应用程序域进行通信,在remoting中要通过信道(channel)来实现。 信道 信道是跨越远程处理边界(应用程序域,进程,计算间之间)在应用程序之间传递消息的对象。信道可以在终结点监听入站消息,向基他终结点发送出站消息,或进行这两种操作。 信道须实现IChannel接口,这个接口提供了信息性属性,如:ChannelName,ChannelPriority。IChannelReceiver接口,用于监听特定端口上的特定协议的信道实现。IChannelSender接口用于发送消息的信道实现。TcpChannel和HttpChannel对象都实现了这两种接口,可以发送或接收消息。 .net 提供了几种信道实现: (1)IpcChannel 通过使用命名管道为同一台计算机上的多进程应用程序提供高速进程间通信。它执行的功能: ·使用命名管道进行发送方和接收方的通信 ·支持以二进制格式和行业标准soap序列化格式编码 ·生成并使用对象引用的ChannelDataStore ·支持模拟和委托 ·支持在命名管道上利用访问控制列表(ACL)来提供高级访问控制 在应用程序与本机其它进程中的另一应用程序进行通信时,优先使用IpcChannel。 (2)TcpChannel 二进制格式化程序装所有消息序列化为二进制流,并使用Tcp协议将流传输至URI,它执行以下功能: ·使用tcp socket进行发送方与接收方的通信 ·支持以二进制格式和行业标准soap序列化格式编码负载 ·生成并使用对象引用的ChannelDataStore ·支持模拟和委托 ·支持SSPI加密 当HttpChannel连接到用TcpChannel进行监听的服务器应用程序域时,客户端将收到:基础连接已经关闭:接收时发生错误,这样的异常。存在信道不匹配的问题。 (3)HttpChannel 使用Soap协议在远程对象之间传输消息。所有的消息都通过SoapFormatter传递,这个格式化器会将消息转换为xml,然后进行序列化,同时向数据流中添加soap头。如果指定二进制格式化器,则会创建二进制数据流。最后通过http协议将数据流传输到URI。HttpChannel符合soap1.1标准,它执行以下功能: ·通过http协议在发送方和接收方之间的通信 ·支持以soap和二进制格式编码负载 ·将接收方设置为通过.net和tcp套接字接收http请求并发送http响应 ·支持模拟和委托 ·支持SSPI加密 HttpChannel一次只向给定服务器打开指定数目的连接。默认为2,可通过clientConnectionLimit属性进行设置。 使用.net remoting处理的应用程序来跨应用程序域边界进行通信,需要一个可远程处理的类的实现,一个监听或宿主应用程序域,一个客户端或调用应用程序域,同时段将每个应用程序域中的远程处理系统都配置为对可远程处理的类型使用远程激活 远程对象 为了让其它应用程序域中的对象能访问类的实例,那么这个类要从MarshalByRefObject派生。 public class RemotableType : MarshalByRefObject {} 这个类支持远程处理的应用程序中跨应用程序域边界访问对象。 同一应用程序域中的对象直接通信。不同应用程序域中的对象的通信方式有两种: ·跨应用程序域边界传输对象副本 ·通过代理交换消息 MarshalByRefObject是通过使用代理交换消息来跨应用程序域边界进行通信的对象的基类。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
.net remoting用于应用程序之间通信。 先做一简单的示例: 准备三个项目。 (一)远程对象 新建立类库项目:SelfRemote public class selfRemoteObject : MarshalByRefObject { public int Plus(int a, int b) { Console.WriteLine("客户端请求调用:a={0},b={1}",a,b); Console.WriteLine("计算结果:a+b={0},返回给客户端调用", a+ b); return a + b; } } 从MarshalByRefObject派生,然后完成可远程调用的服务(方法)。然后生成库文件,并把库文件做为服务端与客户端两端的引用库。 (二)服务端 建立控制台应用程序,用于注册通道。(添加对远程对象生成库的引用) static void Main(string[] args) { HttpChannel _channel = new HttpChannel(10001); ChannelServices.RegisterChannel(_channel,false); Console.WriteLine("http 通道remoting服务开始……"); RemotingConfiguration.RegisterWellKnownServiceType (typeof(selfRemoteObject), "selfRemoteObject", WellKnownObjectMode.Singleton); Console.Read(); } 建立Http通道 (三)客户端 添加对远程对象生成库的引用 public void Test() { selfRemoteObject app = (selfRemoteObject)Activator.GetObject(typeof(selfRemoteObject), "http://localhost:10001/selfRemoteObject"); Console.WriteLine(app.Plus(1,3)); Console.ReadLine(); } (四)测试 控制台: 客户端请求调用:a=1,b=3 计算结果:a+b=3,返回给客户端调用 客户端: 4 以一个简单的示例来演示remoting的实现过程。 ·在这个例子中,做为远程remoting对象的SelfRemote类库生成库文件,要在两端都引用。 ·对于两端的配置可以在以程序的方式进行,也可以以配置文件的方式进行。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
可以将MessageQueue对象设置为生成确认消息,能通知消息发送者消息是否已经成功传递。 有两种主要的确认类型: ·消息到达目标队列的确认 ·目标应用程序从队列中检索到消息的确认 确认是通过向队列发送新消息来处理的。这种情况下,确认消息从目标队列发送到一个特殊类型的队列中:管理队列。确认消息不同于标准消息,因为它们不包含正文;在确认中,消息头中的信息最重要。 这里以一个示例来演示一下: (一)新建管理队列 private MessageQueue CreateAdminQueue() { string trPath = @".\Private$\selfAdminQueue"; MessageQueue _queue; if (!MessageQueue.Exists(trPath)) return MessageQueue.Create(trPath); _queue = new MessageQueue(trPath); return _queue; } (二)传递消息 public void SendConfirmMessage(string strMsg) { MessageQueue _queue = CreateQueue(); Message _message = new Message(strMsg); _message.AdministrationQueue = CreateAdminQueue(); _message.AcknowledgeType = AcknowledgeTypes.PositiveReceive | AcknowledgeTypes.PositiveArrival; _queue.Send(_message); } ·设置AdminstrationQueue的属性。这个属性是用来设置和获取接收消息队列的确认消息的队列。 ·设置确认消息属性AcknowledgeType。这个属性是用来设置和获取返回给发送方消息确认的类型,它是个枚举类型。枚举值: PositiveArrival 一个掩码,用于在原始消息到达队列时请求肯定确认。 PositiveReceive 一个掩码,用于在成功从队列检索到原始消息时请求肯定确认。 NegativeReceive 一个掩码,用于当未能从队列接收原始消息时请求否定确认。 None 一个掩码,用于请求不发送任何确认消息(无论是肯定的还是否定的)。 NotAcknowledgeReachQueue 一个掩码,用于在原始消息不能到达队列时请求否定确认。当到达队列时间计时器过期时或不能对消息进行身份验证时,可能请求否定确认。 NotAcknowledgeReceive 一个掩码,用于当发生错误时请求否定确认,防止在其接收时间计时器过期前从队列接收原始消息。 FullReachQueue 一个掩码,用于在原始消息到达队列时请求肯定确认,或者用于到达队列时间计时器过期后请求否定确认,或者用于不能对原始消息进行身份验证时请求否定确认。 FullReceive 一个掩码,用于在接收时间计时器过期前从队列收到原始消息时请求肯定确认,否则请求否定确认。 (三)接收方 public string GetNormalMessage() { MessageQueue _queue = CreateQueue(); _queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); Message _message = _queue.Receive(); return _message.Id; } 接收消息正常接收。然后得到消息的Id,通过Id来获取在管理队列中的管理消息的信息 (四)管理队列消息 public void GetAcknowledgmentMessage(string strMsgId) { MessageQueue _queue = CreateAdminQueue(); _queue.MessageReadPropertyFilter.CorrelationId = true; _queue.MessageReadPropertyFilter.Acknowledgment = true; try { while (_queue.PeekByCorrelationId(strMsgId) != null) { Message myAcknowledgmentMessage = _queue.ReceiveByCorrelationId(strMsgId); Console.WriteLine("Correlation Id: " + myAcknowledgmentMessage.CorrelationId.ToString()); Console.WriteLine("Id: " + myAcknowledgmentMessage.Id.ToString()); Console.WriteLine("Acknowledgment Type: " + myAcknowledgmentMessage.Acknowledgment.ToString()); } } catch { } } 通过消息标识来检索管理队列,检索成功打印信息。确认类型Acknowledgment用于指定尝试的消息传递的结果。它是个枚举: None 该消息不是确认消息。 AccessDenied 一个否定到达确认,它指示发送应用程序不具有将消息发送到目标队列所需的权限。 BadDestinationQueue 一个否定到达确认,它指示目标队列不可用于发送应用程序。 BadEncryption 一个否定到达确认,它指示目标队列管理器未能解密私有消息。 BadSignature 一个否定到达确认,它指示原始消息的数字签名无效并且未能由消息队列进行身份验证。 CouldNotEncrypt 一个否定到达确认,它指示源队列管理器未能加密私有消息。 HopCountExceeded 一个否定到达确认,它指示已超出了原始消息的跳数(跳数指示中间服务器的数目)。 NotTransactionalQueue 一个否定到达确认,它指示已将事务性消息发送到非事务性队列。 NotTransactionalMessage 一个否定到达确认,它指示非事务性消息被发送到了事务性队列。 Purged 一个否定到达确认,它指示消息在到达其目标队列前已被清除。 QueueDeleted 一个否定读取确认,它指示在可以读取消息前队列已被删除。 QueueExceedMaximumSize 一个否定到达确认,它指示原始消息因其目标队列已满而未被传送。 QueuePurged 一个否定读取确认,它指示在可以读取消息前队列已被清除。 ReachQueue 一个肯定到达确认,它指示原始消息已到达其目标队列。 ReachQueueTimeout 一个否定到达确认,它指示在原始消息可到达目标队列前到达队列时间计时器或接收时间计时器已过期。 ReceiveTimeout 一个否定读取确认,它指示在其接收时间计时器过期前没有从队列接收原始消息。 Receive 一个肯定读取确认,它指示原始消息已由接收应用程序接收 (五)流程 ·当向消息队列传递消息时,同时向管理队列传递了消息(当消息队列收到消息后,管理队列中也会收到消息)。此时的消息是:消息已经到达消息队列。(在管理队列中的消息是不带包体正文的,所以说头最重要。) ·当从消息队列中接收到消息后(此时消息的标识已经读出),消息队列消息就会清空(Revceive的)。然后在管理队列中就会再多一条管理消息,来标示:消息已经被接收。 ·当处理管理队列消息时,会按消息标识来处理管理队列消息。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
复杂消息的消息队列实现及空间下的三种序列化器介绍 先做一个例子 1定义Customer类 public class Customer { public int Unid { get; set; } public string CustomerName { get; set; } public DateTime CreateTime { get; set; } } 3个属性 2向队列发送 public void SendMessage(Customer customer) { MessageQueue _queue = CreateQueue(); Message _message = new Message(customer); _message.Formatter = new XmlMessageFormatter(new Type[] {typeof(Customer) }); _queue.Send(_message); } 这里因为用到复杂消息传递不是简单的基元类型所以要用到序列化。序列化为了传输和存储进行的转换。.net提供了一些序列化工具同时还能利用其它的第三方的工具来序列化和反序列化复杂对象。 对于消息队列的序列化系统提供了三种工具 ·XmlMessageFormatter 对象使用可读的 XML 字符串将对象和基元数据类型保持到消息中和从消息中取消保持。这是 MessageQueue 组件的默认格式化程序设置。 ·BinaryMessageFormatter 对象将一个或多个连接的对象保持到序列化流中。其结果分析起来非常简洁快速但人们无法阅读。 ·ActiveXMessageFormatter 对象保持基元数据类型从而实现与使用“消息队列”早期版本的组件的交互。产生的序列化非常简洁。此格式化程序在设计时考虑了 Windows并且不会产生人们可以阅读的结果。但它却是一种极为快捷的序列化方法。 这个后边说。 3从队列中得到消息并解析 有了序列化还要有反序列化从传输和存储中得到想要的对象。例如Customer对象 public Customer GetMessage() { MessageQueue _queue = CreateQueue(); _queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(Customer) }); Message _message = new Message(); _message = _queue.Peek(); return _message.Body as Customer; } 对于XmlMessageFormatter的用法构造器请见相关帮助文档。这里为MessageQueue对象的Formatter属性指定序列化器。 这里从队列中读取消息我用的是Peek方法它在读取后不改动队列。这样便于测试。 4测试 发送 Customer _customer = new Customer { Unid = 2, CustomerName = "Song江", CreateTime = Convert.ToDateTime("2010-2-2") }; mm.SendMessage(_customer); 接收并打印信息 Console.WriteLine(mm.GetMessage().CustomerName); 这里给出在XmlMessageFormatter序列化器的使用下的MSMQ包的内容在默认情况下也就是创建消息队列对象MessageQueue对象下如果不指定序列化器则在创建MessageQueue时同时创建XmlMessageFormatter并将两者关联 <?xml version="1.0" ?> <Customer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Unid>2</Unid> <CustomerName>Son gæ±</CustomerName> <CreateTime>2 010-02-02 T00:00:00</CreateTime> </Customer> 因为之中用到了中文文字编码的原因这里看来是乱码。 5三种序列化器 1》XmlMessageFormatter 默认的序列化器在创建MessageQueue对象时同时创建并关联到消息队列对象。这里这个简称为xml序列化器。 xml序列化器可用于写入队列但如果要从队列中读取先要对设置TargetTypes或TargetTypeNames属性可以同时设置这两个属性。也可以创建xml序列化器的实例然后通过值参方式创建xml序列化器。 XmlMessageFormatter.TargetTypes 指定可能的类型集这些类型将由格式化程序从提供的消息进行反序列化。 定义 public Type[] TargetTypes { get; set; } XmlMessageFormatter.TargetTypeNames 指定可能的类型集这些类型将由格式化程序从提供的消息进行反序列化。 定义 public string[] TargetTypeNames { get; set; } 在默认情况下xml序列化器被创建并使用可以用来传递消息。但如果要从队列中读取消息需要在读取之前显示对序列化器进行设置 ·设置xml序列化器的TargetTypes属性或TargetTypeNames属性。 MessageQueue _queue = CreateQueue(); _queue.Formatter = new XmlMessageFormatter(new Type[] { typeof(Customer) }); ·通过值参数来创建xml序列化器。 MessageQueue _queue = CreateQueue(); XmlMessageFormatter _formatter = new XmlMessageFormatter(); _formatter.TargetTypes = new Type[] { typeof(Customer)}; _queue.Formatter = _formatter; 2》BinaryMessageFormatter 使用二进制将对象进行序列化和反序列化。这个简称二进制序列化器。 二进制序列化器非常有效且可用于大多数对象的序列化。序列化后的结果紧凑且可以快速分析但不允许进行松耦合消息处理。松耦合意味着客户端和服务端可以独立控制发送和接收的类型的版本。这个二进制速度快。二进制我最爱 这里给出一段经过二进制序列化器序列化的消息的包内容 .....ÿÿÿÿ......... ....?Self MSMQ, Ver sion=1.0. 0.0, Culture=neutral, PublicKeyToken=null.... ..SelfMSMQ.Customer..... <Unid>k__Bac kingField . <CustomerName>k__ BackingField. <CreateTime>k__BackingField...................Song æ± ...̱qÌ.. 在使用时将上例中的xml序列化器换成二进制序列化器就可以了。二进制速度快吞吐量大。 3》ActiveXMessageFormatter 使用与 MSMQ ActiveX 组件兼容的格式将基元数据类型和其他对象序列化成“消息队列”消息体或从“消息队列”消息体反序列化基元数据类型和其他对象。与使用消息队列 COM 组件发送的消息兼容并允许与使用消息队列 COM 控件的应用程序进行互操作。 它的序列化与反序列化有要求可以序列化大多数基元类型以及实现 IPersistStream OLE 接口的对象。它可以反序列化相同的基元类型集但在反序列化实现 IPersistStream 的 COM 对象例如使用 Visual Basic 6.0 创建的对象时需要进行更多的工作。要反序列化的对象必须处于内存中通过首先将对象导入到 .NET Framework 应用程序中。 现在通过这个序列化器进行对字符串的序列化与反序列化。 这里写一个小例子 发送 public void SendMessage(string strMsg) { MessageQueue _queue = CreateQueue(); Message _message = new Message(strMsg); _message.Formatter = new ActiveXMessageFormatter(); _queue.Send(_message); } 接收 public string ReceiveMessage() { MessageQueue _queue = CreateQueue(); Message _message = _queue.Peek(); _message.Formatter = new ActiveXMessageFormatter(); return _message.Body.ToString(); } 测试 mm.SendMessage("这里ActiveX序列化器"); …… Console.WriteLine(mm.ReceiveMessage()); …… 打印这里ActiveX序列化器 对于这个包内容这里不给出了。 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明博客园
System.Messaging 名字空间下包含了用于连接到、监视和管理网络上的消息队列并发送、接收或查看消息的类。 其中一个主要的类是MessageQueue 它提供对消息队列上的消息进行访问。 它有很多成员。例如 ·Send方法用于向队列中写入消息。 ·Receive,ReceiveById,ReceiveByCorrelationId方法用于从队列中读取消息。 ·Peek与Receive相似只是不更改队列内容就是在赢取消息时不会从队列中移除消息 ·BeginPeek异步读取消息 还提供了其它功能 ·创建和删除消息队列的队列 ·通过枚举逐个访问队列中的消息 ·通过枚举循环访问系统队列 ·设置基于ACL访问权限 ·使用连接缓存 另一个类是Message Message类可以对消息进行详尽的控制且是队列接收或查看消息时使用的对象。除了消息正文外这个类的属性还包括确认设置格式化程序选择标识身份验证加密信息时间戳服务器日记不负死信队列的指示以及事务。 简单类型的发送与接收 1创建队列 private MessageQueue CreateQueue() { string trPath=@".\Private$\SelfPrivateDemo"; MessageQueue _queue; if (!MessageQueue.Exists(trPath)) return MessageQueue.Create(trPath); _queue = new MessageQueue(trPath); return _queue; } 这里创建队列对象然后可以通过队列进行发送和接收等操作。 2发送字符串 public void SendMessage(string strMsg) { MessageQueue _queue = CreateQueue(); Message _message = new Message(strMsg); _queue.Send(_message); } 3接收字符串 public string ReceiveMessage() { MessageQueue _queue = CreateQueue(); Message _message=_queue.Receive(); _message.Formatter = new XmlMessageFormatter(new Type[] { typeof(string) }); return _message.Body.ToString(); } 这里解析队列消息时要用到格式化器 4测试 mm.SendMessage("Hello,江"); 这里在消息队列管理控制台中可以看到添加了队列消息可以查看一下它的内容 <?xml ver sion="1.0"?> <string>Hello ,æ±</string> 大概是这段xml。因为传递消息用到了中文字符这里又采用了文字编码所以是乱码状态。 在接收时string strMsg = mm.ReceiveMessage(); 会得到消息队列中的消息并按格式化器解析为正确的类型。 消息Message对象可以设置正文Body也可以设置和获取很多属性。 Console.WriteLine("消息标识{0}",_message.Id); Console.WriteLine("消息类型{0}", _message.MessageType.ToString()); 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明博客园
MSMQ,是Microsoft Message Queue的简写,就是微软消息队列,用于应用程序之间相互通信的一种异步传输模式。应用程序可以分布在同台机器上,也可以分布于互联的网络中的任意位置。 实现的基本原理:消息发送者把要发送的消息放入容器,也就是Message(消息),然后保存到系统公用空间的消息队列中(Message Queue)中,然后,本地或互联位置上的消息接收程序再从队列中取出发给它的消息进行处理。 队列的类型 有两种主要类别的队列:由本机或网络中的其他用户创建的队列和系统队列。 用户创建的队列可能是以下任何一种队列: “公共队列”在整个“消息队列”网络中复制,并且有可能由网络连接的所有站点访问。 “专用队列”不在整个网络中发布。相反,它们仅在所驻留的本地计算机上可用。专用队列只能由知道队列的完整路径名或标签的应用程序访问。 “管理队列”包含确认在给定“消息队列”网络中发送的消息回执的消息。指定希望 MessageQueue 组件使用的管理队列(如果有的话)。 “响应队列”包含目标应用程序接收到消息时返回给发送应用程序的响应消息。指定希望 MessageQueue 组件使用的响应队列(如果有的话)。 系统生成的队列一般分为以下几类: “日记队列”可选地存储发送消息的副本和从队列中移除的消息副本。每个“消息队列”客户端上的单个日记队列存储从该计算机发送的消息副本。在服务器上为每个队列创建了一个单独的日记队列。此日记跟踪从该队列中移除的消息。 “死信队列”存储无法传递或已过期的消息的副本。如果过期消息或无法传递的消息是事务性消息,则被存储在一种特殊的名为“事务性死信队列”的死信队列中。死信存储在过期消息所在的计算机上。有关超时期限和过期消息的更多信息,请参见默认消息属性。 “报告队列”包含指示消息到达目标所经过的路由的消息,还可以包含测试消息。每台计算机上只能有一个报告队列。 “专用系统队列”是一系列存储系统执行消息处理操作所需的管理和通知消息的专用队列。 在应用程序中进行的大多数工作都涉及访问公共队列及其消息。但是,根据应用程序的日记记录、确认和其他特殊处理需要,在日常操作中很可能要使用几种不同的系统队列。 队列类型的路径格式: 队列类型 语法 公共队列 MachineName\QueueName 专用队列 MachineName\Private$\QueueName 日记队列 MachineName\QueueName\Journal$ 计算机日记队列 MachineName\Journal$ 计算机死信队列 MachineName\Deadletter$ 计算机事务性死信队列 MachineName\XactDeadletter$ 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
最近对.net的辅助工具比较感兴趣,网上也已经有.net的十个必备工具的帖子了,这里提供了一份全面的工具列表:Code generation NVelocity CodeSmith X-Code .NET XGoF - NMatrix / DEVerest Compilation eXtensible C# - ResolveCorp Mono DotGNU - GNU Obfuscation LSW-IL-Obfuscator - Lesser Software Demeanor for .NET - Wise Owl Salamander .NET Obfuscator - Remotesoft Salamander .NET Protector - Remotesoft IL-Obfuscator - 9rays.Net Deploy.NET - Jungle Creatures Dotfuscator - PreEmptive Solutions (lite version included in Visual Studio 2003) XenoCode - Oak Vale Networks Thinstall - Jonathan Clark Decompilation Salamander .NET Decompiler - Remotesoft Anakrino Object Browsing .NET Reflector - Lutz Roeder LSW-DotNet-Reflection-Browser - Lesser Software .NET Component Inspector - nogoop software Object State Browser - Jeff Key Refactoring C# Refactoring Tool - .NET Refactoring C# Refactory - Xtreme Simplicity Persistence and data-related code generation See also "RAD Tools - Application Development Automation" below OlyMars (SQL Server Centric .NET Code Generator) LLBLGen - Solutions Design .NET N-Tier Framework Generator - Gavin Joyce Entity Broker – Thona Consulting KickStarter.Net TierDeveloper - AlachiSoft SP/Invoke Data Tier Modeler (DTM) Pragmatier Data Tier Builder ORM.NET - Olero Software .Net Persistence - Netica ObjectSpaces - Microsoft Sisyphus Persistence Framework Objectz.NET – Mongoose solutions Visible Developer – Visible Systems NHibernate ObjectSpark – Firestar Software Bamboo.Prevalence ADO.NET Powertoys - The ADO Guy Database to .NET - Nantronix Software Xheo.Enterprise - Xheo RAD Tools - Application Development Automation DeKlarit - ARTech DataPhor - Alphora CompileX - Atlantis DB Software Model Driven Architecture Constructor() - Dot Net Builders Modeling - UML Rational XDE - IBM Visio - Microsoft (part of Visual Studio .NET Enterprise Architect edition) Visual Case - Artiso IDEs Visual Studio .NET - Microsoft SharpDevelop PrimalCode - Sapien ASP.NET Web Matrix - Microsoft Improve C# Plugin for Eclipse - Improve Builds NAnt SLiNgshoT Draco.NET CruiseControl.NET FinalBuilder – Atozed Software BuildIt - Microsoft Visual Build Professional - Kinook Software Testing NUnit NUnitAddin NUnitAsp csUnit .NET Mock X-Unity – MIIK Ltd. HarnessIt – United Binary SilkPerformer .NET Explorer - Segue Test.NET - Parasoft Code validation - Standard verifiers FxCop - Microsoft DotEASY Profiling - Monitoring - Performance Testing - Optimization nprof Application Center Test - Microsoft (part of Visual Studio .NET Enterprise editions) DevPartner Profiler - Compuware Corporation DevPartner Studio - Compuware Corporation AQtime .NET - AutomatedQA Rational Quantify for Windows - IBM .NET Memory Profiler - SciTech Software AB Allocation Profiler (source) - Microsoft ANTS Profiler - Red Gate Software ANTS Load - Red Gate Software ASPNETWatchDog - ASPNETWatchDog Optimizeit Profiler - Borland VTune Performance Analyzer - Intel Documentation - Code commenting NDoc Documentor for .NET - Lutz Roeder Visual Studio .NET Help Integration Kit - Microsoft VB.DOC VB.NET XML Comments Creator - Fesersoft VB.NET XML Commentor - Tor-Erik Hagen TeeGofer - Steema Software CodeReview - Macadamian Technologies Frameworks Mere Mortals .NET Framework – Oak Leaf Enterprises Solution Design Odyseus - Radical Technologies MVC Maverick.NET AOP AspectC# Weave.NET Code versioning - Source control Visual SourceSafe - Microsoft (part of Visual Studio .NET Enterprise editions) SourceGear Vault - SourceGear SourceOffsite - SourceGear SourceOffsite Collaborative Edition - SourceGear Jalindi Igloo Ankh AccuRev Localization Enterprise Localization Toolkit - Microsoft Reporting ActiveReports - Data Dynamics Crystal Reports - Crystal Decisions Report Sharp-Shooter - 9rays.Net Page templating Page Template Generator - Paul Wilson Xheo.WebTemplate - Xheo Xheo.WebSkin - Xheo Scripting Alintex Script Host - Alintex DOTNET Scripting Host - Holger Schwichtenberg Toolsack DotNet Script Host - Toolsack Software Installation InstallShield - InstallShield Wise for Visual Studio .NET - Wise Solutions Others Page ViewState Parser - Paul Wilson FormBuilder.NET - Dan Wahlin RegexDesigner.NET - Chris Sells QuickCode.NET - Development Expertise Cassini Web Server - Microsoft Web.Config Editor - HunterStone Xheo.Licensing - Xheo 原文地址:http://www.cnblogs.com/dahuzizyd/archive/2004/07/06/21646.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
web性能测试分析-工具篇 用于Web性能分析的工具还有很多,以下只不过是我所略知的几种,如各位有使用未列出性能工具,请跟帖共享。以下软件都可容易下载到.我建议全部下载,根据自已的需求,选择最适合自己的一款,呵呵,这个工作比较累呀。 微软官方免费软件: CLRProfiler 是一个可以用于分析.NET程序行为的工具。可用其分析垃圾回收器堆正在发生的事情,例如什么方法分配了什么类型的对象?另外,还提供了调用图(call graph)功能用于显示哪个方法调用了哪个方法 Microsoft Web Application Stress Tool Microsoft Web Application Stress Tool 是由微软的网站测试人员所开发,专门用来进行实际网站压力测试的一套工具。透过这套功能强大的压力测试工具,您可以使用少量的Client端计算机仿真大量用户上线对网站服务所可能造成的影响,在网站实际上线之前先对您所设计的网站进行如同真实环境下的测试,以找出系统潜在的问题,对系统进行进一步的调整、设置工作。 Microsoft Application Center Test Microsoft Application Center Test是vs.net2003企业版自带的一个测试工具.使用它可以收集性能信息.确定WEB应用程序的容量.也可以创建测试,模拟同时从WEB应用程序请求网页的多个用户.这些模拟测试有助于确定应用程序的稳定性.速度和响应能力. 商业软件(其实试用30天已经足够用了): dotTrace 是个很不错的分析工具(Profiling Tool),可以分析windows form和asp.net ,它能够快速分析、过滤、函数查找(快速定位function,并且导航)和查看源码. 试用版下载(http://www.red-gate.com/products/ants_profiler/index.htm) ANTS Profiler 一个检测基于.Net Framework的任何语言开发出的应用程序的代码性能的工具。她可以通过记录每行代码花费的时间,来帮助你优化程序的执行,而且可以用很多种格式来探测应用程序中效率不好的区域。ANTS Profiler还可以分析应用程序的内存使用状况,她就是通过记录每个对象分配的内存空间大小,来提供很多种报告样式给开着这察看哪个对象或者类占用了最多的内存,从而可以帮助你优化内存的使用。 试用版下载(http://www.red-gate.com/products/ants_profiler/index.htm) AQTime 一款功能强大的Code Profiler工具,有很多种代码分析方式,一般主要是用它来做性能分析,目的是定位程序性能瓶颈,然后再有针对性地进行优化。函数追踪功能,能看清函数的调用,而且能定位到行级,确定系统性能的瓶颈。 试用版下载(http://www.automatedqa.com) .NET Memory Profiler 是NET Memory Profiler是一款强大的.net 内存跟踪和优化工具。 DevPartner Studio Professional Edition 是一套功能非常强大全面性的软件开发除错工具,协助程序开发人员使用微软Visual Studio .NET与Visual Studio 6.0开发应用程序与WebService。 其功能包括扫描程序找出程序码潜在的问题,侦测执行阶段的错误,程序执行效能分析,分散式应用系统问题分析,与程序码测试涵盖度分析等。支持微软的Visual Studio .NET、Native Windows或是两者混合使用的应用程序,加速应用程序的开发,提高应用系统的稳定性与执行效能。 LoadRunner 是否目前国内最多人用的压力测试工具,感觉与.net搭配不是很好。 原文地址:http://www.cnblogs.com/mxy1028/archive/2008/10/06/1305046.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
绑定这块引出了很多细节。绑定解决了消息交换中的传输协议,传输,编码等问题。如果要公开WCF服务,就要公开终结点Endpoint,WCF服务信息交换就是Endpoint之间的信息交换。终结点三大元素:ABC。A解决寻址问题,C解决协定(契约)问题,这里可以设置服务的功能集,这是我们真正用到的地方,B绑定是重中之重,解决了其中最重要的一系列问题。 (一)系统提供了全面的绑定实现 系统提供了相当全面的绑定: ·BasicHttpBinding 适用于符合WS-basic profile的web服务通信。例如基于.net的WS。传输协议Http,消息编码:文本/xml。在配置文件里这个节点的名称就是第一个单词全小写,后边单词首字母大写。basicHttpBinding。其它的系统提供的绑定在配置文件中一般也按这个规则走。 ·WSHttpBinding 非双工的。 ·WSDualHttpBinding 双工的 ·NetTcpBinding 跨计算机通信 ·NetNamedPipeBinding 计算机内通信 ·NetMsmqBinding 消息队列 其它的省略。这里有几个详细的表(这几个表太好): ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.chs /wcf_con/html/2c243746-45ce-4588-995e-c17126a579a6.htm (二)绑定基类 绑定在WCF实现上就是上边这些绑定,对应着各自的类。它们都由Binding类派生: public class BasicHttpBinding : Binding, IBindingRuntimePreferences Binding类 public abstract class Binding : IDefaultCommunicationTimeouts Binding类实现了IdefaultCommunicationTimeouts接口,这个接口用于定义通信的超时属性。它有4个属性: ·CloseTimeout 获取 close 方法(由通信对象调用)在超时之前的时间间隔。 ·OpenTimeout 获取 open 方法(由通信对象调用)在超时之前的时间间隔。 ·ReceiveTimeout 获取 receive 方法(由通信对象调用)在超时之前的时间间隔。 ·SendTimeout 获取 send 方法(由通信对象调用)在超时之前的时间间隔。 (三)绑定是分层的 一个绑定对象对应着一组有序的集合,一组绑定元素对象的集合,而这些元素按层来分别处理各自的事务。因为WCF服务是基于消息交换,而这种交换是在终结点之间进行的,所以每个元素负责终结点通信的一个方面。例如:编码,传输,安全等等。 它的层: 层 选项 是否必需 事务流 TransactionFlowBindingElement 否 可靠性 ReliableSessionBindingElement 否 安全性 对称、非对称、传输级 否 形状更改 CompositeDuplexBindingElement 否 传输升级 SSL 流、Windows 流、对等解析程序 否 编码 文本、二进制、MTOM、自定义 是 传输 TCP、命名管道、HTTP、HTTPS、MSMQ、自定义 是 其中编码层和传输层是必要条件。 这一层一层的绑定元素(Binding Element)就是BindingElement (四)绑定元素 绑定元素在WCF实现上就是BindingElement 它做为其它绑定元素的基类。绑定元素用于为各种类型的信道生成信道工厂和信道监听器以处理传出和传入消息的绑定元素。(有的叫通道,也有叫信道的,指得是一个意思,但理解上有一些不同。) 由它派生的绑定元素实现: OneWayBindingElement TransportBindingElement CompositeDuplexBindingElement StreamUpgradeBindingElement PrivacyNoticeBindingElement UseManagedPresentationBindingElement ReliableSessionBindingElement SecurityBindingElement MessageEncodingBindingElement PeerResolverBindingElement TransactionFlowBindingElement ContextBindingElement 从字面上可以大概能够看出每个元素的主要义务。 (五)绑定的绑定元素 这里看下几种系统绑定是由哪几个绑定元素组成的。 在绑定基类中Binding有个CreateBindingElements方法, public abstract BindingElementCollection CreateBindingElements() 它的功能就是:创建一个集合,该集合包含属于当前绑定的绑定元素,而由绑定基类实现的各个系统绑定都实现了这个方法。然后绑定元素(BindingElement)有个GetType方法,用于获取绑定元素实例(Type),然后就可以通过类型属性来得到详细信息了。 (1)现在看看BasicHttpBinding绑定包含哪些绑定元素: public void TestBasicHttpBindingElements() { BasicHttpBinding _binding=new BasicHttpBinding(); BindingElementCollection _list = _binding.CreateBindingElements(); foreach (BindingElement ele in _list) { Console.WriteLine(ele.GetType().FullName); } } 结果: System.ServiceModel.Channels.TextMessageEncodingBindingElement System.ServiceModel.Channels.HttpTransportBindingElement 它由2层组成。从字面上可以大概推断出一层用于消息编码,一层用于传输。绑定是分层的,最低要有两层:传输层与编码层。 ·TextMessageEncodingBindingElement这个绑定元素的结构为: BindingElement MessageEncodingBindingElement TextMessageEncodingBindingElement 它的定义为: public sealed class TextMessageEncodingBindingElement : MessageEncodingBindingElement, IWsdlExportExtension, IPolicyExportExtension ·HttpTransportBindingElement这个绑定元素的结构为: BindingElement TransportBindingElement HttpTransportBindingElement 它的定义为: public class HttpTransportBindingElement : TransportBindingElement, IWsdlExportExtension, IPolicyExportExtension (2)WSDualHttpBinding绑定元素 public void TestWSDuplexHttpBindingElements() { WSDualHttpBinding _binding = new WSDualHttpBinding(); BindingElementCollection _list = _binding.CreateBindingElements(); foreach (BindingElement ele in _list) { Console.WriteLine(ele.GetType().FullName); } } 结果: System.ServiceModel.Channels.TransactionFlowBindingElement System.ServiceModel.Channels.ReliableSessionBindingElement System.ServiceModel.Channels.SymmetricSecurityBindingElement System.ServiceModel.Channels.CompositeDuplexBindingElement System.ServiceModel.Channels.OneWayBindingElement System.ServiceModel.Channels.TextMessageEncodingBindingElement System.ServiceModel.Channels.HttpTransportBindingElement 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园
WCF绑定提供了WCF通信的细节。 (一)绑定元素 绑定元素(Binding Element)用于为各种类型的信道(Channel)生成信道工厂(Channel Factory)和信道监听器(Channel Listener)以处理传出和传入消息(Message)。 绑定(Binding)包含绑定元素,一个绑定对象是一个绑定元素对象的有序集合,而这些绑定元素是用来指定WCF通信所用的协议、编码、传输等。 每一个绑定元素描述一个终结点与其它终结点通信方式的一个方面。下面是绑定层下的各绑定元素的介绍: 层 选项 是否必需 事务流 TransactionFlowBindingElement 否 可靠性 ReliableSessionBindingElement 否 安全性 对称、非对称、传输级 否 形状更改 CompositeDuplexBindingElement 否 传输升级 SSL 流、Windows 流、对等解析程序 否 编码 文本、二进制、MTOM、自定义 是 传输 TCP、命名管道、HTTP、HTTPS、MSMQ、自定义 是 其中,编码层与传输层是必需的。 绑定元素负责对信道管理器(Channel Manager)的创建。 (二)信道管理器 信道管理器(Channel Manager)由绑定元素负责创建,它是信道的创建者。服务端的信道管理器叫做信道监听器(Channel Listener);客户端的信道管理器叫信道工厂。 信道管理器基类:ChannelManagerBase 它从CommunicationObject派生,且实现了IDefaultCommunicationTimeouts接口 public abstract class ChannelManagerBase : CommunicationObject, IDefaultCommunicationTimeouts (1)服务端的信道监听器 信道监听器从ChannelListenerBase类派生, 另外还有接口IChannelListener 还有一个泛型接口IChannelListener<T> 这两个接口分别有抽象基类: ChannelListenerBase ChannelListenerBase<T> (2)客户端的信道工厂 信道工厂从ChannelFactoryBase 类派生, 另外还有接口IChannelFactory 还有一个泛型接口IChannelFactory<T> 两个接口分别有抽象基类: ChannelFactoryBase ChannelFactoryBase<T> 信道管理器负责对信道的创建。 (三)信道 多个信道(Channel)组成连续的信道栈。信道栈构成了消息(Message)传递的通道。 接口:IChannel 定义所有信道对象都必须实现的基本接口。它要求信道对象实现由所有信道对象共享的状态机接口,还要求信道对象实现从信道栈中检索对象的方法。 这个接口有一个抽象基类:ChannelBase 因为消息交换模式不同(MEP),有不同的信道接口。 IChannel 由基本消息交换模式的每个关联信道接口继承: IInputChannel 用于接收消息。 IOutputChannel 用于发送消息。 IRequestChannel 用于发送请求。 IReplyChannel 用于发送回复。 IDuplexChannel 用于双向消息传递。 更多内容,请见: http://www.cnblogs.com/artech/archive/2008/09/22/1295639.html 博客园大道至简 http://www.cnblogs.com/jams742003/ 转载请注明:博客园