-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1505 lines (902 loc) · 536 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>MongoDB权威指南(二)</title>
<link href="/2019/02/12/MongoDB%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E4%BA%8C/"/>
<url>/2019/02/12/MongoDB%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E4%BA%8C/</url>
<content type="html"><![CDATA[<ol><li><p>可以使用insert方法向目标集合插入一个文档,该操作会给文档自动增加一个”_id”键(若原先没有),然后将其保存到MongoDB中;在shell中还可以通过save函数来创建文档,该函数接收一个文档参数,如果文档中含有”id”键并在集合中已存在则进行更新,否则进行插入</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.insert({"_id":18,"name":"stefan"})</div></pre></td></tr></table></figure></li><li><p>如果要向集合中插入多个文档,可以使用批量插入将一组文档传递给数据库,使用insert方法也可实现批量插入,该方法接受的是一个文档数组作为参数。如果在执行批量插入的过程中有一个文档插入失败,那么在这个文档之前的所有文档都会插入成功,这个文档以及之后的所有文档全部插入失败,可以使用continueOnError来忽略错误并继续执行后续插入。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.insert([{"name":"stefanli"},{"name":"stefanbi"}])</div></pre></td></tr></table></figure></li><li><p>可以使用remove方法,来删除集合中的文档,接受一个参数来删除指定的文档,如果参数为空文档,则删除集合中的所有文档(如果要清空整个集合,使用drop会更快)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">db.tester.remove({"name":"stefanbi"})</div><div class="line">db.tester.drop()</div></pre></td></tr></table></figure></li><li><p>可以使用update方法来更新文档,update有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器文档,用于说明要对找到的文档进行哪些修改。</p><ul><li>最简单的更新就是用一个新文档完全替换匹配的文档,这样更新时,要注意避免重复”_id”报错</li><li>通常文档只会有一部分要更新。可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。更新修改器是种特殊的键,用来指定复杂的更新操作。使用修改器时,”_id”的值不能改变(整个文档替换时可以改变”id”),其他键值都可以更改。</li></ul></li><li><ul><li><p>“$set”修改器:用来指定一个字段的值。如果这个字段不存在,则创建它。这对更新模式或者增加用户定义的键来说非常方便。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$set":{"age":15}})</div></pre></td></tr></table></figure></li><li><p>“$unset”修改器:用来删除某个键值对</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$unset":{"age":""}})</div></pre></td></tr></table></figure></li><li><p>“$inc”修改器:用来增加已有键的值,或者该键不存在那就创建一个。对于更新分析数据、因果关系、投票或者其他有变化数值的地方,使用这个都会非常方便</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$inc":{"age":20}})</div></pre></td></tr></table></figure></li><li><p>“$push”修改器:如果数组已经存在,会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组。在此修改器的基础上使用”$each”子操作符,可以一次追加多个元素,与”$each”子操作符相关的还有”$slice”和”$sort”操作符</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$push":{"hobby":{"$each":["basketball","pingpangball"]}}})</div></pre></td></tr></table></figure></li><li><p>“$addToSet”修改器:可以保证数组中的元素是唯一的,如果存在则不做任何操作,否则就将该值添加到数组中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$addToSet":{"hobby":"movie"}})</div></pre></td></tr></table></figure></li><li><p>“$pop”修改器:可以从数组的任何一端删除元素;”$pull”修改器:可以基于指定条件来删除元素</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":sd._id},{"$pop":{"hobby":1}})</div><div class="line">db.tester.update({"_id":sd._id},{"$pull":{"hobby":"basketball"}})</div></pre></td></tr></table></figure></li><li><p>MongoDB提供了定位操作符”$”,用来定位查询文档已经匹配的数组元素,并进行更新。定位符只更新第一个匹配的元素。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"hobby":"football"},{"$set":{"hobby.$":"basketball"}})</div></pre></td></tr></table></figure></li><li><p>upsert是一种特殊的更新,要是没有找到符合更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。upsert位于update方法的第三个参数,是一个布尔值,默认为false。与其相关的一个修改器是”$setOnInsert”,其表示存在则不更新,不存在则在创建的时候为其赋值</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"_id":tes._id},tes,true)</div><div class="line">db.tester.update({"_id":tes._id},{"$setOnInsert":{age:18}},true)</div></pre></td></tr></table></figure></li></ul></li><li><p>默认情况下,更新只能对符合匹配条件的第一个文档执行操作,要更新所有匹配的文档,可以将update的第四个参数设置为true。要想知道多文档更新到底更新了多少文档,可以运行getLastError命令。键”n”的值就是被更新文档的数量。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.tester.update({"age":20},{"$inc":{"age":2}},false,true)</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> MongoDB </tag>
</tags>
</entry>
<entry>
<title>MongoDB权威指南(一)</title>
<link href="/2019/02/11/MongoDB%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E4%B8%80/"/>
<url>/2019/02/11/MongoDB%E6%9D%83%E5%A8%81%E6%8C%87%E5%8D%97-%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>纵向扩展就是使用计算能力更强的机器,纵向扩展是最省力的做法,其缺点是大型机一般都非常昂贵。而且,当数据量达到机器的物理极限时,无论花多少钱也买不到更强的机器了。横向扩展就是通过分区将数据分散到更多机器上,要增加存储空间或提高性能,只需购买一台普通的服务器并将它添加到集群中就可以了。<a id="more"></a></li><li>MongoDB不但区分类型,而且区分大小写;同时MongoDB的文档不能有重复的键;文档中的键/值对是有序的</li><li>组织集合的一种惯例是使用”,”分隔不同命名空间的子集合,虽然子集合没有任何特别的属性,但它们却非常有用。</li><li>数据库最终会变成文件系统里的文件,而数据库名就是相应的文件名,因此数据库名有许多限制</li><li>把数据库名添加到集合名前,得到集合的完全限定名,即命名空间</li><li>启动shell时,shell会连到MongoDB服务器的test数据库,并将数据库连接赋值给全局变量db。这个变量是通过shell访问MongoDB的主要入口点。可以通过db查看当前指向哪个数据库。</li><li>创建日期对象时,应该使用new Date(…),而非Date(…),前者返回的是日期对象,后者返回的是日期字符串。</li><li>MongoDB中存储的文档必须有一个”_id”键。这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每个文档都有唯一的该键,确保集合里面每个文档都能被唯一标识。</li><li>ObjectId是”_id”的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它,ObjectId使用12字节的存储空间,前四个字节是以秒为单位的时间戳;接下来的三个字节是所在主机的唯一标识符,通常是机器主机名的散列值;接下来的两个字节是产生该ObjectId的进程标识符;最后三个字节是一个自动增加的计数器。</li><li>能交给客户端驱动程序来做的事情就不要交给服务器来做,扩展应用层要比扩展数据库层容易很多。</li><li><p>在启动shell时指定机器名和端口,就可以连接到一台不同的机器</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mongo host:30000/myDB</div></pre></td></tr></table></figure></li><li><p>当集合名称中包含保留字或者无效的JavaScript属性名称时,db.collectionName就不能正常工作了,此时可以使用db.getCollection方法,或者使用数组访问语法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">db.getCollection("person")</div><div class="line">db["person"]</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> MongoDB </tag>
</tags>
</entry>
<entry>
<title>IDEA使用技巧</title>
<link href="/2018/12/29/IDEA%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/"/>
<url>/2018/12/29/IDEA%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/</url>
<content type="html"><![CDATA[<ol><li>在使用代码补齐时,用Tab键可以输入弹出列表里的高亮显示部分,这个操作会覆盖掉字符右边名字的其它部分,比Enter键有用。<a id="more"></a></li><li>当想用代码片段捕捉异常时,在编辑器里选中这个片段,按Ctrl-Alt-T,然后选择try/catch。它会自动产生代码片段中抛出的所有异常的捕捉块。</li><li>用Ctrl-F12键可以在当前编辑的文件中快速导航。这时它会显示当前类的成员列表。选中一个要导航的元素然后按Enter键或F4键。要轻松地定位到列表中的一个条目,只需键入它的名字即可。</li><li>在代码中把光标置于标记符或者它的检查点上按Alt-F7,会查找到所有引用该标记符的地方</li><li>把光标放到查看点上再按Ctrl-Alt-B可以导航到一个抽象方法的实现代码。</li><li>用Alt-F3在编辑器中实现快速查找功能。</li><li>如果光标置于一个方法调用的括号里,按Ctrl-P会显示一个可用参数的列表。</li><li>要快速查看编辑器脱字符处使用的类或方法的Java文档,按Ctrl-Q即可。</li><li>把编辑器脱字符置于任何一个变量名字上然后按Shift-F6。在对话框里键入要显示地新名字再按Enter。可以看到这个变量地所有地方然后按”Do Refactor”按钮结束重命名操作。</li><li>Alter-Enter可以将多行拼接的字符串,去除拼接然后放到粘贴板上。</li><li>Ctrl-Shift-Backspace可以跳转到上次编辑的地方</li></ol>]]></content>
<tags>
<tag> Intellij IDEA </tag>
</tags>
</entry>
<entry>
<title>Spring实战(三)</title>
<link href="/2018/12/27/Spring%E5%AE%9E%E6%88%98-%E4%B8%89/"/>
<url>/2018/12/27/Spring%E5%AE%9E%E6%88%98-%E4%B8%89/</url>
<content type="html"><![CDATA[<ol><li>注解:Java提供了一种源程序中的元素关联任何信息和任何元数据的途径和方法</li><li>JDK自带注解<ul><li>@Override 表示覆盖或重写父类的方法<a id="more"></a></li><li>@Deprecated 表示该方法已过时,如果使用了加有该注解的元素,编译器会发出警告信息</li><li>@SuppressWarnings 表示忽略指定警告</li></ul></li><li>第三方注解,如Spring中的与bean相关的注解以及ibatis中的与Sql语句相关的注解等</li><li>按照来源划分的话,除了以上两种类型的注解外,还包含自定义注解;按运行机制(注解存在于程序的哪个阶段)将注解可分为三类<ul><li>源码注解(只存在于源码中)</li><li>编译注解(在class文件中也存在)</li><li>运行时注解(在运行阶段仍然起作用)</li></ul></li><li>自定义注解的语法要求<ul><li>使用@interface关键字定义注解</li><li>成员以无参无异常方式声明;成员类型是受限的,合法的类型包括原始类型及String,Class,Annotation,Enumeration;如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号;可以用default为成员指定一个默认值。</li><li>注解类可以没有成员,没有成员的注解称为标识注解</li></ul></li><li><p>可以在自定义注解上添加以下元注解</p><ul><li>@Target 注解的作用域:表示该注解可以用于一个类中的哪些属性及方法上,如果作用域类型有多个用逗号分隔。</li><li>@Retention 注解的生命周期</li><li>@Inherited 此注解是标识性的元注解,表示当前注解可以由子注解来继承</li><li>@Documented 表示生成javadoc的时候会包含注解<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">@Target({ElementType.METHOD})</div><div class="line">@Retention(RetentionPolicy.RUNTIME)</div><div class="line">@Inherited</div><div class="line">@Documented</div><div class="line">public @interface TestAnnotation {</div><div class="line"> String value();</div><div class="line">}</div></pre></td></tr></table></figure></li></ul></li><li><p>可以通过反射获取类、函数或成员上运行时注解信息,从而实现动态控制程序运行的逻辑</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">Child child=new Child();</div><div class="line">Class c=Class.forName("Service.Imp.Child");</div><div class="line">Method[] methods=c.getMethods();</div><div class="line">for(Method method:methods){</div><div class="line"> if(method.isAnnotationPresent(TestAnnotation.class)){</div><div class="line"> TestAnnotation testAnnotation=method.getAnnotation(TestAnnotation.class);</div><div class="line"> Object name=method.invoke(child,testAnnotation.value());</div><div class="line"> System.out.println(name);</div><div class="line"> }</div><div class="line">}</div><div class="line">Field[] fields=c.getDeclaredFields();//使用getFields获取某个类的所有的公共的字段</div><div class="line">for (Field i : fields){</div><div class="line"> System.out.println(123);</div><div class="line"> if(i.isAnnotationPresent(ColumnAnnotation.class))</div><div class="line"> {</div><div class="line"> ColumnAnnotation columnAnnotation=i.getAnnotation(ColumnAnnotation.class);</div><div class="line"> String columnName=columnAnnotation.value();</div><div class="line"> System.out.println(i.getName());</div><div class="line"> String getMethodName="get"+i.getName().substring(0,1).toUpperCase()+i.getName().substring(1);</div><div class="line"> String value=c.getMethod(getMethodName).invoke(p).toString();</div><div class="line"> if(value!=null){</div><div class="line"> s=s+" and "+columnName+"="+value;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>HTTP知识</title>
<link href="/2018/12/27/HTTP%E7%9F%A5%E8%AF%86/"/>
<url>/2018/12/27/HTTP%E7%9F%A5%E8%AF%86/</url>
<content type="html"><![CDATA[<ol><li>Request Payload通常指POST或者PUT请求发送的数据,位于HTTP请求头和一个回车换行之后的请求体中。如果请求时Content-Type的类型为默认的<code>application/x-www-form-urlencoded</code>,则数据存在于特殊的Request Payload(Form Data)中。<a id="more"></a></li><li>根据请求时Content-Type的类型不同,会对请求体中的数据进行不同形式的编码,如默认的Content-Type是<code>application/x-www-form-urlencoded</code>,这种编码方式,就是把表单名字和值组成键值对的形式;若Content-Type是<code>application/json</code>,那么需要的数据就是Json格式的字符串。如果不使用默认的Content-Type,数据是不会在Form Data中的,此时服务器端可能会需要做某些特殊处理来解析此类数据,比如Spring Boot中的RequestBody注解。</li><li>针对请求时Content-Type类型的不同,接收端会对接收到的数据做不同的处理。</li></ol>]]></content>
<tags>
<tag> -HTTP </tag>
</tags>
</entry>
<entry>
<title>Java常用操作</title>
<link href="/2018/12/25/Java%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/"/>
<url>/2018/12/25/Java%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/</url>
<content type="html"><![CDATA[<h3 id="配置文件读取"><a href="#配置文件读取" class="headerlink" title="配置文件读取"></a>配置文件读取</h3><p>如果针对程序进行打jar包,则配置文件可以放在包内也可以放在包外<br><a id="more"></a></p><ol><li><p>如果配置文件放在src/main/resources目录下,则属于在包内,读取时可以使用以下语句</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Properties config=new Properties();</div><div class="line">properties.load(Config.class.getResourceAsStream("/init.properties"));</div><div class="line">//properties.load(this.getClass().getResourceAsStream("/init.properties"));</div><div class="line">config.getProperty("key");</div></pre></td></tr></table></figure></li><li><p>读取系统外配置文件(即jar包外文件)时,需要在Jar包所在目录放置配置文件(依赖于读取位置)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Properties externalConfig=new Properties();</div><div class="line">String filepath=System.getProperty("user.dir")+ File.separator+"conf"+File.separator+"externalTest.properties";</div><div class="line">InputStream in1=new BufferedInputStream(new FileInputStream(filepath));</div><div class="line">externalConfig.load(bf1);</div><div class="line">externalConfig.getProperty("name");</div></pre></td></tr></table></figure></li><li><p>流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。</p><ul><li>字节流,以字节为单位,能处理所有类型的数据</li><li>字符流,以字符为单位,一次可能读多个字节,只处理字符类型的数据,使用了缓冲区(buffer)</li></ul></li><li><p>如果配置文件中出现中文配置,可能在读取后产生乱码数据,因为读取时使用的字节流,可以通过将其包装为字符流来解决乱码问题</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">BufferedReader bf1 = new BufferedReader(new InputStreamReader(in1));</div></pre></td></tr></table></figure></li><li><p>如果在cmd里面用java -jar执行jar包时出现乱码,则可以通过以下方法进行解决</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">java -Dfile.encoding=utf-8 -jar ***.jar</div></pre></td></tr></table></figure></li></ol><h3 id="日志记录"><a href="#日志记录" class="headerlink" title="日志记录"></a>日志记录</h3><ol><li><p>使用log4j来记录日志时,需要引用以下两个jar包:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"> compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.1'</div><div class="line">compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.1'</div></pre></td></tr></table></figure></li><li><p>需要在resources文件夹下创建一个log4j2.xml配置文件用于log4j的配置,包括输出位置,输出等级以及输入格式等等,常规配置如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><?xml version="1.0" encoding="UTF-8"?></div><div class="line"><configuration status="error"></div><div class="line"> <appenders></div><div class="line"> <Console name="Console" target="SYSTEM_OUT"></div><div class="line"> <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/></div><div class="line"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/></div><div class="line"> </Console></div><div class="line"> <File name="log" fileName="target/test.log" append="false"></div><div class="line"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/></div><div class="line"> </File></div><div class="line"> <RollingFile name="RollingFile" fileName="logs/app.log"</div><div class="line"> filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"></div><div class="line"> <PatternLayout pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/></div><div class="line"> <SizeBasedTriggeringPolicy size="500 MB" /></div><div class="line"> </RollingFile></div><div class="line"> </appenders></div><div class="line"> <loggers></div><div class="line"> <root level="error"></div><div class="line"> <appender-ref ref="RollingFile"/></div><div class="line"> <appender-ref ref="Console"/></div><div class="line"> </root></div><div class="line"> </loggers></div><div class="line"></configuration></div></pre></td></tr></table></figure></li><li><p>在需要记录日志的地方创建一个Logger,然后就可以使用log4j来记录日志了,如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">private static Logger logger= LogManager.getLogger(ReadPropertiesApp.class);</div><div class="line">logger.error(ex.getMessage());</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Java </tag>
</tags>
</entry>
<entry>
<title>Gradle教程(一)</title>
<link href="/2018/12/20/Gradle%E6%95%99%E7%A8%8B-%E4%B8%80/"/>
<url>/2018/12/20/Gradle%E6%95%99%E7%A8%8B-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>和Maven一样,Gradle只是提供了构建项目的一个框架,真正起作用的是Plugin。Gradle在默认情况下为我们提供了许多常用的Plugin,其中包括有构建Java项目的Plugin,还有War,Ear等。与Maven不同的是,Gradle不提供內建的项目生命周期管理,只是java Plugin向Project中添加了许多Task,这些Task依次执行,为我们营造了一种如同Maven般项目构建周期。<br><a id="more"></a></p><ol><li>Gradle本身的领域对象主要有Project和Task。Project为Task提供了执行上下文,所有的Plugin要么向Project中添加用于配置的Property,要么向Project中添加不同的Task。一个Task表示一个逻辑上较为独立的执行过程。</li><li>Gradle目录结构说明:<ul><li>build.gradle:Gradle用于配置当前项目的构件脚本</li><li>gradle-wrapper.jar:Gradle可执行jar包</li><li>gradle-wrapper.properties:Gradle可执行jar包的属性配置</li><li>gradlew:用于类Unix系统的脚本执行</li><li>gradlew.bat:用于Windows系统的脚本执行</li><li>settings.gradle:用于配置Gradle构件的一些Gradle设置脚本</li></ul></li><li><p>创建一个task基本语法示例:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">task copy(type: Copy, group: "Custom", description: "Copies sources to the dest directory") {</div><div class="line"> from "src"</div><div class="line"> into "dest"</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>Gradle包含一系列的插件,使用plugins语法来添加插件,确保plugins语句块在文件的开始(Gradle版本2.1及之后)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">plugins {</div><div class="line"> id "base"</div><div class="line"> //id «plugin id» version «plugin version»</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>一些常用的用法:</p><ul><li>tasks命令:列举出你能够调用的tasks,包括插件中的以及自定义的task</li><li>如果指定了多个task,应该使用空格来将其进行区分</li><li>在多项目构建中,二级项目的tasks可以通过在二级项目名称与task名称间加冒号来进行调用;如果在根目录直接调用该task,会广播到所有的二级项目;如果将目录切换到二级目录直接调用该task,则只会对该项目有影响</li><li>从包含依赖的tasks中排除某个task,可以在执行某个主task时使用-x参数来进行排除</li><li>通过<code>-q help --task taskname</code>语句可以查看特定task的明细信息</li></ul></li><li><p>使用Gradle的jar任务可以将项目打成一个jar包,需要指定主类文件的限定名,如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">jar{</div><div class="line"> manifest.attributes 'Main-Class':'AppApplication'</div><div class="line"> from configurations.compile.collect{zipTree it}</div><div class="line"> archiveName '测试.jar'//默认jar包名称[baseName]-[appendix]-[version]-[classifier].[extension]</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>使用shadow插件也可以将项目打成一个jar包,类似于jar任务</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">plugins {</div><div class="line"> id 'com.github.johnrengelman.shadow' version '4.0.3'</div><div class="line">}</div><div class="line">shadowJar {</div><div class="line"> manifest {</div><div class="line"> attributes(</div><div class="line"> 'Main-Class': 'AppAppliication'</div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>当项目中存在多个module,或者依赖的Library中引用了相同的库,但库的版本不一致时,或者依赖的库版本已经不再使用时,可以强制使用库的某个版本(直接在build.gradle中添加即可)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">configurations.all {</div><div class="line"> resolutionStrategy {</div><div class="line"> force 'com.github.bumptech.glide:glide:4.2.0'</div><div class="line"> force 'com.github.bumptech.glide:compiler:4.2.0'</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>当项目使用了SpringBoot框架时,可以通过<code>org.springframework.boot</code>插件将其进行打jar包,使用插件中的bootJar任务即可</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">plugins{</div><div class="line"> id "org.springframework.boot" version "2.1.1.RELEASE" </div><div class="line">}</div><div class="line">bootJar{</div><div class="line"> archiveName '测试.jar'</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p><code>io.spring.dependency-management</code>插件,可以在添加第三方依赖时,不需要明确指定版本号,它能自动帮我们选择一个最优的版本,保证最大限度的扩展。</p></li><li><p>添加依赖的几种方式</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">//添加外部依赖</div><div class="line">configurationName "group:name:version:classifier@extension"</div><div class="line">configurationName group: group, name: name, version: version, classifier: classifier, ext: extension</div><div class="line">//添加项目依赖</div><div class="line">configurationName project(':someProject')</div><div class="line">//添加文件依赖</div><div class="line">configurationName files('a file')</div></pre></td></tr></table></figure></li><li><p>针对依赖,可以添加一些额外的配置,如下语句,既可以强制使用某个版本的依赖,也可以排除指定的依赖</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">compile('org.hibernate:hibernate:3.1') {</div><div class="line"> //in case of versions conflict '3.1' version of hibernate wins:</div><div class="line"> force = true</div><div class="line"></div><div class="line"> //excluding a particular transitive dependency:</div><div class="line"> exclude module: 'cglib' //by artifact name</div><div class="line"> exclude group: 'org.jmock' //by group</div><div class="line"> exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group</div><div class="line"></div><div class="line"> //disabling all transitive dependencies of this dependency</div><div class="line"> transitive = false</div><div class="line"> }</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Gradle </tag>
</tags>
</entry>
<entry>
<title>Spring实战(二)</title>
<link href="/2018/12/07/Spring%E5%AE%9E%E6%88%98-%E4%BA%8C/"/>
<url>/2018/12/07/Spring%E5%AE%9E%E6%88%98-%E4%BA%8C/</url>
<content type="html"><![CDATA[<h3 id="自动化装配bean"><a href="#自动化装配bean" class="headerlink" title="自动化装配bean"></a>自动化装配bean</h3><ol><li>Spring从两个角度来实现自动化装配:<ul><li>组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。</li><li>自动装配(autowiring):Spring自动满足bean之间的依赖。<a id="more"></a></li></ul></li><li>在类上加某些注解,可以用来告知Spring要为这个类创建bean,以下注解功能一样,只是用于不同需求的类上:<ul><li>@Component:通用的Spring管理的组件</li><li>@Repository:用于持久层</li><li>@Service:用于服务层</li><li>@Controller:用于表现层(spring-mvc)</li></ul></li><li>组件扫描默认是不启用的,还需要显示配置下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。@ComponentScan注解则用于该目的,默认其会扫描与配置类相同的包以及子包,当然也可以使用XML来启用组件扫描。</li><li>简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,可以借助@Autowired注解。</li><li>@Autowired注解,可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常,为了避免异常的出现,可以设置其required属性为false。@Inject与其功能类似。</li><li>Spring提供了多种可选方案来解决装配bean时存在的歧义:<ul><li>可以将可选bean中的某一个设为首选(primary)的bean,通过Primary注解</li><li>或者使用限定符(qualifier)来帮助Spring将可选的bean的范围缩小到只有一个bean,通过Qualifier注解,此注解所设置的参数是想要注入的bean的ID,所有使用Component注解声明的类都会创建为bean,并且bean的ID为首字母变为小写的类名,如果类名是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致。</li></ul></li></ol><h3 id="Java装配bean"><a href="#Java装配bean" class="headerlink" title="Java装配bean"></a>Java装配bean</h3><ol><li>如果想要将第三方库中的组件装配到应用中,自动化装配的方案就行不通了,此时可以使用显示装配,Java和XML中Java配置是更好的选择,因为它更为强大、类型安全并且对重构友好。</li><li>创建Java配置类的关键在于为其添加@Configuration注解,该注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。</li><li>要在Java配置中声明bean,我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。该注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。</li><li>可以通过构造器和Setter方法来装配依赖</li></ol><h3 id="XML装配bean"><a href="#XML装配bean" class="headerlink" title="XML装配bean"></a>XML装配bean</h3><p>不推荐使用该方式装配bean</p>]]></content>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>Spring实战(一)</title>
<link href="/2018/12/06/Spring%E5%AE%9E%E6%88%98-%E4%B8%80/"/>
<url>/2018/12/06/Spring%E5%AE%9E%E6%88%98-%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>在基于Spring的应用中,应用对象生存于Spring容器中。Spring容器负责创建对象,装配他们,配置它们并管理他们的整个生命周期,从生存到死亡。</li><li>容器是Spring框架的核心。Spring容器使用DI管理构成应用的组件,它会创建相互协作的组件之间的关联。<a id="more"></a></li><li>Spring容器并不是只有一个。Spring自带了多个容器实现:<ul><li>bean工厂 是最简单的容器,提供基本的DI支持</li><li>应用上下文 基于BeanFactory构建,并提供应用框架级别的服务。</li></ul></li><li>Spring自带了多种类型的应用上下文:<ul><li>AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。</li><li>ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类资源。</li><li>FileSystemXmlApplicationContext:从文件系统下的一个或多个XML配置文件中加载上下文定义。</li><li>AnnotationConfigWebApplicationContext和XmlWebApplicationContext是与Web应用相关的上下文</li></ul></li><li>在bean准备就绪之前,bean工厂执行了若干启动步骤:<ul><li>Spring对bean进行实例化</li><li>Spring将值和bean的引用注入到bean对应的属性中</li><li>如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBean-Name方法</li><li>如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory方法,将BeanFactory容器实例传入</li><li>如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext方法,将bean所在的应用上下文的引用传入进来</li><li>如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization方法</li><li>如果bean实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet方法。类似地,如果bean使用了init-method声明了初始化方法,该方法也会被调用</li><li>如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization方法</li><li>此时,bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁</li><li>如果bean实现了DisposableBean接口,Spring将调用它的destroy接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。</li></ul></li><li>创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。在Spring中装配bean有多种方式,最常见的三种方法如下:<ul><li>在XML中进行显示配置</li><li>在Java中进行显示配置</li><li>隐式的bean发现机制和自动装配。</li></ul></li></ol>]]></content>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>git教程(四)</title>
<link href="/2018/11/20/git%E6%95%99%E7%A8%8B-%E5%9B%9B/"/>
<url>/2018/11/20/git%E6%95%99%E7%A8%8B-%E5%9B%9B/</url>
<content type="html"><![CDATA[<ol><li>远程引用是对远程仓库的引用(指针),包括分支、标签等等。可以通过ls-remote来显式地获得远程引用的完整列表,一个更常见的做法是利用远程跟踪分支。<a id="more"></a></li><li>远程跟踪分支是远程分支状态的引用。它们是你不能移动的本地引用,当你做任何网络通信操作时,它们会自动移动。远程跟踪分支像是你上次连接到远程仓库时,那些分支所处状态的书签。</li><li>它们以(remote)/(branch)形式命名。如果想要看最后一次与远程仓库origin通信时master分支的状态,可以查看origin/master分支。你与同事合作解决一个问题并且他们推送了一个iss53分支,你可能有自己的本地iss53分支;但是在服务器上的分支会指向origin/iss53的提交。</li><li>假设你的网络里有一个在git.ourcompany.com的Git服务器。如果你从这里克隆,Git的clone命令会为你自动将其命名为origin(默认远程仓库名字,可修改),拉取它的所有数据,创建一个指向它的master分支的指针,并且在本地将其命名为origin/master。Git也会给你一个与origin的master分支在指向同一个地方的本地master分支,这样就有工作的基础。</li><li><p>使用push命令可以将本地分支推送到远程分支上去</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git push origin localbranch:remotebranch #推送本地的localbranch分支,将其作为远程仓库的remotebranch分支</div></pre></td></tr></table></figure></li><li><p>使用https协议克隆下来的代码,每次fetch和push代码都需要输入账号和密码,如果不想每次推送时都输入用户名与密码,可以设置一个”credential cache”,可以简单的运行<code>git config --global credential.helper cache</code>来设置它;使用ssh协议克隆下来的代码需要在克隆之前先配置和添加好SSH Key,每次fetch和push代码都不需要输入账号和密码。</p></li><li>从一个远程跟踪分支签出一个本地分支会自动创建一个叫做”跟踪分支”(有时候也叫作”上游分支”)。跟踪分支是与远程分支有直接关系的本地分支。如果在一个跟踪分支上输入pull命令,Git能自动识别去哪个服务器上抓取、合并到哪个分支。</li><li><p>以下命令会创建一个分支来跟踪远程的一个分支</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">git checkout -b branch remote/branch(重命名默认本地分支名称)</div><div class="line">git checkout --track remote/branch</div></pre></td></tr></table></figure></li><li><p>设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用-u或–set-upstream-to选项运行branch来显示地设置。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git branch -u origin/serverfix</div></pre></td></tr></table></figure></li><li><p>如果想要查看设置的所有跟踪分支,可以使用branch的-vv选项。这会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支,与本地分支是否是领先、落后或是都有。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git branch -vv</div></pre></td></tr></table></figure></li><li><p>使用fetch命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容,它只会获取数据然后让你自己合并。然而有一个命令叫做pull,在大多数情况下它的含义是一个fetch命令紧接着一个merge命令。</p></li><li><p>可以运行带有–delete选项的push命令来删除一个远程分支,基本上这个命令做的只是从服务器上移除这个指针。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git push origin --delete branch</div></pre></td></tr></table></figure></li><li><p>带有-v参数的remote命令可以查看当前项目的远程git地址。带有-a参数的branch命令可以查看所有分支列表(包括远程)</p></li></ol>]]></content>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>git教程(三)</title>
<link href="/2018/11/16/git%E6%95%99%E7%A8%8B-%E4%B8%89/"/>
<url>/2018/11/16/git%E6%95%99%E7%A8%8B-%E4%B8%89/</url>
<content type="html"><![CDATA[<ol><li>所有的版本控制系统都以某种形式支持分支。使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。在许多版本控制系统中,这是一个略微低效的过程–常常需要完全创建一个源代码目录的副本。<a id="more"></a></li><li>Git的分支,其实本质上仅仅是指向提交对象的可变指针。Git的默认分支名字是master。在多次提交操作之后,其实已经有一个指向最后那个提交对象的master分支。它会在每次的提交操作中自动向前移动。</li><li><p>使用branch命令可以创建一个新的分支,并不会自动切换到新分支中取,该操作只会在当前所在的提交对象上创建一个指针。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git branch testing</div></pre></td></tr></table></figure></li><li><p>Git有一个名为HEAD的特殊指针,指向当前所在的本地分支。</p></li><li><p>可以使用log命令查看各个分支当前所指的对象。提供这一功能的参数是–decorate。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git log --oneline --decorate</div></pre></td></tr></table></figure></li><li><p>使用checkout命令可以切换到一个已存在的分支,此时HEAD将指向当前分支,如果加上-b参数,则会自动创建分支并切换到该分支。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git checkout testing</div></pre></td></tr></table></figure></li><li><p>分支切换会改变你工作目录中的文件,在切换分支时,一定要注意你工作目录里的文件会被改变。如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。如果Git不能干净利落地完成这个任务,它将禁止切换分支。</p></li><li>使用带-d选项的branch命令可以删除一个分支;使用带-D选项的branch命令可以强制删除一个分支;</li><li>使用merge命令,可以进行分支的合并,合并分为三种:<ul><li>快进(fast-forward):若当前分支所指向的提交是你要并入的提交的直接上游,则Git只是简单的将指针向前移动。</li><li>三方合并:当你的开发历史从一个更早的地方开始分叉开来,当前分支所在的提交并不是要并入的提交的直接祖先,此时Git会进行一个简单的三方合并。</li><li>冲突:如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,在合并它们的时候就会产生合并冲突。此时Git做了合并,但是没有自动创建一个新的合并提交,Git会暂停下来,等待你去解决合并产生的冲突。</li></ul></li><li><p>Git会在有冲突的文件中加入标准的冲突解决标记,看起来像下面这个样子,这表示HEAD所指示的版本在这个区段的上半部分(=======),而iss53分支所指示的版本在=======的下半部分。为了解决冲突,你需要自行合并这些内容,并删除冲突解决标记:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><<<<<<< HEAD:index.html</div><div class="line"><div id="footer">contact : [email protected]</div></div><div class="line">=======</div><div class="line"><div id="footer"></div><div class="line"> please contact us at [email protected]</div><div class="line"></div></div><div class="line">>>>>>>> iss53:index.html</div></pre></td></tr></table></figure></li><li><p>在解决了所有文件里的冲突之后,对每个文件使用add命令将其标记为冲突已解决。一旦暂存这些原本有冲突的文件,Git就会将它们标记为冲突已解决。这时可以输入commit命令来完成合并提交。</p></li><li>branch命令不止可以创建与删除分支。如果不加任何参数运行它,会得到当前所有分支的一个列表(分支前的*表示当前所在分支)。如果想要查看每个分支的最后一次提交,可以加-v参数。–merged与–no-merged这两个参数可以过滤这个列表中已合并或尚未合并到当前分支的分支。</li></ol>]]></content>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>git教程(二)</title>
<link href="/2018/11/12/git%E6%95%99%E7%A8%8B-%E4%BA%8C/"/>
<url>/2018/11/12/git%E6%95%99%E7%A8%8B-%E4%BA%8C/</url>
<content type="html"><![CDATA[<ol><li>要查看哪些文件处于什么状态,可以用status命令<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git status</div></pre></td></tr></table></figure></li></ol><a id="more"></a><ol><li><p>使用命令add可以开始跟踪一个未处于跟踪状态的文件,也可以将已跟踪的修改的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。该命令使用文件或目录的路径作为参数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git add readme</div></pre></td></tr></table></figure></li><li><p>status命令的输出十分详细,但其用于有些繁琐。如果加上-s参数,你将得到一种更为紧凑的格式输出,新添加的未跟踪文件前面有??标记,新添加到暂存区中的文件前面有A标记,修改过的文件前面有M标记,出现在右边的M表示该文件被修改了但是还没放入暂存区,出现在靠左边的M表示该文件被修改了并放入了暂存区。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git status -s</div></pre></td></tr></table></figure></li><li><p>一般总会有些文件无需纳入Git的管理,也不希望它们总出现在未跟踪文件列表,通常这些都是自动生成的文件。在这种情况下,我们可以创建一个名为.gitignore的文件,列出要忽略的文件模式。</p></li><li><p>如果想要知道具体修改了什么地方,可以使用diff命令,该命令将通过文件补丁的格式显示具体哪些行发生了改变。当不加参数时,该命令比较的是工作目录中当前文件和暂存区域快照之间的差异;若要查看已暂存的将要添加到下次提交里的内容,可以加上–cached(–staged)参数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git diff --cached</div></pre></td></tr></table></figure></li><li><p>使用commit命令可以提交暂存区域中的数据(默认不会提交还没暂存起来的数据)。当不加参数时该命令会启动文本编辑器以便输入本次提交的说明;添加-m选项,可以将提交信息与命令放在同一行,不需要启动文本编辑器;添加-a选项,Git会自动把所有已跟踪的文件暂存起来一并提交,从而跳过add步骤。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git commit -am</div></pre></td></tr></table></figure></li><li><p>使用rm命令,可以从Git中移除某个文件,并连带从工作目录中删除指定的文件,如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项-f;添加–cached选项,可以把文件从Git仓库中删除,但文件仍然存在于工作目录中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git rm -cached</div></pre></td></tr></table></figure></li><li><p>使用mv命令,可以进行移动文件或文件重命名</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git mv file_from file_to</div></pre></td></tr></table></figure></li><li><p>使用log命令,可以查看项目的提交历史,该命令会按提交时间列出所有的更新。常用的选项是-p,用来显示每次提交的内容差异,也可以加上-n来显示最近n次提交</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">git log -p -2</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>git教程(一)</title>
<link href="/2018/11/12/git%E6%95%99%E7%A8%8B-%E4%B8%80/"/>
<url>/2018/11/12/git%E6%95%99%E7%A8%8B-%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>在分布式版本控制系统中,像Git、Mercurial、Bazaar以及Darcs等,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。<a id="more"></a></li><li>Git项目有三个工作区域:<ul><li>Git仓库目录:Git用来保存项目的元数据和对象数据库的地方。是Git中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。</li><li>工作目录:对项目的某个版本独立提取出来的内容。这些从Git仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。</li><li>暂存区域:是一个文件,保存了下次提交的文件列表信息,一般在Git仓库目录中。有时也被称作”索引”,不过一般说法还是叫暂存区域。</li></ul></li><li>基本的Git工作流程如下:<ul><li>在工作目录中修改文件。(已修改状态)</li><li>暂存文件,将文件的快照放入暂存区域。(已暂存状态)</li><li>提交更新,找到暂存区域的文件,将快照永久性存储到Git仓库目录。(已提交状态)</li></ul></li><li>Git自带一个git config的工具来帮助设置控制Git外观和行为的配置变量。这些变量存储在三个不同的位置,每一个级别覆盖上一级别的配置:<ul><li>/etc/gitconfig文件:包含系统上每一个用户及他们仓库的通用配置。</li><li>~/.gitconfig或~/.config/git/config文件:只针对当前用户。</li><li>当前使用仓库的Git目录中的config文件(.git/config):针对该仓库。</li></ul></li><li>当安装完Git应该做的第一件事就是设置你的用户名称与邮件地址。因为每一个Git的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改。</li><li>如果想要检查你的配置,可以使用git config –list命令来列出所有Git当时能找到的配置。</li><li><p>如使用Git时需要获取帮助,有三种方法可以找到Git命令的使用手册:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">git help <verb></div><div class="line">git <verb> --help</div><div class="line">man git-<verb></div></pre></td></tr></table></figure></li><li><p>有两种取得Git项目仓库的方法。第一种是在现有项目或目录下导入所有文件到Git中;第二种是从一个服务器克隆一个现有的Git仓库。</p></li><li>在现有的项目中初始化仓库,可以使用init命令,该命令将创建一个名为.git的子目录,这个子目录含有你初始化的Git仓库中所有的必须文件,这些文件是Git仓库的骨干。(此时项目还没有被跟踪)</li><li>如果你想获得一份已经存在了的Git仓库的拷贝,可以使用clone命令,该命令会在当前目录下创建一个Git仓库目录(可以自定义本地仓库的名字),并在这个目录下初始化一个.git文件夹,从远程仓库拉取下所有数据放入.git文件夹,然后从中读取最新版本的文件的拷贝。</li><li>工作目录下的每一个文件都不外乎这两种状态:已跟踪或未跟踪。已跟踪的文件是指那些被纳入了版本控制的文件,除此之外都属于未跟踪文件,它们即不存在于上次快照的记录中,也没有放入暂存区。</li></ol>]]></content>
<tags>
<tag> git </tag>
</tags>
</entry>
<entry>
<title>SQL执行计划四</title>
<link href="/2018/10/10/SQL%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E5%9B%9B/"/>
<url>/2018/10/10/SQL%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E5%9B%9B/</url>
<content type="html"><![CDATA[<ol><li>存储过程或语句运行时间取决于服务器的工作量。服务器负担重的话,存储过程或语句可能需要更多的时间来运行,因为它在等待CPU周期和IO完成,因此通过在语句前后打印时间差的方式并不适合来判定性能是否提高<a id="more"></a></li><li><p>通过将IO STATISTICS设置为ON,可以查看之后语句所发生的IO操作,其输出的消息中最重要的是逻辑读取次数和lob 逻辑读取次数,两者越小,性能越好。因为它不会随着执行又执行而改变,除非数据或查询语句有变动</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">SET STATISTICS IO ON; </div><div class="line">......</div><div class="line">SET STATISTICS IO OFF;</div></pre></td></tr></table></figure></li><li><p>通过将TIME STATISTICS设置为ON,可以查看之后语句所消耗的时间,其输出的消息中最重要的是SQL Server执行时间中的CPU占用时间(相对稳定),总时间与服务器上的负载有关</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">SET STATISTICS TIME ON; </div><div class="line">......</div><div class="line">SET STATISTICS TIME OFF;</div></pre></td></tr></table></figure></li><li><p>在使用以上两条语句之前,先要清除SQL Server的数据和过程缓冲区,否则,每次执行查询得到的结果就不具有可比性了</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">DBCC DROPCLEANBUFFERS</div><div class="line">DBCC FREEPROCCACHE</div></pre></td></tr></table></figure></li><li><p>在建立有索引的情况下,查询优化器仍可能使用聚集索引扫描(表扫描),这取决于该索引列中唯一数据占总数据的比例,唯一数据越多,使用该索引的可能性会越大,通过下列语句可以分析查询优化器使用该索引的可能性,其中density越小说明越可能使用该索引</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">DBCC SHOW_STATISTICS('Sales.SalesOrderDetail',</div><div class="line">'IX_SalesOrderDetail_OrderQty')</div></pre></td></tr></table></figure></li><li><p>有时查询优化器选择的执行计划并没有达到预期的效果,或者说查询优化器做出了错误的选择。此时我们可能需要使用Hints(提示)来覆盖SQL Server查询优化器决定的过程。使用Hints时需要注意:</p><ul><li>SQL Server绝大多数情况下会做出正确的选择,即便使用的Hints短期内有效,但随着数据库内容的更改,使用的查询计划反而可能更高效,但此时因为Hints,SQL Server并不会主动地使用优化器</li><li>SQL Server发布补丁后,有效的Hints也可能会改变</li></ul></li></ol>]]></content>
<tags>
<tag> T-SQL </tag>
</tags>
</entry>
<entry>
<title>MongoDB</title>
<link href="/2018/09/20/MongoDB/"/>
<url>/2018/09/20/MongoDB/</url>
<content type="html"><![CDATA[<ol><li>NoSQL全称是”Not Only Sql”,指非关系型数据库,主要有这些特点:非关系型、分布式、开源和水平可扩展的。<a id="more"></a></li><li>NoSQL中用的最多的是key-value存储(Reids),除此之外还有文档型的、列存储、图形数据库、xml数据库等。</li><li>MongoDB是一个介于关系型数据库和非关系型数据库之间的产品,是非关系型数据库当中功能最丰富,最像关系型数据库的。它支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。</li><li>MongoDB最大的特点:它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。它是一个面向集合的,模式自由的文档型数据库。</li><li>文档(document)是MongoDB中的核心概念,它是MongoDB逻辑存储的最小基本单元,对应于关系型数据库中的行;集合(collections)由多个文档组成,对应于关系型数据库中的表;数据库(databases)由多个集合组成。</li><li>在MongoDB shell中输入help可以提供最高的帮助;进入到数据库中后我们可以使用db.help()查看数据库级别的帮助;使用集合.help()函数可以查看集合中的帮助。</li><li>MongoDB中的数据类型:<ul><li>数字:在MongoDB中都是64位的浮点数,他不需要区分数字类型,就是number类型</li><li>字符串:在MongoDB中无需事先定义其长度,无论是定长字符串还是变长字符串</li><li>布尔类型:true、false</li><li>null:在MongoDB中null代表值为null或者字段不存在</li><li>数组:一组数据集合</li><li>对象类型</li></ul></li><li>数据库名是不区分大小写的,通常数据库名使用小写</li><li>常用命令:<ul><li>show dbs:显示数据库列表</li><li>use dbname:进入dbname数据库,大小写敏感,若没有该数据库,在插入数据时会自动创建</li><li>show collections 显示数据库中的集合</li><li>db.users.save({}):创建名为users的集合,并新增一条文档</li><li>db.users.insert({}):在users集合中插入一条新文档,如果没有users集合,则自动创建</li><li>db.users.remove({}):删除users集合下符合条件的数据,如果为空集合则删除所有数据。</li><li>db.users.drop()或db.runCommand({“drop”,”users”}):删除集合users</li><li>db.runCommand({“dropDatabase”:1}):删除当前数据库</li><li>db.users.find():查找users集合中所有数据</li><li>db.users.findOne():查找users集合中的第一条数据</li><li>db.users.update({“name”:”lecaf”},{“age”:10}):修改name=lecaf的数据为age=10,第一个参数是查找条件,第二个参数是修改内容,除了主键其他内容会被第二个参数的内容替换,主键不能修改。</li></ul></li><li>高级应用:<ul><li>db.collection.find({“key”:value}):key= value</li><li>db.collection.find({“key”:{$gt:value}}):key> value</li><li>db.collection.find({“key”:{$lt:value}}):key< value</li><li>db.collection.find({“key”:{$gte:value}}):key>= value</li><li>db.collection.find({“key”:{$lte:value}}):key<= value</li><li>db.collection.find({“key”:{$gt:value1,$lt:value2}}):value1< key< value2</li><li>db.collection.find({“key”:{$ne:value}}):key<>value</li><li>db.collection.find({“key”:{$nin:[1,2,3]}}):key not in [1,2,3]</li><li>db.collection.find({“key”:{$in:[1,2,3]}}):key in [1,2,3]</li><li>db.collection.find({“key”:{$exists:true|false}}):$exists 字段是否存在,存在字段key的文档返回true,不存在字段key的文档返回false</li><li>db.collection.find({“key”:{/^val.*val$/i}}):正则,类似like;”i”忽略大小写,”m”支持多行</li><li>db.collection.find({$or:[{a:1},{b:2}]}):a=1 || b=2</li><li>db.collection.find({“key”:value,$or:[{a:1},{b:2}]}):key=value && (a=1 || b=2)</li><li>db.collection.find(“key.subkey”:value):内嵌对象中的值匹配</li><li>db.collection.find().sort({“key1”:-1,”key2”:1}):1代表升序,-1代表降序</li><li>db.collection.find().limit(5):控制返回结果数量</li><li>db.collection.find().skip(5):控制返回结果跳过多少数量</li><li>db.collection.find().count(true):count()返回结果集的条数 </li></ul></li></ol>]]></content>
<tags>
<tag> MongoDB </tag>
</tags>
</entry>
<entry>
<title>用Python写爬虫(三)</title>
<link href="/2018/09/07/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%B8%89/"/>
<url>/2018/09/07/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%B8%89/</url>
<content type="html"><![CDATA[<ol><li><p>切换到想要存储爬虫代码的目录,然后使用以下命令可以创建一个新的Scrapy项目:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">scrapy startproject tutorial</div></pre></td></tr></table></figure></li><li><p>Spiders是Scrapy用于从网站抓取信息的一些类,它们必须继承scrapy.Spider并且声明了初始化的请求,类内部可以定义如何跟踪页面中的链接,以及如何解析下载页面中的内容,其中一些常用的属性和方法如下:</p><ul><li>name:Spider的标识,项目中必须是唯一的</li><li>start_requests():返回一个可迭代的Requests对象,用于Spider的初始化爬取,后续的请求均产生自这些初始化的请求</li><li>parse():用来处理每个请求所产生的响应数据,通常包括解析响应、抽取数据为字典、寻找页面中的有用链接–用于产生新的请求。response参数是TextResponse类的一个实例</li><li></li></ul></li></ol>]]></content>
<tags>
<tag> 爬虫 </tag>
<tag> Scrapy </tag>
</tags>
</entry>
<entry>
<title>SQL执行计划三</title>
<link href="/2018/09/03/SQL%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%B8%89/"/>
<url>/2018/09/03/SQL%E6%89%A7%E8%A1%8C%E8%AE%A1%E5%88%92%E4%B8%89/</url>
<content type="html"><![CDATA[<p>与执行计划相关的运算符有很多种,详情可以在<a href="http://msdn2.microsoft.com/en-us/library/ms175913.aspx" target="_blank" rel="external">此处</a>中查看</p><ol><li><p>聚集索引扫描(Clustered Index Scan):与表扫描类似,当贯穿整个索引或其中很大一部分时,为了获取所需要查询的数据,会逐行进行扫描。当一个索引存在但优化器认为扫描所有的数据比使用索引查询更快的话,也可能使用索引扫描。该运算符性能较差</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM Person.ContactType</div></pre></td></tr></table></figure></li><li><p>聚集索引查找(Clustered Index Seek):与扫描不同,优化器会使用索引来获取所需要的数据。性能比扫描高</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM Person.ContactType WHERE ContactTypeID=5</div></pre></td></tr></table></figure></li><li><p>非聚集索引查找(Non-clustered Index Seek):与聚集索引查找类似</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT EmailAddress FROM Person.EmailAddress WHERE EmailAddress LIKE 'works.com%'</div></pre></td></tr></table></figure></li><li><p>键查找(Key LookUp):意味着优化器不能通过一个运算符直接返回需要的数据,必须用聚集索引来获取额外的数据。该运算符性能较差</p></li><li><p>嵌套循环(Nested Loops):一个标准的join操作,键查找存在时该运算符必然存在</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM Person.EmailAddress WHERE EmailAddress LIKE 'works.com%'</div></pre></td></tr></table></figure></li><li><p>表扫描(Table Scan):通过逐行扫描表来返回数据。该运算符出现的原因可能是由于无用的索引或者没有过滤条件,也可能优化器认为该操作比使用索引更快。该运算符性能较差</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM dbo.DatabaseLog</div></pre></td></tr></table></figure></li><li><p>RID查找(RID LookUp):与键查找类似,当一张表没有聚集索引时,可能会出现该运算符。该运算符性能较差</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM dbo.DatabaseLog WHERE DatabaseLogID=5</div></pre></td></tr></table></figure></li><li><p>哈希匹配(Hash Match):这种连接有两种输入,即建立输入和探测输入。首先SQL Server会根据统计信息从两张表中筛选出较小的表作为建立输入,并且读入所有行,然后在内存中根据关联条件建立一个哈希表。在整个建立阶段完成后进入探测阶段。以后一行一行的对探测输入进行扫描和计算,并为每个探测行进行计算哈希值,然后进行匹配。当表没有排序或者没有索引时,可能会出现该运算符</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM Person.BusinessEntityAddress a JOIN Person.Address b ON b.AddressID = a.AddressID</div></pre></td></tr></table></figure></li><li><p>合并连接(Merge Join):合并连接要求两个输入都要在合并列上排序。由于每个输入都已排序,因此Merge Join运算符将从每个输入中获取一行进行比较,如果行相等则进行返回,不等则舍弃,如果两张表数据量不同,则会进行重复比较。当数据量不大的时候,这种连接方式比哈希匹配更加有效</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM Person.BusinessEntityAddress a JOIN Person.Address b ON b.AddressID = a.AddressID ORDER BY b.AddressID</div></pre></td></tr></table></figure></li><li><p>嵌套循环(Nested Loop):将一个联接输入用作外部输入表,将另一个联接输入用作内部输入表。外部逐行循环来处理外部输入表,内部逐行循环来处理内部输入表,内部循环会针对每个外部行执行,在内部输入表中搜索匹配的行。当外部输入较小且内部输入有索引在连接的字段上的时候,会出现该运算符</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">SELECT * FROM HumanResources.Employee a JOIN Person.BusinessEntityAddress b ON b.BusinessEntityID = a.BusinessEntityID</div></pre></td></tr></table></figure></li><li><p>计算标量(Compute Scalar):用于一个标量计算并返回计算值,一般出现在筛选谓词或联接谓词中</p></li><li>排序(Sort):当含有order by子句并且排序字段不是聚集索引时,可能会出现该操作符,该操作符会影响性能,尽量避免。</li><li>聚合(Aggregate):当含有group by子句时,会出现哈希匹配(聚合),这是比较昂贵的操作,应该尽量少的出现或使用where子句先过滤一部分数据。</li><li>筛选器(Filter):当含有having子句时,会出现筛选器操作符,这是比较昂贵的操作,应该尽量少的出现或使用where子句先过滤一部分数据。</li></ol>]]></content>
<tags>
<tag> T-SQL </tag>
</tags>
</entry>
<entry>
<title>Python常用操作</title>
<link href="/2018/08/16/Python%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/"/>
<url>/2018/08/16/Python%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C/</url>
<content type="html"><![CDATA[<h3 id="文件操作"><a href="#文件操作" class="headerlink" title="文件操作"></a>文件操作</h3><ol><li><p>open函数用于打开文件,该函数返回一个文件对象。文件名为必填参数,打开文件的模式和缓冲参数都是可选的。语法如下:</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">open(name[,mode[,buffering]])</div></pre></td></tr></table></figure></li><li><p>如果open函数只带一个文件名参数,那么可获得一个只读的文件对象,想要向文件内写入内容,则必须提供一个模式参数来显示声明。有以下几类模式:</p><ul><li>r 只能读,若文件不存在会报错,不能写</li><li>r+ 可读可写,若文件不存在会报错,写入内容会覆盖原内容</li><li>w 只能写,若文件不存在会创建,写入内容会覆盖原内容</li><li>w+ 可读可写,若文件不存在会创建,写入内容会覆盖原内容</li><li>a 只能写,若文件不存在会创建,写入内容会在原内容上追加</li><li>a+ 可读可写,若文件不存在会创建,写入内容会在原内容上追加</li><li>b 将文件标示为二进制文件</li></ul></li><li>文件对象的读写方法有以下几种:<ul><li>read 读取整个文件,通常将文件内容放到一个字符串变量中,可以传递size参数来限制读取字节长度</li><li>readline 每次只读取一行,比readlines慢很多,不会处理换行符</li><li>readlines 读取整个文件,类似于read函数,会自动将文件内容分析成一个行的列表,不会处理换行符</li><li>write、writeline、writelines与上述方法功能类似,用于写入文件</li></ul></li></ol><h3 id="JSON操作"><a href="#JSON操作" class="headerlink" title="JSON操作"></a>JSON操作</h3><p>在python中,有专门处理json格式的模块,json和picle模块(不常用),其中都有四个方法:</p><pre><code>- loads 反序列化为Python对象- load 只接收文件描述符,完成了读取文件和反序列化- dumps 序列化为Python字符串- dump 只接收文件描述符,完成了序列化和写入文件</code></pre><h3 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h3><ol><li>在python中,与正则表达式相关的模块是re模块,其中常用的方法有:<ul><li>match 尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match就返回None</li><li>search 扫描整个字符串并返回第一个成功的匹配</li><li>sub 用于替换字符串中的匹配项,其中目标字符串参数可以是一个函数</li><li>compile 函数用于编译正则表达式,生成一个正则表达式对象,供match和search这两个函数使用</li><li>findall 在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。</li><li>finditer 和findall类似,在字符串中找到正则表达式所匹配的所有字符串,并把它们作为一个迭代器返回。</li><li>split 方法按照能够匹配的子串将字符串分隔后返回列表</li></ul></li><li>如果匹配成功,match或search方法会返回一个匹配的对象,可以使用group(num)或groups匹配对象函数来获取匹配表达式。</li></ol><h3 id="Excel操作"><a href="#Excel操作" class="headerlink" title="Excel操作"></a>Excel操作</h3><ol><li>python操作excel主要用到xlrd(读)和xlwt(写)两个模块,这两个模块需要先进行安装</li><li>常用的单元格中的数据类型:0 empty;1 string(text);2 number;3 date;4 boolean;5 error;6 blank。</li><li>xlrd模块下常用的函数如下:<ul><li>open_workbook 用于打开一个Excel文件,参数为文件名及其路径,返回一个book对象</li><li>book对象 sheets方法获取Excel中的所有sheet页;sheet_by_index方法通过索引来获取指定的sheet页;sheet_by_name方法通过名称来获取指定的sheet页</li><li>sheet对象 nrows属性获取sheet页中的有效行数;row方法通过索引来获取指定的行;row_slice方法通过索引以及指定列索引,来获取指定行的部分列数据;row_types与row_slice方法参数类似,用于获取指定行的部分列数据的类型;row_values与row_slice方法参数类似,用于获取指定行的部分列数据的值;row_len通过索引获取指定行的有效列数。</li><li>针对列的操作与上述针对行的操作类似,只不过方法名有所不同,当然也可以直接操作单元格,针对单元格的操作也是如此。</li></ul></li><li>xlwt模块下常用的函数如下:<ul><li>Workbook 用于创建一个Excel文件,返回一个book对象</li><li>book对象 add_sheet方法创建一个sheet页,如果会对一个单元格重复操作 ,需要为该方法添加一个cell_overwrite_ok参数;save方法保存文件,参数为文件名及其路径。</li><li>sheet对象 write写入某个单元格数据,通过传入style参数,可以指定单元格的样式</li></ul></li><li>如果要操作Excel 2007及以上版本,可以使用openpyxl模块(需要先进行安装)</li></ol><h3 id="SQLServer操作"><a href="#SQLServer操作" class="headerlink" title="SQLServer操作"></a>SQLServer操作</h3><ol><li>python操作SQLServer需要使用pymssql模块,该模块需要先进行安装</li><li>使用pymssql模块的connect方法可以创建一个连接对象,需要提供的主要参数有主机名、用户名、用户密码、数据库名称,字符集、应用程序名称等</li><li>调用连接对象的cursor方法可以创建一个数据库指针,用来处理查询并从数据库中获取数据;连接对象的close方法用于查询结束后关闭连接,可用于回滚未提交的事务;连接对象的commit方法用于提交当前程序中的事务</li><li>调用数据库指针对象的execute可以执行一个sql语句,接着调用对象的fetchall方法可以获取sql执行后的结果</li><li>如果从数据库获取的数据乱码的话可以使用<code>encode('latin-1').decode('gbk')</code>来将其进行转码</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>用Python写爬虫(二)</title>
<link href="/2018/08/15/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%BA%8C/"/>
<url>/2018/08/15/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%BA%8C/</url>
<content type="html"><![CDATA[<ol><li>爬取数据时可能出现的异常有如下几种:<ul><li>爬取的网页出现HTTP错误,此时urlopen函数会抛出”HTTPError”异常</li><li>爬取的服务器不存在,此时urlopen函数会返回一个None对象<a id="more"></a></li><li>如果标签没有定义某属性,却试图去访问该属性时,会抛出”KeyError”异常,此时可以使用in先去判断是否存在该属性,或使用标签的get方法来获取该属性,如果没找到该方法会返回none</li><li>把find_all()的返回结果当作一个tag或文本节点使用,实际上返回结果是一个列表或ResultSet对象的字符串,此时会抛出”AttributeError”异常</li><li>调用find()方法,如果没有找到任何标签,则返回None</li></ul></li><li>Selenium是一个强大的网络数据采集工具,其最初是为网站自动化测试而开发的。近几年,它还被广泛用于获取精确的网站快照,因为它们可以直接运行在浏览器上。Selenium可以让浏览器自动加载页面,获取需要的数据,甚至页面截屏,或者判断网站上某些动作是否发生。</li><li>Selenium自己不带浏览器,它需要与第三方浏览器结合在一起使用,如火狐、谷歌等浏览器,最新的版本不再支持PhantomJS(无头浏览器)</li><li>使用Selenium来操作谷歌浏览器的前期工作:<ul><li>下载与Selenium版本对应的chromedriver</li><li>将其放到C:\Program Files (x86)\Google\Chrome\Application目录下,并将该目录添加到环境变量path里</li><li>将其放到C:\Users\xuxing\AppData\Local\Programs\Python\Python36-32\Scripts目录下</li><li>调用时可能会出现系统找不到指定的文件问题,此时可能需要修改subprocess模块中的Popen类的构造函数中的shell参数默认为True</li></ul></li><li><p>selenium.webdriver模块提供了所有的WebDriver实现,目前支持的WebDriver实现有火狐,谷歌,IE以及远程等,使用该模块相应的方法,可以创建对应的一个WebDriver的一个实例:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">driver=webdriver.Chrome()</div></pre></td></tr></table></figure></li><li><p>driver.get方法将导航到一个特定的页面,WebDriver直到页面加载完成才会返回到脚本中,默认情况下,WebDriver并不会获取到AJAX返回的数据:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">driver.get('https://www.cnblogs.com/')</div></pre></td></tr></table></figure></li><li><p>WebDriver提供了一系列的方法去定位元素的位置,包括使用元素id,元素name,XPath,元素tag名称,元素css选择器,元素class名称等等</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">mainelem=driver.find_element_by_class_name('post_nav_block')</div></pre></td></tr></table></figure></li><li><p>行为链可以自动触发一系列的交互动作,如鼠标移动、鼠标按键、键盘按键等操作;当调用ActionChains实例上的行为方法时,行为被存储在一个队列上,当调用perform方法时,事件会按照在队列中顺序依次执行。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">menu = driver.find_element_by_css_selector(".nav")</div><div class="line">hidden_submenu = driver.find_element_by_css_selector(".nav #submenu1")</div><div class="line"></div><div class="line">actions = ActionChains(driver)</div><div class="line">actions.move_to_element(menu)</div><div class="line">actions.click(hidden_submenu)</div><div class="line">actions.perform()</div></pre></td></tr></table></figure></li><li><p>selenium支持的行为:</p><ul><li>鼠标点击行为:click、click_and_hold、context_click、double_click</li><li>鼠标移动行为:move_by_offset、move_to_element、move_to_element_with_offset</li><li>拖拽行为:drag_and_drop、drag_and_drop_by_offset</li><li>键盘行为:key_down、key_up</li><li>其他行为:send_keys、send_keys_to_element</li></ul></li><li>在selenium.webdriver.common.keys.Keys中保存了一些特殊的键值,比如回车键、F(n)键等等</li><li><p>通过WebDriverWait可以让Selenium不断地检查某个元素是否存在(适用于Ajax或动态生成元素等情况),以免出现元素不存在等情况。Selenium有两种等待模式:</p><ul><li><p>显式等待 针对某个特定的元素设置的等待时间,如果在规定的时间范围内,没有找到元素,则会抛出异常,如果在规定的时间内找到了元素,则直接执行,即找到元素就执行相关操作(使用WebDriverWait和expected_conditions)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">from selenium import webdriver</div><div class="line">from selenium.webdriver.common.by import By</div><div class="line">from selenium.webdriver.support.ui import WebDriverWait</div><div class="line">from selenium.webdriver.support import expected_conditions as EC</div><div class="line">chrome=webdriver.Chrome()</div><div class="line">chrome.get('http://pythonscraping.com/pages/javascript/ajaxDemo.html')</div><div class="line">try:</div><div class="line"> element=WebDriverWait(chrome,10).until(EC.presence_of_element_located((By.ID,'loadedButton')))</div><div class="line"> parentElement=element.find_element_by_xpath('..')</div><div class="line"> print(parentElement.text)</div><div class="line">finally:</div><div class="line"> chrome.quit()</div></pre></td></tr></table></figure></li><li><p>隐式等待 直接调用webdriver实例的implicitly_wait方法即可,传入需要等待的时间参数。</p></li></ul></li><li>当元素存在于iframe中时,直接通过id或者xpath等定位方法是定位不到元素的,此时存在两种方法来定位到该元素:<ul><li>单独打开iframe网址,直接定位</li><li>使用switch_to来定位到irame内部</li></ul></li></ol>]]></content>
<tags>
<tag> 爬虫 </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(十二)</title>
<link href="/2018/07/25/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81%E4%BA%8C/"/>
<url>/2018/07/25/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81%E4%BA%8C/</url>
<content type="html"><![CDATA[<ol><li><p>任何Python程序都可以作为模块导入,可以将程序放在磁盘的任意位置,之后向sys模块下的sys.path追加该磁盘路径即可。sys.path即为指定模块搜索路径的字符串列表,从环境变量PYTHONPATH初始化,加上依赖于安装的默认值。</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">#testmodule.py放于E盘根目录</div><div class="line">print('测试模块')</div><div class="line"></div><div class="line">import sys</div><div class="line">sys.path.append('E:')</div><div class="line">import testmodule</div><div class="line">import testmodule</div></pre></td></tr></table></figure></li><li><p>导入模块并不意味着在导入时执行某些操作,他们主要用于定义,比如变量、函数和类等。此外,因为只需要定义这些东西一次,导入模块多次和导入一次的效果是一样的。</p></li><li><p>在’主程序’中,变量<strong>name</strong>的值是<strong>main</strong>,而在导入的模块中,这个值就被设定为模块的名字,通过这一点,可以将模块中的测试代码在导入时不被执行。(P168)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">import sys</div><div class="line">sys.path.append('E:')</div><div class="line">import testmodule</div><div class="line">print(__name__)</div><div class="line">print(testmodule.__name__)</div></pre></td></tr></table></figure></li><li><p>pprint函数是pprint模块下的方法,相对于print方法是一种标准、格式化输出方式。</p></li><li>在每次调用模块时,都执行sys.path.append语句并不是最好的选择。在理想情况下,一开始sys.path本身就应该包含正确的目录。有两种方法可以做到这一点:<ul><li>将模块放到正确位置(site-packages目录)</li><li>告诉编译器去哪找,标准的实现方式是在PYTHONPATH环境变量中包含模块所在的目录。</li></ul></li><li>为了组织好模块,可以将它们分组为包。包基本上是另一类模块,有趣的地方是它们能包含其他模块。当模块存储在文件中时(扩展名.py),包就是模块所在的目录。为了让Python将其作为包对待,它必须包含一个名为<strong>init</strong>.py的文件(模块)。如果将它作为普通模块导入的话,文件的内容就是包的内容。</li><li><p>使用dir函数可以查看模块包含的内容,它会将模块的所有特性列出(包括以下划线开始的特性)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">import copy</div><div class="line">print([i for i in dir(copy) if i.startswith('_')!=True])</div></pre></td></tr></table></figure></li><li><p>模块.<strong>all</strong>定义了模块的公有接口。更准确地说,它告诉解释器:从模块导入所有名字代表什么含义。在编写模块的时候,设置<strong>all</strong>是相当有用的。如果没有设定<strong>all</strong>,用import*语句默认将会输出模块中所有不以下划线开头的全局名称。</p></li><li>模块.<strong>file</strong>可以用于查看模块源代码所在的位置。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>用Python写爬虫(一)</title>
<link href="/2018/07/20/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%B8%80/"/>
<url>/2018/07/20/%E7%94%A8Python%E5%86%99%E7%88%AC%E8%99%AB-%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>在Python 3.x里,urllib2改名为urllib。urllib是Python的标准库,包含了从网络请求数据,处理cookie,甚至改变请求头和用户代理这些元数据的函数,该包含有的模块:<a id="more"></a><ul><li>urllib.request 请求并读取URLs</li><li>urllib.error 包含由urllib.request产生的一些异常对象</li><li>urllib.parse 解析URL</li><li>urllib.robotparser 解析robots.txt文件</li></ul></li><li>url.request模块下有个urlopen函数,用来打开并读取一个从网络获取的远程对象。(它可以轻松地读取HTML文件、图像文件,或其他任何文件流),该函数返回http.client模块下的一个HTTPResponse对象,该对象的Read函数可以读取并返回最多N个字节。</li><li>在解析HTML页面之前,可以先思考与页面相关的以下问题:<ul><li>寻找”打印此页”的链接,或者看看网站有没有HTML样式更友好的移动版。</li><li>寻找隐藏在JavaScript文件里的信息。</li></ul></li><li>安装BeautifulSoup后,使用bs4模块下的BeautifulSoup类,可以将字符串或文件对象解析为BeautifulSoup对象。</li><li><p>BeautifulSoup里的find()和findAll()是最常用的两个函数。借助它们,可以通过标签的不同属性轻松地过滤HTML页面,查找需要的标签组或单个标签。</p><ul><li>find(tag=None,attrs={},recursive=True,text=None,**kwargs)<ul><li>标签参数tag,可以传一个标签名称或多个标签名称组成的列表做标签参数。</li><li>属性参数attrs,用一个字典封装一个标签的若干属性和对应的属性值。</li><li>递归参数recursive,是否递归抓取HTML文档标签结构的信息。</li><li>文本参数text,用标签的文本内容去匹配,而不是用标签的属性。</li><li>关键字参数kwargs(不常用)</li></ul></li><li><p>findAll(tag=None,attrs={},recursive=True,text=None,limit=None,**kwargs)</p><ul><li>范围限制参数limit,find其实等价于findAll的limit等于1时的情形。</li><li>其余参数与find函数的类似<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">.findAll('span',{'class':'red'},True,None,1)</div></pre></td></tr></table></figure></li></ul></li><li><p>get(self, key, default=None)</p><ul><li>通过属性名,获取标签属性的值,找不到会返回空值</li></ul></li><li>select(self, selector, _candidate_generator=None, limit=None)<ul><li>通过CSS选择器来定位相应的标签,返回一个序列,如果想返回单个标签,可以使用select_one函数</li></ul></li></ul></li><li>使用BeautifulSoup库时常用的对象有四个:<ul><li>BeautifulSoup对象:使用BeautifulSoup库的基础</li><li>标签Tag对象:BeautifulSoup对象通过find和findAll,或者直接调用子标签获取的一列对象或单个对象。</li><li>NavigableString对象:用来表示标签里的文字,不是标签。</li><li>Comment对象:用来查找HTML文档的注释标签。</li></ul></li><li>和许多其他库一样,在BeautifulSoup库里,孩子(child)和后代(descendant)有显著不同:子标签是父标签的直接下级标签,而后代标签是父标签的所有下级标签。一般情况下,BeautifulSoup函数总是处理当前标签的后代标签。children属性用于返回子标签,descendants属性用于返回后代标签。</li><li>next_siblings属性返回当前标签的所有兄弟标签(向下);previous_siblings属性返回当前标签的所有兄弟标签(向上);next_sibling和previous_sibling属性与上述类似,只不过返回单个标签,而不是一组标签。</li><li>parents属性返回当前标签的所有父级标签,parent与其类似,返回单个标签。</li><li>正则表达式可以作为BeautifulSoup语句的任意一个参数</li><li>对于一个标签对象,使用attrs属性可以获取标签的全部属性。该属性是一个字典对象;调用get_text()方法可以返回标签的内容。</li><li>BeautifulSoup允许我们把特定函数类型当做findAll函数的参数。唯一的限制条件是这些函数必须把一个标签作为参数且返回结果是布尔类型。BeautifulSoup用这个函数来评估它遇到的每个标签对象,最后把评估结果为True的标签保留,把其他标签剔除。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">soup.findAll(lambda tag: len(tag.attrs) == 2)</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> 爬虫 </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(十一)</title>
<link href="/2018/07/20/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81%E4%B8%80/"/>
<url>/2018/07/20/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>在Python中,有的名称会在前面和后面都加上两个下划线,由这些名字组成的集合所包含的方法称为魔法(特殊)方法。</li><li>在Python3.0中没有”旧式”的类,也不需要显示地子类化object或者将元类设置为type。所有的类都会隐式地成为object的子类,在之前的版本中,可能存在旧式类和新式类的区别。(P139)<a id="more"></a></li><li><p>构造方法是魔法方法中使用最多的一个,它类似于.NET中的构造函数,用于对象的初始化(<strong>init</strong>)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">class Person:</div><div class="line"> def __init__(self,name,gender):</div><div class="line"> self.name=name</div><div class="line"> self.gender=gender</div><div class="line"></div><div class="line">a=Person('张三','男')</div><div class="line">print(a.name,a.gender)</div></pre></td></tr></table></figure></li><li><p>当子类继承超类时,子类可能会重写超类中的构造方法,导致超类中的构造方法不能正常使用。为了达到预期的效果,有两种方法能达到这个目的:调用超类构造方法的未绑定版本,或者使用super函数。</p></li><li><p>Python对象的绑定方法,有以下结论:</p><ul><li>凡是类中的方法和函数,都是绑定给对象使用的;</li><li>绑定方法都有自动传值的功能。传递进去的值,就是对象本身;</li><li>类调用类中的方法时,这个方法仅仅只是一个函数,既然是函数,就不会有自动传值这一功能,就必须遵循函数的参数规则。<br>给类中相应方法加上classmethod装饰器,则该方法属于类的绑定方法<br>给类中相应方法加上staticmethod装饰器,则该方法属于非绑定方法<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">class Person():</div><div class="line"> def sayHello(self):</div><div class="line"> print('含有self')</div><div class="line"> def sayHello1():</div><div class="line"> print('不含self')</div><div class="line"> @classmethod</div><div class="line"> def sayHello2(cls):</div><div class="line"> print('类方法')</div><div class="line">p=Person()</div><div class="line">print(p.sayHello)</div><div class="line">print(p.sayHello1)</div><div class="line">print(Person.sayHello)</div><div class="line">print(Person.sayHello1)</div><div class="line">print(Person.sayHello2)</div></pre></td></tr></table></figure></li></ul></li><li><p>调用超类构造方法的未绑定版本类似于类调用类中的方法(P142);在新式类中可以使用super函数,当前的类和对象可以作为super函数的参数使用(Python3.0可以不传参数),调用函数返回的对象的任何方法都是调用超类的方法,而不是当前类的方法。除此之外,<strong>init</strong>方法也能以一个普通的方式被调用(不需要传参)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">class Animal():</div><div class="line"> """docstring for Animal"""</div><div class="line"> def __init__(self):</div><div class="line"> self.AnimalName='Animal'</div><div class="line"></div><div class="line">class Person(Animal):</div><div class="line"> """docstring for Person"""</div><div class="line"> def __init__(self):</div><div class="line"> # Animal.__init__(self)</div><div class="line"> super().__init__()</div><div class="line"> self.AnimalType="Person"</div><div class="line"></div><div class="line">a=Person()</div><div class="line">print(a.AnimalType)</div><div class="line">print(a.AnimalName)</div></pre></td></tr></table></figure></li><li><p>创建自己的序列或映射、迭代器、生成器方面的知识(P145)</p></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(十)</title>
<link href="/2018/07/16/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81/"/>
<url>/2018/07/16/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%8D%81/</url>
<content type="html"><![CDATA[<ol><li>Python用异常对象来表示异常情况。遇到错误后,会引发异常。如果异常对象并未被处理或捕捉,程序就会用所谓的回溯(Traceback)终止执行。</li><li>事实上,每个异常都是一些类的实例,这些实例可以被引发,并且可以用很多种方法进行捕捉,使得程序可以捉住错误并且对其进行处理,而不是让整个程序失败。</li><li><p>人为的引发异常,可以使用一个类(Exception的子类)或者实例参数调用raise语句。使用类时,程序会自动创建实例。內建的异常类都可以在exceptions模块中找到。(P128)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">raise Exception</div><div class="line">raise Exception('异常测试')</div></pre></td></tr></table></figure></li><li><p>如果没有捕捉异常,它就会被”传播”到调用的函数中。如果在那里依然没有捕获,这些异常就会”浮”到程序的最顶层。也就是说你可以捕捉到在其他人的函数中所引发的异常。</p></li><li><p>为了捕捉异常并且做出一些错误处理,可以使用try/except语句来实现,可以存在多个except子句。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">try:</div><div class="line"> '张三'/0</div><div class="line">except ZeroDivisionError:</div><div class="line"> print('不能整除0')</div><div class="line">except TypeError:</div><div class="line"> print('非数值')</div></pre></td></tr></table></figure></li><li><p>如果需要用一个块捕捉多个异常类型,那么可以将它们作为元组列出。(P131)</p></li><li><p>如果希望在except子句中访问异常对象本身,可以使用except errorname as instance子句。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">try:</div><div class="line"> raise Exception('异常对象')</div><div class="line">except Exception as e:</div><div class="line"> print(e)</div></pre></td></tr></table></figure></li><li><p>可以像条件语句和循环语句那样,给try/except语句加个else子句,如果没有异常则会执行else子句,否则并不执行。(P133)</p></li><li>finally子句,不管try子句中是否发生异常都会被执行。通常用于关闭文件或网络套接字。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(九)</title>
<link href="/2018/07/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B9%9D/"/>
<url>/2018/07/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B9%9D/</url>
<content type="html"><![CDATA[<p>在Python中可以使用class来创建一个自定义类。类的成员包括字段、方法以及属性。</p><h3 id="字段"><a href="#字段" class="headerlink" title="字段"></a>字段</h3><ol><li>字段包括:普通字段和静态字段,普通字段属于对象,静态字段属于类,两者的本质区别在于内存中保存的位置不同。<a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">class Person(object):</div><div class="line"> """docstring for Person"""</div><div class="line"> name="王五" #静态字段</div><div class="line"></div><div class="line"> def setAge(self,age):</div><div class="line"> self.age=age #普通字段</div><div class="line"></div><div class="line"> def getAge(self):</div><div class="line"> print(self.age)</div><div class="line">a=Person()</div><div class="line">print(Person.name) #访问静态字段</div><div class="line">a.setAge(25)</div><div class="line">print(a.age) #访问普通字段</div></pre></td></tr></table></figure></li></ol><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><ol><li>方法包括:普通方法、静态方法和类方法,三种方法在内存中都归属于类,区别在于调用方式不同。<ul><li>普通方法:由对象调用;至少一个self参数;执行普通方法时,自动将调用该方法的对象赋值给self;</li><li>类方法:由类调用;至少一个cls参数;执行类方法时,自动将调用该方法的类赋值给cls;</li><li>静态方法:由类调用;无默认参数;<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">class Person:</div><div class="line"> @staticmethod #静态方法</div><div class="line"> def SayHello():</div><div class="line"> print("Hello")</div><div class="line"></div><div class="line"> @classmethod #类方法</div><div class="line"> def SayNiHao(cls):</div><div class="line"> print("你好")</div><div class="line"></div><div class="line"> def SetName(self,name): #普通方法</div><div class="line"> self.name=name</div><div class="line"></div><div class="line"> def SayHi(self):</div><div class="line"> print(self.name)</div><div class="line"></div><div class="line">a=Person()</div><div class="line">a.SayHello()</div><div class="line">a.SayNiHao()</div><div class="line">a.SetName("张三")</div><div class="line">a.SayHi()</div></pre></td></tr></table></figure></li></ul></li></ol><h3 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h3><ol><li><p>属性可以看做是普通方法的变种。属性和字段的区别在于可以在属性内部进行一系列的逻辑运算,然后再将结果进行返回,属性的使用方式与字段的使用类似。属性有两种定义方式:</p><ul><li><p>装饰器 即在方法上应用装饰器,与属性相关的装饰器有@Property(获取相应方法的返回值)、@方法名.setter(将值赋值给相应方法的参数)、@方法名.deleter(执行相应方法)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"> class Person:</div><div class="line"> @property</div><div class="line"> def Name(self):</div><div class="line"> return self._Name</div><div class="line"> </div><div class="line"> @Name.setter</div><div class="line"> def Name(self,name):</div><div class="line"> if name=="张三":</div><div class="line"> self._Name="李四"</div><div class="line"> else:</div><div class="line"> self._Name=name</div><div class="line"></div><div class="line"> @Name.deleter</div><div class="line"> def Name(self):</div><div class="line"> print('李四')</div><div class="line"></div><div class="line">person=Person();</div><div class="line">del person.Name </div><div class="line">person.Name="王五"</div><div class="line">print(person.Name)</div></pre></td></tr></table></figure></li><li><p>静态字段 调用property函数,并将其值赋值给一个静态字段,property函数有四个参数,前三个参数与修饰符一一对应,最后一个参数为属性说明</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">class Person:</div><div class="line"> def getName(self):</div><div class="line"> return self._Name</div><div class="line"></div><div class="line"> def setName(self,name):</div><div class="line"> self._Name=name</div><div class="line"></div><div class="line"> def printName(self):</div><div class="line"> print(self._Name)</div><div class="line"></div><div class="line"> name=property(getName,setName,printName,'Name Property')</div><div class="line"></div><div class="line">a=Person()</div><div class="line">a.name="李四"</div><div class="line">b=a.name</div><div class="line">print(b)</div><div class="line">del a.name</div></pre></td></tr></table></figure></li></ul></li><li><p>在类中使用两个下划线来表示这是一个私有的属性,私有属性也涵盖类的私有属性和实例的私有属性,该私有属性其实还是能够被访问的,只是访问方式有些不同(约定大于配置)</p></li><li>Python是一门动态语言,任何实体都可以动态地添加或删除属性;一个类定义引用了一个作用域,一个类实例也引用了一个作用域,在类实例中查找属性的时候,首先在实例自己的作用域中查找,如果没有找到,则再在类定义的作用域中查找;在对类实例属性进行赋值的时候,实际上会在类实例定义的作用域中添加一个属性(如果不存在的话),并不会影响到相应类中定义的同名属性。</li><li><p>子类可以扩展超类的定义。将其他类名写在class语句后的圆括号内可以指定超类。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">class Person:</div><div class="line"> def sayHello(self):</div><div class="line"> print('你好,%s'%self.name)</div><div class="line"></div><div class="line">class Chinese(Person):</div><div class="line"> def setName(self,name):</div><div class="line"> self.name=name</div><div class="line"></div><div class="line"></div><div class="line">a=Chinese()</div><div class="line">a.setName("张三")</div><div class="line">a.sayHello()</div></pre></td></tr></table></figure></li><li><p>如果想要查看一个类是否是另一个的子类,可以使用內建的issubclass函数;如果想要知道已知类的基类,可以直接使用它的特殊特性<strong>bases</strong>;还能使用isinstance方法检查一个对象是否是一个类的实例;如果只想知道一个对象属于哪个类,可以使用<strong>class</strong>特性。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">#类型如上例</div><div class="line">a=Chinese()</div><div class="line">print(issubclass(Chinese,Person))</div><div class="line">print(isinstance(a,Person))</div><div class="line">print(isinstance(a,Chinese))</div><div class="line">print(Chinese.__base__)</div><div class="line">print(a.__class__)</div></pre></td></tr></table></figure></li><li><p>一个类可以同时扩展多个超类,也称为多重继承,多重继承应尽量避免使用。如果一个方法从多个超类继承,那么必须要注意超类的顺序(在class语句中):先继承的类中的方法会重写后继承的类中的方法。(P124)</p></li><li>使用hasattr方法可以判断一个对象是否存在某个方法,该方法返回一个布尔值;使用getattr方法与hasattr类似,只不过当未找到该方法时会将getattr中的第三个参数作为返回值进行返回。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(八)</title>
<link href="/2018/07/09/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%85%AB/"/>
<url>/2018/07/09/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%85%AB/</url>
<content type="html"><![CDATA[<ol><li>在Python中可以使用def语句来创建一个函数,使用內建的callable函数可以用来判断函数是否可调用。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">def sum(x,y):</div><div class="line"> print(x+y)</div><div class="line">print(callable(sum)) True</div></pre></td></tr></table></figure></li></ol><a id="more"></a><ol><li>在def语句后面以及在模块或者类的开头直接写上字符串,它就会作为函数的一部分进行存储,这称为文档字符串。使用函数名.<strong>doc</strong>或內建的help函数都可以查看当前函数的文档字符串。</li><li>位置参数:调用函数时根据函数定义的参数位置来传递参数,形参与实参必须一一对应。</li><li>关键字参数(命名参数):用于函数调用,通过”键-值”形式加以指定。可以让函数更加清晰、容易使用,同时也清楚了参数的顺序需求。有位置参数时,位置参数必须在关键字参数的前面,但关键字参数之间不存在先后顺序。</li><li>默认参数:用于定义函数,为参数提供默认值,调用函数时可传可不传该默认参数。所有位置参数都必须出现在默认参数前,包括函数定义和调用。</li><li><p>定义函数时,有时不确定调用的时候会传递多少个参数(不传参也可以)。此时,可用包裹(packing)位置参数,或者包裹关键字参数,来进行参数传递,会显得非常方便:</p><ul><li><p>包裹位置传递,使用*开头来声明一个形参即可,它会根据传进参数的位置合并为一个元组,即该参数的值是一个元组。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">def test(x,*tum):</div><div class="line">print(tum)</div><div class="line">test(1,3,5) (3,5)</div></pre></td></tr></table></figure></li><li><p>包裹关键字传递,使用**前缀,所有正常形参之外的其他的关键字参数都将放置在一个字典中传递给函数,即该参数的值是一个字典。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">def test(x,**dic):</div><div class="line"> print(dic)</div><div class="line">test(1,name="张三") {'name': '张三'}</div></pre></td></tr></table></figure></li></ul></li><li><p>单星和双星,也可以在函数调用的时候使用,称之为解包裹(unpacking)。在传递元组时,让元组的每一个元素对应一个位置参数;在传递字典时,让字典的每个键值对作为一个关键字参数传递给函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">def test(x,y):</div><div class="line"> print(x,y)</div><div class="line">a=(7,9)</div><div class="line">test(*a)</div><div class="line">def test(x,y):</div><div class="line"> print(x,y)</div><div class="line">a={"x":5,"y":6}</div><div class="line">test(**a)</div></pre></td></tr></table></figure></li><li><p>在Python程序中创建、改变或查找变量名时,都是在一个保存变量名的空间中进行,称之为作用域(命名空间)。Python的作用域是静态的,在源代码中变量名被赋值的位置决定了该变量能被访问的范围。即Python变量的作用域由变量所在源代码中的位置决定。</p></li><li>就作用域而言,Python与C有着很大的区别,在Python中并不是所有的语句块中都会产生作用域,只有当变量在Module(模块)、Class(类)、def(函数)中定义的时候,才会有作用域的概念。</li><li>在Python中,作用域的类型有四种(LEGB):<ul><li>L(local)局部作用域:包含在def关键字定义的语句块中,即在函数中定义的变量。每当函数被调用时都会创建一个新的局部作用域。在函数内部的变量声明,除非特别的声明为全局变量,否则均默认为局部变量。使用global关键字可以在函数内部定义全局变量。</li><li>E(enclosing)嵌套作用域:对于一个函数而言,L是定义在此函数内部的局部作用域,而E是定义在此函数的上一层父级函数的局部作用域。主要是为了实现Python的闭包。</li><li>G(global)全局作用域:即在模块层次中定义的变量,每一个模块都是一个全局作用域。</li><li>B(built-in)内置作用域:系统内固定模块里定义的变量,如预定义在builtin模块内的变量。</li></ul></li><li>搜索变量名的优先级:局部作用域>嵌套作用域>全局作用域>内置作用域,如果没有找到,会抛出NameError错误。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title></title>
<link href="/2018/06/22/SQL%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B8%80/"/>
<url>/2018/06/22/SQL%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E4%B8%80/</url>
<content type="html"><![CDATA[<a id="more"></a><ol><li>合并连接:合并连接要求两个输入都要在合并列上排序。由于每个输入都已排序,因此Merge Join运算符将从每个输入中获取一行进行比较,如果行相等则进行返回,不等则舍弃。当数据量不大的时候,这种连接方式比哈希匹配更加有效。</li><li>嵌套循环连接:嵌套循环也称”嵌套迭代”,他将一个联接输入用作外部输入表,将另一个联接输入用作内部输入表。外部逐行循环来处理外部输入表,内部逐行循环来处理内部输入表,内部循环会针对每个外部行执行,在内部输入表中搜索匹配的行。</li></ol>]]></content>
</entry>
<entry>
<title>Python程序设计(七)</title>
<link href="/2018/06/19/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%83/"/>
<url>/2018/06/19/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%83/</url>
<content type="html"><![CDATA[<ol><li>使用print可以同时打印多个表达式,表达式之间用逗号隔开就好,打印出来的结果中会在每个参数之间都插入了一个空格符。</li><li><p>从模块导入函数的时候除了之前说的导入方式之外,还可以给模块、函数起别名,以防止与其他模块中的函数冲突。</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> from math import sqrt as pingfanggen</div><div class="line">>>> a=pingfanggen(9)</div><div class="line">>>> print(a) #3</div></pre></td></tr></table></figure></li><li><p>除了简单的赋值操作外,多个赋值操作也可以同时进行(P67):</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> x,y,z=1,2,3</div><div class="line">>>> print(x,y,z) #1 2 3</div></pre></td></tr></table></figure><p> 这里所做的事情叫做序列解包————将多个值的序列解开,然后放到变量的序列中。当函数或者方法返回元组(或其他序列或可迭代对象)时,这个特性尤其有用(所解包的序列中的元素数量必须和放置在赋值符号左边的变量数量完全一致)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">>>> x={"name":"Stefan","gender":"male"}</div><div class="line">>>> a,b=x.popitem() #返回由键值对组成的一个元组</div><div class="line">>>> print(a) #key</div><div class="line">>>> print(b) #value</div></pre></td></tr></table></figure></li><li><p>链式赋值是将同一个值赋给多个变量的捷径</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> a=b="Stefan"</div><div class="line">>>> print(a) #Stefan</div><div class="line">>>> print(b) #Stefan</div></pre></td></tr></table></figure></li><li><p>将表达式运算符放置在赋值运算符的左边,写成+=,这种写法叫做增量赋值,对于*、/、%等标准运算符都适用。</p></li><li>语句块是在条件为真(条件语句)时执行或者执行多次(循环语句)的一组语句。在代码前放置空格来缩进语句即可创建语句块。在Python中,冒号(:)用来标示语句块的开始,块中的每个语句都是缩进的(缩进量相同)。当回退到和已经闭合的块一样的缩进量时,就表示当前块已经结束了。</li><li>标准值False和None、所有类型的数字0、空序列以及空的字典都为假。其他的一切都被解释为真,包括特殊值True。</li><li>在Python中比较运算和赋值运算一样是可以连接的————几个运算符可以连在一起使用,比如:<code>0<age<100</code>。</li><li>Python中的布尔运算符为and,or和not。</li><li><p>Python中的三元运算符:a if b else c,如果b为真,返回a,否则,返回c</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> a=5</div><div class="line">>>>b=10 if a==5 else 6</div><div class="line">>>>print(b) #10</div></pre></td></tr></table></figure></li><li><p>因为迭代某范围的数字是很常见的,所以有个內建的范围函数供使用range(类似于分片),该函数有三个参数分别是下限(包含)、上限(不包含)和步长。如果要迭代一个巨大的序列,使用xrange更高效。</p></li><li><p>for循环的一大好处就是可以在循环中使用序列解包,这对于字典而言很有用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>>a={"name":"stefan","gender":"male"}</div><div class="line">>>>for key,value in a.items():</div><div class="line">>>> print(key+"---"+value) #name---stefan gender---male</div></pre></td></tr></table></figure></li><li><p>使用內建的zip函数可以把两个序列”压缩”在一起,然后返回一个元组的列表。zip函数也可以作用于任意多的序列。同时它也可以应付不等长的序列,当其中最短的序列”用完”的时候就会停止。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>>a=zip(range(5),range(1000))</div><div class="line">>>>for b in a:</div><div class="line">>>> print(b)</div></pre></td></tr></table></figure></li><li><p>有些时候想要迭代序列的对象,同时还要获取当前对象的索引。此时,可以使用enumerate函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">>>>a=[1,3,5,7,9]</div><div class="line">>>>b=enumerate(a)</div><div class="line">>>>for c in b:</div><div class="line">>>> print(c)</div></pre></td></tr></table></figure></li><li><p>while True/break习语(P81)</p></li><li>在循环中增加一个else子句–它仅在没有调用break时执行(P82)。</li><li><p>推导式comprehensions(解析式),是Python的一种独有特性。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。共有三种推导:列表推导式、字典推导式和集合推导式(字典推导式和集合推导式只是将方括号修改为大括号,格式与列表推导式类似)。列表推导式的基本格式如下:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">#out_exp_res:列表生成元素的表达式,可以是有返回值的函数</div><div class="line">#for out_exp in input_list:迭代input_list将out_exp传入out_exp_res表达式中</div><div class="line">#if out_exp_filter:对input_list进行过滤</div><div class="line">#variable=[out_exp_res for out_exp in input_list if out_exp_filter]</div><div class="line">a=[i*i for i in range(1,6) if i<5]</div><div class="line">print(a) [1, 4, 9, 16]</div></pre></td></tr></table></figure></li><li><p>pass、del和exec语句(P84)</p></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(六)</title>
<link href="/2018/05/21/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%85%AD/"/>
<url>/2018/05/21/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%85%AD/</url>
<content type="html"><![CDATA[<p>通过名字引用值的数据结构被称为映射。字典是Python中唯一內建的映射类型。字典中的值并没有特殊的顺序,但是都存储在一个特定的键里。键可以是数字、字符串甚至是元组。<br><a id="more"></a></p><h3 id="创建和使用字典"><a href="#创建和使用字典" class="headerlink" title="创建和使用字典"></a>创建和使用字典</h3><ol><li>字典由多个键及其对应的值构成的对组成。每个键和它的值之间用冒号隔开,项之间用逗号隔开,而整个字典是由一对大括号括起来的。字典中的键是唯一的,而值并不唯一。</li><li>可以用dict函数,将其他映射或者(键,值)这样的序列对建立字典。</li><li>字典的基本行为在很多方面与序列类似:<ul><li>len(d)返回d中项的数量;</li><li>d[k]返回关联到键k上的值;</li><li>d[k]=v将值v关联到键k上;</li><li>del d[k]删除键为k的项;</li><li>k in d检查d中是否有含有键为k的项。</li></ul></li><li>字典和列表有很多特性相同,但也有一些重要的区别:<ul><li>字典的键不一定为整型数据,也可能是其他不可变类型,比如浮点型(实型)、字符串或者元组。</li><li>如果键起初在字典中不存在,也可以为它分配一个值。而不能将值关联到列表范围之外的索引上</li><li>表达式k in d 查找的是键,而不是值。</li></ul></li><li>在字典中检查键的成员资格比在列表中检查值的成员资格更高效。</li><li><p>类似于元组,可以使用字典来格式化字符串中的多个部分:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">a=[("name","Stefan"),("gender","male")];</div><div class="line">b=dict(a)</div><div class="line">print(b)</div><div class="line">c="i'm %(name)s"</div><div class="line">print(c%b)</div></pre></td></tr></table></figure></li><li><p>字典的一些常用方法(P59):</p><ul><li>clear方法,可以清空字典中所有的项。这是个原地操作,所以无返回值(或者说返回None)。</li><li>copy方法返回一个具有相同键值对的新字典(这个方法实现的是浅复制,因为值本身就是相同的,而不是副本)。与浅复制不同的是来自于copy模块下的deepcopy函数,该函数为深复制,复制其包含所有的值(使用递归)。</li><li>fromkeys方法使用给定的键建立新的字典,每个键默认对应的值为None。如果不想使用默认值,可以自己提供默认值作为第二个参数。</li><li>一般来说,如果试图访问字典中不存在的项时会出错,使用get方法则不会出现这个问题。当使用get访问一个不存在的键时,会得到None值,当然也可以使用自定义值来替换None值,如果键存在,get方法会返回相应项的值。</li><li>has_key方法可以检查字典中是否含有给出的键,相当于表达式 k in d(Python 3.0不包括该函数)</li><li>items方法将所有的字典项以列表方式返回,这些列表项中的每一项都来自于(键,值)。但是项在返回时并没有特殊的顺序;iteritems方法的作用大致相同,但是会返回一个迭代器对象而不是一个列表(Python3.0不包括该函数)。</li><li>keys方法将字典中的键以列表形式返回,而iterkeys则返回针对键的迭代器(同上)。</li><li>pop方法用来获得对应于给定键的值,然后将这个键值对从字典中移除。</li><li>popitem方法类似于list.pop,后者会弹出列表的最后一个元素。popitem会弹出随机的项,因为字典并没有”最后的元素”或者其他有关顺序的概念。尽管popitem和列表的pop方法很类似,但字典中没有与append等价的方法。</li><li>setdefault方法类似于get方法,不同之处在于,setdefault还能在字典中不含有给定键的情况下设定相应的键值。</li><li>update方法可以利用一个字典更新另外一个字典,提供的字典中的项会被添加到旧的字典中,若有相同的键则会进行覆盖。update方法可以使用与调用dict函数同样的方式进行调用。</li><li>values方法以列表的形式返回字典中的值(itervalues返回值的迭代器)</li></ul></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(五)</title>
<link href="/2018/04/13/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%BA%94/"/>
<url>/2018/04/13/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%BA%94/</url>
<content type="html"><![CDATA[<p>所有标准的序列操作(索引、分片、乘法、判断成员资格、求长度、取最小值和最大值)对字符串同样适用;唯一要注意的是字符串是不可变的。<br><a id="more"></a></p><h3 id="字符串格式化"><a href="#字符串格式化" class="headerlink" title="字符串格式化"></a>字符串格式化</h3><ol><li><p>字符串格式化使用字符串格式化操作符即百分号%来实现。在%的左侧放置一个字符串(格式化字符串),而右侧则放置希望格式化的值。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>>a="你好%s"</div><div class="line">>>>b="张三"</div><div class="line">>>>print(a%b)</div></pre></td></tr></table></figure><p> 格式化字符串的%s部分称为转换说明符,它们标记了需要插入转换值的位置。s表示值会被格式化为字符串————如果不是字符串,则会用str将其转换为字符串(P46)。</p></li><li>如果要在格式化字符串里面包括百分号,那么必须使用%%(其他同理)</li><li>string模块提供另外一种格式化值的方法:模板字符串(类似于其他模板)(P45)</li><li>如果希望格式化字符串中的多个部分时,可以使用元组或字典(如果使用列表或者其他序列代替元组,那么序列就会被解释为一个值。只有元组和字典可以格式化一个以上的值)。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">a="%s + %s = %s"</div><div class="line">b=(1,1,2)</div><div class="line">print(a%b)</div></pre></td></tr></table></figure></li></ol><h3 id="字符串方法"><a href="#字符串方法" class="headerlink" title="字符串方法"></a>字符串方法</h3><ol><li>尽管字符串方法完全来源于string模块,但是这个模块还包括一些不能作为字符串方法使用的常量和函数。下面是一些有用的字符串常量:<ul><li>string.digits :包含数字0-9的字符串</li><li>string.letters :包含所有字母(大写或小写)的字符串(python3.0与地区相关)</li><li>string.lowercase :包含所有小写字母的字符串(python3.0与地区相关)</li><li>string.printable :包含所有可打印字符的字符串</li><li>string.punctuation :包含所有标点的字符串</li><li>string.uppercase :包含所有大写字母的字符串(python3.0与地区相关)</li></ul></li><li>find方法可以在一个较长的字符串中查找子字符串。它返回子串所在位置的最左端索引。如果没有找到则返回-1。该方法还可以接受可选的起始点和结束点参数,由起始和终止值指定的范围包含第一个索引,但不包含第二个索引。这在Python中是个惯例。</li><li><p>join方法用于连接字符串序列。将字符串、元组、列表中的元素以指定的字符(分隔符)连接生成一个新的字符串。(split方法与此相反)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">>>>a=["张三","李四","王五"]</div><div class="line">>>>b="+"</div><div class="line">>>>c=b.join(a)</div><div class="line">>>>print(c)</div><div class="line">>>>print(c.split("+"))</div></pre></td></tr></table></figure></li><li><p>lower方法返回字符串的小写字母版,常用于不区分大小写的情况。(upper方法与此类似)</p></li><li>replace方法返回某字符串的所有匹配项均被替换后得到的字符串。</li><li><p>strip方法返回去除两侧(不包括内部)空格的字符串,也可以指定需要去除的字符,将它们列为参数即可。(lstrip、rstrip去除指定一侧的空格或字符)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>>a=" @123@ "</div><div class="line">>>>print(a.strip())</div><div class="line">>>>print(a.strip(" @"))</div></pre></td></tr></table></figure></li><li><p>translate方法和replace方法一样,可以替换字符串中的某些部分,但是和前者不同的是,translate方法只处理单个字符。它的优势在于可以同时进行多个替换,有时比replace效率高得多。在使用translate转换之前,需要先完成一张转换表,可以使用maketrans函数来生成该表。(python3.0,maketrans函数不再位于string模块)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>>trantable=str.maketrans('张王','赵李')</div><div class="line">>>>a="张三王四"</div><div class="line">>>>print(a.translate(trantable))</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(四)</title>
<link href="/2018/02/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%9B%9B/"/>
<url>/2018/02/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E5%9B%9B/</url>
<content type="html"><![CDATA[<p>列表不同于元组和字符串的地方:列表是可变的————可以改变列表的内容,并且列表有很多有用的、专门的方法。<br><a id="more"></a></p><h3 id="列表"><a href="#列表" class="headerlink" title="列表"></a>列表</h3><ol><li><p>因为字符串不能像列表一样被修改,所以有时根据字符串创建列表会很有用。list函数可以实现这个操作,当然,list函数适用于所有类型的序列,而不只是字符串。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> str='stefan'</div><div class="line">>>> print(list(str)) ['s', 't', 'e', 'f', 'a', 'n']</div></pre></td></tr></table></figure></li><li><p>通过元素赋值可以直接改变列表。(与其他语言类似)</p></li><li><p>通过del语句可以很容易的将元素从列表中删除。除了删除列表中的元素,del语句还能用于删除其他元素。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> str=[1,2,3]</div><div class="line">>>> del str[2]</div><div class="line">>>> print(str) [1, 2]</div></pre></td></tr></table></figure></li><li><p>分片是一个非常强大的特性,分片赋值操作则更加显现它的强大。分片赋值既可以替换列表中的某些元素,也可以在特定位置插入某些元素,甚至通过分片可以删除某些元素。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">>>> str=[1,2,3,4,5]</div><div class="line">>>> str[2:3]=[7] [1,2,7,4,5]</div><div class="line">>>> str[2:2]=[3] [1,2,3,7,4,5]</div><div class="line">>>> str[3:4]=[] [1,2,3,4,5]</div><div class="line">>>> print(str)</div></pre></td></tr></table></figure></li><li><p>列表提供了几个方法,用于检查或修改其中的内容</p><ul><li>append方法用于在列表末尾追加新的对象;</li><li>count方法统计某个元素在列表中出现的次数;</li><li>extend方法可以在列表的末尾一次性追加另一个序列中的多个值。换句话说,可以用新列表扩展原列表。类似于连接操作,但是extend方法会修改原列表,而连接操作会返回一个新列表;</li><li>index方法用于从列表中找出某个值第一个匹配项的索引位置,如果找不到该值,会抛出异常;</li><li>insert方法用于将对象插入到列表中的指定位置;</li><li>pop方法会移除列表中指定位置的一个元素(默认是最后一个),并且返回该元素的值</li><li>remove方法用于移除列表中某个值的第一个匹配项,同index方法,如果找不到该值,会抛出异常;</li><li>reverse方法将列表中的元素反向存放</li><li>sort方法用于在原位置对列表进行排序,也就是会改变原列表,并且不会有返回值。如果想要在不改变原列表的情况下进行排序,可以使用分片将原列表复制出来,再进行排序;也可以使用sorted函数来获取已排序的列表副本,这个函数适用于任何序列,却总返回一个列表。</li><li>如果希望元素能按照特定的方式进行排序(而不是sort函数默认的方式),可以为sort方法添加特定的参数。</li></ul></li></ol><h3 id="元组"><a href="#元组" class="headerlink" title="元组"></a>元组</h3><ol><li><p>如果用逗号分隔了一些值,那么就自动创建了元组。元组大部分时候是通过圆括号括起来的。如果要实现包含一个值的元组,也要加个逗号,只有圆括号是没用的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> str=1,2,3</div><div class="line">>>> print(str) (1, 2, 3)</div></pre></td></tr></table></figure></li><li><p>tuple函数的功能与list函数基本上是一样的:以一个序列作为参数并把它转换为元组</p></li><li>元组其实并不复杂————除了创建元组和访问元组元素之外,没有太多其他操作。虽然列表和元组很相似,但某些情况下只能使用元组,如作为映射中的键使用时。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(三)</title>
<link href="/2018/02/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%89/"/>
<url>/2018/02/12/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%89/</url>
<content type="html"><![CDATA[<ol><li>数据结构是通过某种方式(例如对元素进行编号)组织在一起的数据元素的集合,在Python中,最基本的数据结构是序列。序列中的每个元素被分配一个序号,即元素的位置,也称为索引。第一个索引是0,第二个则是1,以此类推。<a id="more"></a></li><li>Python包含6种內建的序列:列表、元组、字符串、Unicode字符串、buffer对象和xrange对象。列表和元组的主要区别在于,列表可以修改,元组则不能。</li><li>Python之中还有一种名为容器的数据结构。容器基本上是包含其他对象的任意对象。序列和映射是两类主要的容器。序列中的每个元素都有自己的序号,而映射中的每个元素则有一个名字(键)。既不是序列也不是映射的容器类型,集合算是一个例子。</li><li>所有序列类型都可以进行某些特定的操作。这些操作包括:索引、分片、迭代、加、乘以及检查某个元素是否属于序列的成员(成员资格)。除此之外,Python还有计算序列长度、找出最大元素和最小元素的內建函数。</li><li>序列中的所有元素都是有编号的————从0开始递增。这些元素可以通过编号分别访问。使用负数索引时,Python会从右边,也就是从最后一个元素开始计数。最后一个元素的位置编号是-1。</li><li><p>与使用索引来访问单个元素类似,可以使用分片操作来访问一定范围内的元素。分片通过冒号相隔的两个索引来实现。第一个索引的元素是包含在分片内的,而第二个则不包含在分片内。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3,4,5,6,7,8,9]</div><div class="line">>>> print(array[0:3]) [1,2,3]</div></pre></td></tr></table></figure><p>只要分片中最左边的索引比它右边的晚出现在序列中,结果就是一个空的序列。如果分片所得部分包括序列结尾的元素,那么,只需置空最后一个索引即可,这种方法同样适用于序列开始的元素,实际上,如果需要复制整个序列,可以将两个索引都置空。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3,4,5,6,7,8,9]</div><div class="line">>>> print(array[-3:-4]) []</div><div class="line">>>> print(array[:]) [1, 2, 3, 4, 5, 6, 7, 8, 9]</div></pre></td></tr></table></figure><p>进行分片的时候,分片的开始和结束需要进行指定(不管是直接还是间接)。而另外一个参数————步长,通常都是隐式设置的。在普通的分片中,步长是1————分片操作就是按照这个步长逐个遍历序列的元素,然后返回开始和结束点之间的所有元素。当然也可以显示设置步长,要注意步长为负数时的分片操作。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3,4,5,6,7,8,9]</div><div class="line">>>> print(array[::2]) [1, 3, 5, 7, 9]</div></pre></td></tr></table></figure></li><li><p>通过使用加号可以进行序列的连接操作,只有相同类型的序列才能进行连接操作</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3,4,5,6,7,8,9]</div><div class="line">>>> array1=['张三','李四']</div><div class="line">>>> print(array+array1) [1, 2, 3, 4, 5, 6, 7, 8, 9, '张三', '李四']</div></pre></td></tr></table></figure></li><li><p>用数字x乘以一个序列会生成一个新的序列,在新的序列中,原来的序列将被重复x次。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3]</div><div class="line">>>> print(array*3) [1, 2, 3, 1, 2, 3, 1, 2, 3]</div></pre></td></tr></table></figure><p>空列表可以简单的通过两个中括号进行表示([])。但是,如果想创建一个占用十个元素空间,却不包括任何有用内容的列表,可以使用<code>[None]*10</code>,None是Python的一个內建值,它的确切含意是”这里什么也没有”。</p></li><li><p>为了检查一个值是否在序列中,可以使用in运算符,该运算符检查某个条件是否为真,然后返回相应的布尔值。针对字符串序列而言,可以判断某个字符串是否在检查字符串中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> array=[1,2,3,4]</div><div class="line">>>> print(3 in array) True</div></pre></td></tr></table></figure></li><li><p>內建函数len、min和max也非常有用。len函数返回序列中所包含元素的数量,min函数和max函数则分别返回序列中最大和最小的元素。</p></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(二)</title>
<link href="/2018/02/11/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%BA%8C/"/>
<url>/2018/02/11/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%BA%8C/</url>
<content type="html"><![CDATA[<ol><li><p>在3.0版本之后,整除运算并不是使用一个<code>/</code>,一个<code>/</code>只代表普通的除法运算,两个<code>//</code>才是整除运算。</p><a id="more"></a><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> 4/3 1.3333333333333333</div><div class="line">>>> 4//3 1</div></pre></td></tr></table></figure></li><li><p><code>**</code>代表幂运算符,要注意的是幂运算符比取反(一元减运算符)的优先级要高,可以使用函数pow来代替幂运算符。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> -2**3 -8</div><div class="line">>>> pow(-2,3) -8</div></pre></td></tr></table></figure></li><li><p>对于比较大的数值,可以使用长整型.长整型的书写方法与普通整数一样,只是结尾有个L</p></li><li>表达式是某事,而语句是做某事。在Python交互式环境下,解释器总是把所有表达式的值打印出来。在Python3.0中print是函数。</li><li><p>像pow函数一样,有很多这样內建的函数用于数值表达式,如abs函数可以得到数的绝对值,round函数会将浮点数进行四舍五入。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> abs(-2) 2</div><div class="line">>>> round(3.6) 4</div></pre></td></tr></table></figure></li><li><p>Python支持复数,只不过操作复数的函数存在于cmath模块下,并非math模块</p></li><li>Python中的字符串也支持单双引号混用,换言之,字符串既可以用单引号括住,也可以用双引号括住。通过加号也支持字符串的拼接。</li><li><p>通过str函数,它会把值转换为合理形式的字符串,以便用户理解;而repr函数会创建一个字符串,它以合法的Python表达式的形式来表示值,repr函数的功能也可以用反引号来实现(仅限Python3.0以下版本)。注意,在Python中并不支持字符串和数字直接相加。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> repr("张三") '张三'</div><div class="line">>>> str("张三") 张三</div></pre></td></tr></table></figure></li><li><p>input函数会假设用户输入的都是合法的Python表达式,因此当用户输入不带引号的字符串时会报错。然而,要求用户带着引号输入他们的信息有点过分,因此,这就需要使用raw_input函数,它会把所有的输入当做原始数据,然后将其放入字符串中。(在3.0版本之后,不再有raw_input函数)</p></li><li><p>如果需要写一个跨多行的字符串(长字符串),那么可以使用三个引号代替普通引号;普通字符串也可以跨行,如果一行之中最后一个字符是反斜线,那么,换行符本身就”转义”了,也就是被忽略了,这个方法也适用于表达式和语句。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> 3+5+\</div><div class="line">...5 10</div></pre></td></tr></table></figure></li><li><p>原始字符串对于反斜线的使用并不会过分挑剔,也就是说原始字符串不会把反斜线当作特殊字符。在原始字符串中输入的每个字符都会与书写的方式保持一致,但不能在原始字符串的结尾输入反斜线,否则Python就不清楚是否应该结束字符串。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">>>> r'd:\a.py'</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>WCF核心技术(三)</title>
<link href="/2018/01/05/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%B8%89/"/>
<url>/2018/01/05/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%B8%89/</url>
<content type="html"><![CDATA[<h3 id="数据契约"><a href="#数据契约" class="headerlink" title="数据契约"></a>数据契约</h3><ol><li>在服务内部,应用的功能是由代码实现的;而在服务外部,功能则由WSDL定义。在WCF服务的内部,程序的数据表示为简单和复杂类型;而在服务外部,数据有XSD表示。WCF的数据契约提供了一种功能,在.NET的CLR类型与W3C组织定义的XSD之间,以及代码内部定义与服务外部通信之间,进行数据的映射。<a id="more"></a></li><li>在设计阶段,[DataContract]属性用于指明哪些类需要用XSD来表示,随服务的WSDL一起发布。而[DataMember]属性则进一步定义XSD的形式,指明哪些类成员要在外部表示中出现。运行时,DataContractSerializer类按照[DataContract]与[DataMember]属性所描述的规则,将对象序列化为XML。(P50)</li><li>默认情况下,XML Schema中元素的名称将与类名一致,而Schema目标命名空间则为 <a href="http://schemas.datacontract.org/2004/07/" target="_blank" rel="external">http://schemas.datacontract.org/2004/07/</a> 附上该类的.NET命名空间。与服务契约类似,这两者都可以被改写,通过改写它们,可以控制服务对外发布的各项名称。</li><li>代码中的复杂类型通常都是用类来实现的。更复杂一些的类,则通过继承的方式来定义越来越具体的构造。WCF支持在WSDL中表示类的层次结构,可以在类结构和XML间进行序列化和反序列化,并将各个类的属性保留下来。</li><li>在一些情况下,需要在WSDL契约之中强制包含某些类型。其中一种情况就是类的层次结构。比如,一个序列化的派生类被发送到端点,而端点则期望得到序列化的基类,WCF就无法知道如何反序列化这个类,因为这个派生类不是契约的一部分。另一种情况则是hashtable类,其中存储的元素是其他的类。WSDL中可以定义hashtable类,但无法定义hashtable中所包含的类。<br> 在这些情况下,WCF必须被告知哪些类需要显示地包含在WSDL契约中,利用KnownType属性可以完成这一功能。做法有4种:在[DataContract]中加入KnownType属性;在[ServiceContract]或[OperationContract]中加入该属性;在配置中加入对其的引用;在生成WSDL时指定该属性。其中后两种方法优于前两种。(P58)</li><li>无破坏更改和破坏性更改(P60)</li></ol><h3 id="消息契约"><a href="#消息契约" class="headerlink" title="消息契约"></a>消息契约</h3><ol><li>消息契约描述了从服务收发的SOAP消息的结构,并且可以令使用者检视与控制SOAP消息头和消息体中的大部分细节。数据契约通过XSD标准来达到互通性,消息契约则使得使用者可以与任何通过SOAP通信的系统互通。</li><li>使用消息契约可以直接访问SOAP的消息头和消息体,从而完全掌控服务所收发的SOAP消息。这样就可以用简单或复杂类型来精确定义SOAP各部分的内容。当需要完全掌控数据序列化时,可以从DataContractSerializer转用XmlSerializer;同理,当需要完全掌控SOAP消息时,可以从DataContracts转用MessageContracts。</li><li>当需要定义操作中未定义的”带外”通信时,非常有效的方法就是在SOAP头中传递这些信息。例如,会话或关联信息就可以在消息头中传递,除此之外还有安全信息等。这项技术的一个不利之处在于,客户端和服务都必须对SOAP头手工操作,从而注入和获取消息,而不能利用与数据以及操作契约关联的序列化类来处理。</li></ol>]]></content>
<tags>
<tag> WCF </tag>
</tags>
</entry>
<entry>
<title>Linux基本命令(二)</title>
<link href="/2017/11/28/Linux%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4-%E4%BA%8C/"/>
<url>/2017/11/28/Linux%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4-%E4%BA%8C/</url>
<content type="html"><![CDATA[<h3 id="文件属性和用户用户组"><a href="#文件属性和用户用户组" class="headerlink" title="文件属性和用户用户组"></a>文件属性和用户用户组</h3><ol><li>whoami:查看当前登录用户</li><li><p>chmod:修改文件的权限,用法有两种:</p><a id="more"></a><ul><li><p>文字设定法:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">chmod [who] [+|-|=] [mode] 文件名</div></pre></td></tr></table></figure><p>操作对象who可以是u(用户,user)、g(同组用户,gourp)、o(其他用户,others)、a(所有用户,all,系统默认值)<br>操作符号可以是+(添加某个权限)、-(取消某个权限)、=(赋予给定权限并取消其他所有权限)<br>设置mode所表示的权限可用下述字母的任意组合:r(可读)、w(可写)以及x(可执行)</p></li><li><p>数字设定发:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">chmod [mode] 文件名</div></pre></td></tr></table></figure><p>mode是由三个八进制数字组成,分别代表u、g、o的权限</p></li></ul></li><li><p>chown:修改文件或目录的属主和属组</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">chown [OPTION]...[OWNER:GROUP] FILE...</div></pre></td></tr></table></figure><p>当修改目录的属主和属组时,可以加上-R参数,用以递归更改目录下所有文件的属主和属组。注意该命令需要root权限,并且属主和属组之间没有实际关联</p></li><li>chgrp:修改指定文件所属的用户组,可用chown代替</li></ol><h3 id="查找与检索"><a href="#查找与检索" class="headerlink" title="查找与检索"></a>查找与检索</h3><ol><li><p>find:根据文件名查找</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">find [OPTION] path...[expression]</div></pre></td></tr></table></figure></li><li><p>grep:根据内容检索</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">grep [options] PATTERN [FILE...]</div></pre></td></tr></table></figure></li></ol><h3 id="安装卸载软件"><a href="#安装卸载软件" class="headerlink" title="安装卸载软件"></a>安装卸载软件</h3><ol><li>通过apt-get命令安装下载软件(该命令需要联网):<ul><li>更新源服务器列表,修改/etc/apt/sources.list文件</li><li>更新源,<code>sudo apt-get update</code></li><li>安装包,<code>sudo apt-get install package</code></li><li>删除包,<code>sudo apt-get remove package</code></li><li>搜索软件包,<code>sudo apt-cache search package</code></li></ul></li><li>通过deb包进行软件安装:<ul><li>安装deb软件包命令,<code>sudo dpkg -i xxx.deb</code></li><li>删除deb软件包命令,<code>sudo dpkg -r xxx.deb</code></li><li>查看系统中已安装软件包信息命令,<code>sudo dpkg -l</code></li></ul></li><li>通过源码安装:<ul><li>解压缩源代码包</li><li>进入该目录</li><li>执行<code>./configure</code>命令,用于检测文件是否缺失,创建Makefile,检测编译环境</li><li>执行<code>make</code>命令,编译源码,生成库和可执行程序</li><li>执行<code>sudo make install</code>,把库和可执行程序,安装到系统路径下</li></ul></li></ol><h3 id="磁盘管理"><a href="#磁盘管理" class="headerlink" title="磁盘管理"></a>磁盘管理</h3><ol><li>挂载U盘的步骤:<ul><li>检测存储设备名称,<code>sudo fdisk -l</code></li><li>挂载存储设备到挂载点/mnt目录,<code>sudo mount /dev/sdb1 /mnt</code></li><li>访问/mnt</li><li>卸载/mnt,<code>sudo umount /mnt</code><br>注意:块设备必须进行挂载,否则不能进行访问其内部文件;可以挂载到任何目录,但是挂载后,该目录原有的内容将暂时不可见。</li></ul></li><li>dd:用于拷贝操作</li></ol><h3 id="压缩包管理"><a href="#压缩包管理" class="headerlink" title="压缩包管理"></a>压缩包管理</h3><ol><li><p>tar压缩,主要命令如下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">tar cvf dir.tar dir 创建档案文件(备份文件)</div><div class="line">tar xcf dir.tar dir 释放档案文件(备份文件)</div><div class="line">tar zcvf dir.tar.gz dir --用gzip来解压缩文件</div><div class="line">tar zxvf dir.tar.gz 在解压时可加上-C参数来解压到指定文件夹下</div><div class="line">tar jcvf dir tar.bz2 dir --用bzip2来解压缩文件</div><div class="line">tar jxvf dir.tar.bz2 在解压时可加上-C参数来解压到指定文件夹下</div></pre></td></tr></table></figure></li><li><p>rar压缩,主要命令如下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">rar a -r newdir dir 打包</div><div class="line">unrar x newdir.rar 解包</div></pre></td></tr></table></figure></li><li><p>zip压缩,主要命令如下</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">zip -r dir.zip dir 打包</div><div class="line">unzip dir.zip 解包</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> Linux </tag>
</tags>
</entry>
<entry>
<title>Linux基本命令(一)</title>
<link href="/2017/11/26/Linux%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4-%E4%B8%80/"/>
<url>/2017/11/26/Linux%E5%9F%BA%E6%9C%AC%E5%91%BD%E4%BB%A4-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>shell即命令解释器,根据输入的命令执行相应命令。shell的种类有很多,bash是许多Linux平台的内定shell。<br><a id="more"></a></p><h3 id="基本操作"><a href="#基本操作" class="headerlink" title="基本操作"></a>基本操作</h3><ol><li><p>在bash下敲命令时,Tab键可以补全已经敲了一部分的文件名和目录名,也可以补全命令的某些参数、Makefile目标等等(如果补全内容有所相似,按Tab键后会显示出所有相似内容供选择)。在Ubuntu系统下,系统默认启用了bash completion,如果是Debian系统,可以用以下命令启用bash completion:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ source /etc/bash_completion</div></pre></td></tr></table></figure></li><li><p>shell有历史记录功能,按上下移动光标键(或者<code>Ctrl-p</code>、<code>Ctrl-n</code>)可以一条一条浏览以前输过的命令。通过按<code>Ctrl-r</code>,然后输入关键字,可以查找含有关键字的历史记录,随着每输入一个字母,bash会做增量式查找,也可以反复按<code>Ctrl-r</code>或<code>Ctrl-s</code>向前向后查找。</p></li><li>主键盘快捷键(尽量不要使用移动光标键和编辑键)<br>| 功能 | 快捷键 | 助记 |<br>|———–+———–+——————|<br>| 上 | Ctrl-p | previous |<br>| 下 | Ctrl-n | next |<br>| 左 | Ctrl-b | backward |<br>| 右 | Ctrl-f | forward |<br>| Del | Ctrl-d | delete光标后面的 |<br>| Home | Ctrl-a | the first letter |<br>| End | Ctrl-e | end |<br>| Backspace | Backspace | delete光标前面的 |<h3 id="目录和文件"><a href="#目录和文件" class="headerlink" title="目录和文件"></a>目录和文件</h3></li><li><p>类Unix系统没有盘符这个概念,只有一个根目录/,所有文件都在它下面</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">/ 根目录</div><div class="line"> bin //系统可执行程序,如命令</div><div class="line"> boot //内核和启动程序,所有和启动相关的文件都保存在这里</div><div class="line"> grub //引导器相关文件</div><div class="line"> dev //设备文件</div><div class="line"> etc //系统软件的启动和配置文件,系统在启动过程中需要读取的文件都在这个目录。如LILO参数、用户账户和密码。</div><div class="line"> home //用户的主目录。下面是自己定义的用户名的文件夹</div><div class="line"> lib //系统程序库文件,这个目录里存放着系统最基本的动态链接共享库,类似于Windows下的system32目录,几乎所有的应用程序都需要用到这些共享库。</div><div class="line"> media //挂载媒体设备,如光驱、U盘等</div><div class="line"> mnt //目录是让用户临时挂载别的文件系统,如挂载Windows下的某个分区,ubuntu默认还是挂载在/media目录。</div><div class="line"> opt //可选的应用软件包(很少使用)</div><div class="line"> proc //这个目录是系统内存的映射,我们可以直接访问这个目录来获取系统信息。也就是说,这个目录的内容不在硬盘上而是在内存里。</div><div class="line"> sbin //管理员系统程序</div><div class="line"> selinux</div><div class="line"> srv</div><div class="line"> sys //udev用到的设备目录树,/sys反映你机器当前所接的设备</div><div class="line"> tmp //临时文件夹</div><div class="line"> usr //这是个最庞大的目录,我们要用到的很多应用程序和文件几乎都存放在这个目录下。</div><div class="line"> bin // 应用程序</div><div class="line"> game //游戏程序</div><div class="line"> include</div><div class="line"> lib //应用程序的库文件</div><div class="line"> lib64</div><div class="line"> local //包含用户程序等</div><div class="line"> sbin //管理员应用程序</div></pre></td></tr></table></figure></li><li><p>用户目录位于/home/user,也被称之为家目录,表示方式:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">/home/user</div><div class="line">~</div></pre></td></tr></table></figure></li><li><p>每个目录下都有.和..,其中.表示当前目录,..表示上一级目录,即父目录</p></li><li><p>ls是英文单词list的简写,其功能是列出目录的内容。该命令类似于DOS下的dir命令。对于每个目录,该命令将列出其中的所有子目录与文件。对于每个文件,ls将输出其文件名以及所要求的其他信息。主要的参数有:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">-a 列出隐藏文件,文件中以"."开头的均为隐藏文件</div><div class="line">-l 列出文件的详细信息</div><div class="line">-R 连同子目录中的内容一起列出</div></pre></td></tr></table></figure></li><li><p>用ls -l命令显示的信息中,开头由10个字符构成的字符串,其中第一个字符表示文件类型,它可以是下述类型之一:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">- 普通文件</div><div class="line">d 目录</div><div class="line">l 符号链接</div><div class="line">b 块设备文件</div><div class="line">c 字符设备文件</div><div class="line">s socket文件,网络套接字</div><div class="line">p 管道</div></pre></td></tr></table></figure><p>后面的9个字符表示文件的访问权限,分为3组,每组3位。第一组表示文件属主的权限,第二组表示同组用户的权限,第三组表示其他用户的权限。每组的三个字符分别表示对文件的读、写、和执行权限。各权限如下所示:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">r 读</div><div class="line">w 写</div><div class="line">x 可执行。对于目录,表示进入权限</div><div class="line">- 没有相应位置的权限</div><div class="line">其余还有s和t</div></pre></td></tr></table></figure><p>访问权限后面的数字表示与该文件共享inode的文件组数,即硬链接数</p></li><li>cd 改变当前所在路径(change dir)</li><li>which 查看指定命令所在路径</li><li>pwd 查看当前所在路径</li><li>mkdir 创建目录,后跟参数以及目录名,可以一次创建多个。参数如果是-p,表示可以连同父目录一起创建。</li><li>rmdir 删除空目录,后跟参数以及目录名,可以一次删除多个,注意只能删除空目录。参数如果是-p,表示可以连同空的父目录一起删除。</li><li>touch 如果文件不存在,则创建一个字节数为0的文件。否则将每个文件的访问及修改时间都更新为目前的时间。</li><li>rm 删除文件,如果是删除非空目录则使用<code>rm dir -rf</code></li><li>mv 在同文件夹下使用表示重命名,不同文件夹下使用表示移动文件</li><li>cp 拷贝文件;拷贝目录,可在其后追加参数-r</li><li>cat 将文件内容输出到终端,如果cat后没跟文件名,则读标准输入,遇到\n后,输出到标准输出,终端下输入<code>Ctrl-d</code>表示结束</li><li>more 查看文本文件的内容,屏幕显示完一屏就等待用户按下任意键再滚动到下一屏,按<code>Ctrl-C</code>可以终止显示</li><li>less 与more命令类似,只不过可以向上或向下查看</li><li>head 显示指定文件的前面几行,默认显示前10行,使用方式<code>head -5 a.txt</code></li><li>tail 显示指定文件的最后几行,与head使用方法类似</li><li>ln 链接有两种,一种被称为硬链接,另一种被称为符号链接。建立硬链接时,链接文件和被链接文件必须位于同一文件系统中,并且不能建立指向目录的硬链接。而对符号链接,则不存在这个问题。ln默认产生硬链接,加上-s则建立符号链接。</li><li><p>tree 按结构树的形状显示目录和文件,该命令需要下载安装</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">sudo apt-get install tree</div></pre></td></tr></table></figure></li><li><p>du 查看目录的大小;df 查看磁盘使用情况</p></li></ol>]]></content>
<tags>
<tag> Linux </tag>
</tags>
</entry>
<entry>
<title>WCF核心技术(二)</title>
<link href="/2017/11/16/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%BA%8C/"/>
<url>/2017/11/16/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%BA%8C/</url>
<content type="html"><![CDATA[<p>现实世界中,契约代表着两方或多方之间一份有约束力的协议,它指定按照明确的价格提供货物或服务。在网络世界中,契约有类似的功能:它是两方或多方之间的协议,指定可以交换的消息,以及这些消息的期限和条件。<br><a id="more"></a><br>端点必须指定其实现的功能操作以及期望的数据格式。这些规格加在一起,就构成了契约。在WCF中,有以下三种形式的契约:</p><ul><li>服务契约:服务契约描述服务所实现的功能操作。服务契约将.NET类型中的类方法(class method)映射到WSDL中的服务、端口类型及操作,而服务契约中的操作契约则描述了服务操作,即实现服务功能的函数方法。</li><li>数据契约:数据契约描述服务与客户端通信时使用的数据结构。数据契约将CLR数据类型映射到XML模式定义(XML Schema Definition,XSD),并定义如何对其进行序列化和反序列化。数据契约描述了所有从服务操作发送或接收的数据。</li><li>消息契约:消息契约将CLR数据类型映射成SOAP消息,描述SOAP消息的格式,并影响这些消息的WSDL和XSD定义。消息契约可以精确地控制SOAP信头和信体。<br>为了能够与尽量多的系统互通,通常使用Web服务描述语言(Web Service Description Language,WSDL)来表示契约,WSDL是一个基于XML的语言用于描述web service及其函数、参数和返回值。WSDL文档可分为两部分。顶部分由抽象定义组成,而底部分则由具体描述组成。<br>由于契约是在WSDL和XSD中定义的,而代码通常使用CLR类型,所以在系统中需要对两种类型进行映射。</li></ul><h3 id="服务契约"><a href="#服务契约" class="headerlink" title="服务契约"></a>服务契约</h3><ol><li>服务契约描述了一个服务端点所实现的操作接口。服务契约引用消息格式,并描述如何对其进行交换。消息格式则由数据契约与消息契约进一步描述。</li><li>WCF将消息契约用于设计阶段和运行时。在设计阶段,消息契约主要用于识别代码中需要发布为WSDL中端点的类。代码中被标记为[ServiceContract]的类,以及该类中被标记为[OperationContract]的方法,都将在WSDL中发布,以用于客户端访问。该类将被识别为wsdl:service,而方法则被识别为wsdl:operation;在运行时,WCF分发器收到一则消息,就会查看wsdl:operation的名称,以此判别要将反序列化后的消息发送给哪一个标记有[OperationContract]的类方法。</li><li>由客户端发往服务端点的SOAP消息,其中有以下几点需要注意:<ul><li>SOAP消息的默认命名空间是<a href="http://tempuri.org/" target="_blank" rel="external"> http://tempuri.org/</a>,可以在[ServiceContract]特性里将此默认定义覆盖。如果准备向外界发布服务,而不是仅在应用内部或小范围内使用的话,最好将默认命名空间覆盖。因为命名空间是用来唯一标识服务的构件,这样可以避免在多个服务结合使用时发生混淆。</li><li>类中定义的方法名,在SOAP消息头部分中被用于构成wsa:Action元素。完整的action取值应该是契约命名空间、契约名(如果没有使用显示的服务接口,应为接口名或类名)、操作名组合而成的;如果消息是对应的相应接口,还要加上额外的字符串”Response”。</li><li>SOAP消息体的内容,由方法的调用格式以及在[OperationContract]与[DataContract]属性中指定的限定前缀决定。</li><li>SOAP消息头含有消息发送的目的地址。</li></ul></li><li>良好的设计会尽量避免用户不得不等待任务完成才能启动下个任务的情形,这种多任务功能是通过一种异步设计模式来完成的。用SvcUtil工具生成代码时,在高级选项中打开/async选项,则在同步方法之外,还会为每个服务操作生成异步方法。</li><li>要注意的是服务端并不知道客户端在使用异步编程;服务端契约仅仅指定了使用请求-响应通信,而客户端实现异步模式时并不需要服务器端的参与。</li><li>单向消息交换模式,常用于客户端需要向服务发送消息却不需要回应的情况下。通过这种模式,客户端仅仅需要得到消息被成功发送的通知,而不需要服务真正返回一个响应消息。单向操作可以通过在[OperationContract]属性中设置IsOneWay=true修饰符来指定。当客户端调用服务中的一个单向方法时,控制权在服务操作结束之前就已经被返还给调用者。</li><li>双向消息交换模式,按照建立客户端到服务的会话所使用的绑定方式,WCF会建立一或两条信道来实现双工消息模式。支持双向通信的协议,如命名管道与TCP,只需要一个信道。而无法支持双向通信的协议,如http,WCF则会建立额外的信道来进行服务端反向到客户端的通信。对于预定义的名称中含有”dual”字样的WCF绑定,已经实现了双信道。</li><li>双工操作时,会存在一些可靠性方面的问题。例如,如果服务无法调用客户端的回调操作,则仅仅会在控制台上输出日志信息,但从不重试。服务是否应该重试呢?如果重试,应该多久重试一次,又何时停止?或者,当预先知道客户端在一个时间段内无法接收更新时,这些信息应该存储在哪里以便稍后发送?要解决这一系列重要问题,可以使用消息中间件,如BizTalk。</li><li>使用svcutil.exe或”添加服务引用”功能,可以生成客户端代理。代理定义的接口名称,是由服务名后附加”Callback”组成的。如果服务契约中的接口是IStockService,则客户端接口就是IStockServiceCallback。客户端必须实现一个由此接口派生的类。(P43)</li><li>端点与契约之间是多对一的关系,一个端点只可以具有一个契约,但一个契约可以被多个端点引用。然而,尽管端点只能指定一个契约,接口聚合功能却使得单一契约可以发布多个接口。此外,如果多个端点使用不同的契约,但使用了相同的绑定,则可以位于同一地址,这也造成了单个端点可以实现多个契约的假象。</li></ol>]]></content>
<tags>
<tag> WCF </tag>
</tags>
</entry>
<entry>
<title>WCF核心技术(一)</title>
<link href="/2017/11/13/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%B8%80/"/>
<url>/2017/11/13/WCF%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AF-%E4%B8%80/</url>
<content type="html"><![CDATA[<h3 id="WCF基础"><a href="#WCF基础" class="headerlink" title="WCF基础"></a>WCF基础</h3><ol><li>就一般消费者的应用而言,REST(Representational State Transfer,即表述性状态传递)是主流的Web服务接口。REST将HTTP和一个优秀的URI模式进行组合,用来访问基于XML的数据。使用REST的数据维护操作通常与CRUD模式一一映射,REST协议本身也很简洁。<a id="more"></a>对企业应用而言,SOAP(Simple Object Access Protocol,即简单对象访问协议)是主流的Web服务接口,它提供了更加健壮的协议来交换复杂数据。SOAP消息包括信封(envelope)和正文(body),能对其进行加密并安全地在英特网上路由。如果消息是一个逻辑会话或事务的一部分,其消息携带的信封中将放置相应的语义。如果要保证信息安全,可对消息正文加密,同时在信封中存放安全信息。SOAP消息具有强类型的特征,因此容易被开发者试用。与REST相似,SOAP消息主要以文本方式编码,通过HTTP传输。</li><li>WCF具有一个內建的托管模型,能让服务托管于IIS或Windows的Managed Services环境中。WCF中还提供了丰富的线程和负载控制模型,使服务实例能被轻松控制。无论是定义单例服务还是多线程服务来处理并发请求,编程模型都保持一致,并将开发者与细节隔离(但并未隔绝)。</li><li>WCF支持各种消息交换模式,如请求-应答、单向和双工。WCF还支持对等网–利用啮合网络与寻址,客户端能在没有中心控制机制的情况下找到彼此并相互通信。</li><li>服务是一组向客户端提供可用功能的端点(endpoints)。而端点则是网络上的一个能将消息送达的资源。客户端按照与服务之间的契约(contract)来格式化消息,并将消息发送给端点来访问端点的功能。服务在端点指定的地址(address)上监听具有特定格式的送达消息。</li><li>客户端要和服务进行通信,需要了解ABC:地址(address)、绑定(binding)与契约(contract)。<ul><li>“A”是地址,意味着在哪里(where)。地址定义的是网络消息送达之处,即端点接收消息之处,客户端必须将消息送到此处。</li><li>“B”是绑定,意味着怎么做(how)。绑定定义的是与端点通信的信道。信道是一个所有WCF应用程序传递消息的管道。信道包括一系列绑定元素(binding elements)。最底层的绑定元素是传输(transport),它负责在网络上传递消息。内置的传输包括HTTP、TCP、命名管道(Named Pipes)、PeerChannel和MSMQ。在此之上的绑定元素规定安全(security)和事务(transactions)。幸运的是,WCF中包含了系统提供的绑定,其信道已配置安排就绪,使用绑定能节省考虑配置的时间。</li><li>“C”是契约,意味着内容(what),它定义端点提供的功能或功能集合。契约定义了端点对外发布的操作(operation)以及这些操作所要求的消息格式。契约的操作映射到实现端点的类方法(class methods),包括其输入、输出参数的签名(the signature of parameters)。</li></ul></li><li>WCF中的元数据指的是精确描述与服务通信的方法的信息。客户端可以向运行中的服务请求元数据,以了解服务的端点及端点所要求的消息格式。在设计时,客户端发送由WS-MetadataExchange标准定义的请求消息,接收返回的WSDL。客户端可以利用此WSDL来定义代理类和配置文件,随后在运行时用它们与服务进行通信。</li><li>默认情况下,WCF服务并不暴露元数据交换(MEX)端点,这意味着无法查询出如何与服务进行通信,但WCF能轻松暴露MEX端点,使客户端正确与服务通信。MEX端点可以在代码或配置文件中暴露出来。</li></ol>]]></content>
<tags>
<tag> WCF </tag>
</tags>
</entry>
<entry>
<title>生产环境概述</title>
<link href="/2017/11/09/%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E6%A6%82%E8%BF%B0/"/>
<url>/2017/11/09/%E7%94%9F%E4%BA%A7%E7%8E%AF%E5%A2%83%E6%A6%82%E8%BF%B0/</url>
<content type="html"><![CDATA[<p>生产环境下,有时会存在很大的用户量,在一个服务器中处理这些用户信息是非常困难的,因此出现了诸如Web Farm、Load Balancer、Wbe Garden等概念。<br><a id="more"></a></p><h3 id="应用程序池"><a href="#应用程序池" class="headerlink" title="应用程序池"></a>应用程序池</h3><p>在生产环境中部署应用程序最重要的部分之一就是应用程序池,应用程序池用来分离一系列的IIS 工作进程(共享同样的配置)。应用程序池允许我们隔离我们的应用程序为了更好的安全性、可靠性和可用性,工作进程用作分离每个应用程序池的进程边界,以便当一个工作进程或应用程序出现问题或被回收时,其他应用程序或工作进程不受影响。<br>应用程序池身份配置是IIS 6.0和7.0中一个重要的与安全相关的环节,因为它决定了当工作进程访问资源时工作进程的身份。在IIS 7.0中,有三种预定义的身份类型,IIS 6.0中同样也有。</p><ul><li>LocalSystem:在服务器上有管理员权限的内置账号。该身份能够既能访问本地资源,也能访问远程资源。如果想要访问任何类型的服务器文件或资源,可以将应用程序池的身份设置为LocalSystem。</li><li>LocalServices:该帐户的计算机访问权限比“网络服务”帐户少,并且该帐户的用户权限仅限于本地计算机</li><li>NetworkServices:应用程序池默认的身份类型。该帐户的系统访问权限比“本地系统”帐户少,但仍能通过网络与计算机帐户的凭据进行交互。</li></ul><h3 id="Web-Garden"><a href="#Web-Garden" class="headerlink" title="Web Garden"></a>Web Garden</h3><p>默认情况下,每个应用程序池只运行单独的工作进程(W3Wp.exe)。当然在一个应用程序池中也可以分配多个工作进程,这种我们称之为Web Garden。多个工作进程共享一个应用程序池有时会提升性能以及缩短响应时间。其中的每个工作进程有自己的线程和内存空间。<br>Web Garden的主要优点在于:Web Garden中的工作进程共享针对特定应用程序池的请求。 如果工作进程失败,另一个工作进程可以继续处理请求。<br>要注意的是在Web Garden中不能使用InProc模式下的Session,否则Session并不能在多个工作进程中共享。</p><h3 id="Web-Farm和Load-Balancer"><a href="#Web-Farm和Load-Balancer" class="headerlink" title="Web Farm和Load Balancer"></a>Web Farm和Load Balancer</h3><p>这是在生产环境下最常用的术语。经常在使用多台服务器部署应用程序时出现,使用这些技术的原因在于我们必须分发负载到多个服务器上,负载均衡专门用来做这个事情。<br>针对Web Farm和Load Balancer,Session用户状态和数据的读写是其需要处理的问题之一,首先不能使用InProc模式,其次如果使用StateServer模式的话,需要所有服务器的machinekey相同,对于SQLServer模式,并没有什么需要注意的地方。</p>]]></content>
<tags>
<tag> .net </tag>
</tags>
</entry>
<entry>
<title>Session详情</title>
<link href="/2017/11/08/Session%E8%AF%A6%E6%83%85/"/>
<url>/2017/11/08/Session%E8%AF%A6%E6%83%85/</url>
<content type="html"><![CDATA[<h3 id="Session基础"><a href="#Session基础" class="headerlink" title="Session基础"></a>Session基础</h3><ol><li>Session提供了一种便利的方式来解决HTTP无状态问题,它将数据存储在服务器端,并且支持存储任何类型的数据;对于不同的客户端,Session是分开存储的,这也就意味着Session是基于客户端来进行数据存储的。<a id="more"></a></li><li>Session优点:<ul><li>维护用户状态和数据贯穿整个应用程序</li><li>容易实现并可以存储任何类型的数据</li><li>针对不同客户端数据分开存储</li><li>对用户而言Session是安全透明的<br>Session缺点:</li><li>在数据或用户过多时造成较大的性能开销,因为Session存储在服务器内存中</li><li>Session数据的序列化和反序列化需要一定的开销,因为在StateServer和SQLServer Session模式下,存储和检索对象之前需要先序列化或反序列化对象。</li></ul></li><li>ASP.NET使用一个120位识别码来跟踪每个Session,这是足够安全且不会被反向工程的。当客户端与服务器端进行通信时,仅仅有一个Session ID在两者之间进行传递。这个过程中主要由三个部分在起作用:Client(客户端)、Server(服务器端)、Session State Provider(Session状态提供器,即用于存储Session状态的地方)。</li></ol><h3 id="Session模式和状态提供器"><a href="#Session模式和状态提供器" class="headerlink" title="Session模式和状态提供器"></a>Session模式和状态提供器</h3><ol><li>在ASP.NET中,支持以下四种Session模式:InProc、StateServer、SQLServer、Custom。每种模式使用不同的状态提供器,分别是:In-memory object、Aspnet_state.exe、Database、Custom provider。除此之外,还有一种模式为Off,选择该模式则意味着应用程序将禁用Session。</li><li>Session状态本质上是针对应用程序维护Session所做的一系列设置,或者是在web.config中,或者是在code-behind中。在web.config中<sessionstate>节点用于Session配置,其中包括Mode,Timeout,StateConnectionString,CustomProvider等特性。</sessionstate></li><li>在ASP.NET中有两个与Session相关的事件:Session_Start和Session_End,这两个事件均可以在全局文件中进行注册。</li></ol><h4 id="InProc"><a href="#InProc" class="headerlink" title="InProc"></a>InProc</h4><ul><li>在ASP.NET中InProc是默认的Session模式,它将信息存储在当前应用程序域中。对应用程序的性能而言是最好的一种Session模式。但是缺点也很明显,如果重启服务器,它将丢失数据。同时,该模式也是唯一一种可以触发Session_End事件的模式。</li><li>模式优点:<ul><li>将Session数据存储到应用程序域中的一个内存对象中,因此访问速度快且易于获取</li><li>不需要序列化和反序列化操作</li><li>易于实现,和ViewState操作类似<br>模式缺点:</li><li>服务器重启,数据丢失</li><li>Session用户状态或数据过多时影响性能,由于内存使用</li><li>不能在web graden场景下使用</li><li>不适用于web farm场景</li></ul></li></ul><h4 id="StateServer"><a href="#StateServer" class="headerlink" title="StateServer"></a>StateServer</h4><ul><li>StateServer模式又被称为Out-Proc模式。StateServer用一个单独的Windows服务(独立于IIS并且能够运行在一个分离的服务器上)。这种Session状态是由aspnet_state.exe管理的。由于其独立性,也就意味着如果ASP.NET应用程序重启,Session仍然存在并不会丢失。</li><li>aspnet_state.exe作为一个Windows服务来运行,可以通过Windows管理器或命令行提示符(<code>net start aspnet_state</code>)来开启该服务,默认该服务的启动模式为手动启动,监听的TCP端口号是42424,可以通过修改注册表来改变其监听端口号。</li><li>模式优点:<ul><li>Session用户状态或数据与IIS分离,因此IIS的任何问题不会影响Session数据</li><li>适用于web graden、web farm、负载均衡等场景<br>模式缺点:</li><li>处理过程由于序列化和反序列化导致比较慢</li><li>aspnet_state.exe需要一直处于运行状态</li></ul></li></ul><h4 id="SQLServer"><a href="#SQLServer" class="headerlink" title="SQLServer"></a>SQLServer</h4><ul><li>在ASP.NET中SQLServer模式提供了一种更加安全可靠的Session管理方式,在这种模式下,Session数据被序列化后存储在一个SQL Server数据库中</li><li>该模式的具体实现方式参照(<a href="https://www.codeproject.com/Articles/32545/Exploring-Session-in-ASP-Net" target="_blank" rel="external">https://www.codeproject.com/Articles/32545/Exploring-Session-in-ASP-Net</a>)</li><li>模式优点:<ul><li>如果重启IIS,Session用户状态或数据不受影响</li><li>更加安全可靠的Session管理方式</li><li>使数据中心化,有利于其他应用程序访问</li><li>适用于web graden、web farm、负载均衡等场景<br>模式缺点:</li><li>处理过程比较慢</li><li>操作Session用户状态或数据时需要进行序列化或反序列化</li><li>由于Session用户状态或数据由一个不同的服务器处理,因此必须确保SQL Server数据库一直处于运行状态</li></ul></li></ul><h4 id="Custom-参照上述网址"><a href="#Custom-参照上述网址" class="headerlink" title="Custom(参照上述网址)"></a>Custom(参照上述网址)</h4>]]></content>
<tags>
<tag> .net </tag>
</tags>
</entry>
<entry>
<title>异步编程模式</title>
<link href="/2017/09/07/%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%BC%8F/"/>
<url>/2017/09/07/%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%BC%8F/</url>
<content type="html"><![CDATA[<p>从.NET 4.5开始,支持三种异步编程模式:<br> a. 异步编程模型(APM,Asynchronous Programming Model)<br> b. 基于事件的异步编程设计模式(EAP,Event-based Asynchronous Pattern)<br> c. 基于任务的异步编程设计模式(TAP,Task-based Asynchronous Pattern)<br><a id="more"></a></p><h3 id="APM"><a href="#APM" class="headerlink" title="APM"></a>APM</h3><ol><li>在.NET 1.0开始微软就对异步编程做了相应的支持–即异步编程模型(APM)</li><li>异步编程模型是一种模式,该模式允许使用更少的线程去做更多的操作,.NET Framework很多类实现了该模式,同时也可以自定义类来实现该模式(在自定义类中实现返回类型为IAsyncResult接口的BeginXXX方法和EndXXX方法),另外委托类型也定义了BeginInvoke和EndInvoke方法。</li><li>通过调用BeginXXX方法会开启一个异步操作,并且该方法返回的都是一个实现了IAsyncResult接口的对象,此时我们需要调用对应的EndXXX方法来结束异步操作,并向该方法传递之前的IAsyncResult对象。</li><li>对于访问异步操作的结果,APM提供了四种方式供开发人员选择:<ul><li>在调用BeginXXX方法的线程上调用EndXXX方法来得到异步操作的结果,但是这种方式会阻塞调用线程,直到操作完成之后调用线程才继续执行</li><li>查询IAsyncResult的AyncWaitHandle属性,从而得到WaitHandle,然后再调用它的WaitOne方法来使一个线程阻塞并等待操作完成后再调用EndXXX方法来获得操作的结果。</li><li>循环查询IAsyncResult的IsComplete属性,操作完成后再调用EndXXX方法来获得操作返回的结果。</li><li>使用AsyncCallback委托来指定操作完成时要调用的方法,在操作完成后调用的方法中调用EndXXX操作来获得异步操作的结果。(首选,其余均会阻塞调用线程)</li></ul></li><li>从上述介绍中可以看出,要识别某个类是否实现了异步编程模型,只需要看是不是有BeginXXX方法(当然返回类型需要是IAsyncResult)和EndXXX方法。其实异步编程模型这个模式,就是微软利用委托和线程池帮助我们实现的一个模式(包括后面的基于事件的异步编程和基于任务的异步编程,还有C#5中的async和await关键字,都是利用委托和线程池实现的。他们的本质是一样的,只是后面提出的使异步编程更加简单罢了)。</li><li>在GUI应用程序(包括Windows窗体,WPF和Silverlight)中,创建窗口的线程是唯一能够对那个窗口进行更新的线程,所以在执行异步方法的线程就不能对窗口中的控件进行操作,也就不能把方法允许的结果反应到窗体上。这里有两种解决方案<ul><li>设置控件的CheckForIllegalCrossThreadCalls属性为false,设置为false的意思是代表允许跨线程调用,这种方式虽然可以解决该问题,但是不推荐,因为它违背了.NET安全规范</li><li>使用SynchronizationContext基类,该类记录着线程的同步上下文对象,可以在GUI线程中调用SynchronizationContext.Current属性来获得GUI线程的同步上下文,然后当线程池线程需要更新窗体时,可以调用保存的SynchronizationConetext派生对象的Post方法(Post方法会将回调函数送到GUI线程的队列中,每个线程都有各自的操作队列的,线程的执行都是从这个队列中拿方法去执行),向Post方法传递要由GUI线程调用的方法(该方法的定义要匹配SendOrPostCallback委托的签名),还需要向Post方法传递一个要传给回调方法的参数。</li></ul></li><li>虽然APM为我们实现异步编程提供了一定的支持,同时它也存在着一些明显的问题–不支持对异步操作的取消和没有提供对进度报告的功能,对于GUI应用程序而言,进度报告和取消操作的支持是必不可少的。</li></ol><h3 id="EAP"><a href="#EAP" class="headerlink" title="EAP"></a>EAP</h3><ol><li>针对APM存在的问题,微软在.NET 2.0的时候为我们提供了一个新的异步编程模型,即基于事件的异步编程模型。(EAP)</li><li>实现了基于事件的异步模式的类,将具有一个或多个以Async为后缀的方法和对应的Completed事件,并且这些类都支持异步方法的取消、进度报告和报告结果。并非所有支持APM的类都支持EAP。</li><li>当我们调用实现基于事件的异步模式的类的XXXAsync方法时,即代表开始了一个异步操作,该方法调用完之后会使一个线程池线程去执行耗时的操作,基于事件的异步模式是建立在APM的基础之上的。</li></ol><h3 id="典型例子"><a href="#典型例子" class="headerlink" title="典型例子"></a>典型例子</h3><p>实现了APM的典型类有FileStream,实现了EAP的典型组件有BackgroundWorker</p>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>任务并行库(三)</title>
<link href="/2017/08/28/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%B8%89/"/>
<url>/2017/08/28/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%B8%89/</url>
<content type="html"><![CDATA[<h3 id="其他特性"><a href="#其他特性" class="headerlink" title="其他特性"></a>其他特性</h3><ol><li>可以通过Task.Status属性来获悉任务的状态信息,该属性返回System.Threading.Tasks.TaskStatus枚举类型的一个值。(P43)</li><li>通过结合Lazy变量以及Task<>.Factory.StartNew()方法可以实现任务按需加载。(P44)<a id="more"></a></li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">Task.Factory.StartNew(() => { Console.WriteLine("任务被执行了1"); });</div><div class="line">Lazy<Task<string>> lazy = new Lazy<Task<string>>(() => Task<string>.Factory.StartNew(() =></div><div class="line">{</div><div class="line"> Console.WriteLine("任务被执行了2");</div><div class="line"> return "按需加载";</div><div class="line">}));</div><div class="line">Thread.Sleep(5000);</div><div class="line">Console.WriteLine("还没有执行任务");</div><div class="line">Console.WriteLine(lazy.Value.Result);</div></pre></td></tr></table></figure><ol><li>死锁是指两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。在多任务中同样如此,避免该问题最好的方法是确保任务不会依赖于另外一个任务。(P45)</li><li><p>当在for循环中创建一系列的任务并在lambda表达式中引用了循环变量时,lambda表达式中循环变量的值始终为循环变量最后一个的值,即出现了闭包问题。解决此问题的方式有两种,一种是通过在循环内部创建一个局部变量,lambda表达式引用该变量。另一种是将循环变量作为任务的方式(输入参数)传递给lambda表达式。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"> List<Task<int>> funcs = new List<Task<int>>();</div><div class="line">for (int i = 0; i < 5; i++)</div><div class="line">{</div><div class="line"> funcs.Add(</div><div class="line"> new Task<int>(</div><div class="line"> (x) => (int)x</div><div class="line"> ,i)</div><div class="line"> );</div><div class="line">}</div><div class="line"></div><div class="line">foreach (Task<int> i in funcs)</div><div class="line">{</div><div class="line"> i.Start();</div><div class="line"> Console.WriteLine(i.Result);</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>尽量少的使用Thread.SpinWait()方法或者通过代码循环来达到阻塞的目的,这样会降低并行编程的性能,如果真的要使用CPU循环的方式来进行阻塞,优先使用Therad.SpinWait()方法。</p></li></ol>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>任务并行库(二)</title>
<link href="/2017/08/10/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%BA%8C/"/>
<url>/2017/08/10/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%BA%8C/</url>
<content type="html"><![CDATA[<h3 id="任务阻塞"><a href="#任务阻塞" class="headerlink" title="任务阻塞"></a>任务阻塞</h3><ol><li><p>让一个任务阻塞一段时间有时是非常有用的,当指定的时间到了,任务就会被唤醒,然后继续执行,任务阻塞的方式有:</p><a id="more"></a><ul><li><p>使用CancellationToken对象的阻塞处理程序。调用CancellationToken对象的WaitHandle属性的WaitOne()方法的一个重载,该重载接收一个整型或时间戳参数来表示阻塞的时长,一旦指定了阻塞时长,WaitOne()方法将使任务阻塞,直到指定时长结束或CancellationToken被取消,任务会被再次唤醒。WaitOne()方法会返回一个布尔值,如果CancellationToken被取消导致的任务唤醒则返回true,如果指定时长结束导致的任务唤醒则返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">bool cancelled = token.WaitHandle.WaitOne(10000);</div></pre></td></tr></table></figure></li><li><p>由于TPL内部实现是由经典.NET线程作支撑,所以能够用经典线程技术使得一个任务阻塞。调用静态的Thread.Sleep()方法,并传递一个时间间隔作为参数即可。该方法会阻塞任务执行直到指定时长结束,并不会由于CancellationToken被取消而停止阻塞并继续任务的执行</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Thread.Sleep(10000);</div></pre></td></tr></table></figure></li><li><p>使用Thread.SpinWait()方法也可以阻塞任务的执行,但是不推荐使用该方法,因为它并会让任务调度器执行其他任务当任务阻塞时,该方法会使用空循环CPU次数的方式来阻塞任务。(P29)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Thread.SpinWait(10000);</div></pre></td></tr></table></figure></li></ul></li><li><p>除了常用的通过时间来阻塞任务以为,还可以通过其他一些属性或方法来阻塞任务,如下:</p><ul><li>Task.Result,当任务获取到结果后解除阻塞</li><li>Task.Wait(),有很多重载方法。这些重载一般会在任务完成、任务取消、抛出异常、指定时长结束或指定CancellationToken被取消后解除阻塞。</li><li>Task.WaitAll(),也有许多重载方法。使用方式与Task.Wait()方法类似,只不过该方法是静态方法并且可以指定多个任务共同达到某些条件后才解除阻塞。</li><li>Task.WaitAny(),也有很多重载。所有重载都会接收一个任务数组,数组中任意一个任务满足某些条件后就会解除阻塞,并且该方法会返回一个导致阻塞解除的任务的数组索引号。如果该方法返回-1则标志着是由于指定时长结束或指定CancellationToken被取消导致的阻塞解除。</li></ul></li></ol><h3 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h3><ol><li>任何由任务抛出的异常都被.NET Framework存储起来,直到调用一个触发成员,例如Task.Wait(),Task.WaitAll(),Task.WaitAny()或者Task.Result,触发成员将抛出System.AggregateException类型的一个实例。</li><li>AggregateException类型为多个异常提供了一个封装,对于类似WaitAll()这样需要多个任务协作并有可能抛出多个异常的方法而言是非常有用的。同时这个特性对于任务链来说也是非常有用的。AggregateException总是由触发成员来进行抛出,就算只有一个异常被抛出也是一样。<br>为了获取被聚集的异常,可以通过读取AggregateException实例的InnerExceptions属性,该属性返回一个能够枚举的异常集合。<br>这种处理异常方法的缺点在于异常与抛出它们的任务之间没有明显的关联。</li><li><p>通常来说,应该将预料到的异常和未预料到并需要继续抛出的异常加以区分,AggregateException类提供了一个Handel()方法,该方法将指定一个方法委托用于在每个异常上调用。如果异常是你能够处理的则方法委托应该返回true,否则应该返回false。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">try {</div><div class="line"> Task.WaitAll(task1, task2);</div><div class="line">} catch (AggregateException ex) {</div><div class="line">// iterate through the inner exceptions using</div><div class="line">// the handle method</div><div class="line"> ex.Handle((inner) => {</div><div class="line"> if (inner is OperationCanceledException) {</div><div class="line"> // ...handle task cancellation...</div><div class="line"> return true;</div><div class="line"> } else {</div><div class="line"> // this is an exception we don't know how</div><div class="line"> // to handle, so return false</div><div class="line"> return false;</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>捕获异常的另外一个办法是使用Task类的属性,特别是IsCompleted,IsFaulted,IsCancelled和Exception属性。虽然当调用触发方法时仍需要捕获AggregateException异常,但是可以使用属性来获知任务是否完成,是否抛出异常,是否取消以及如果抛出异常,异常的具体细节。</p></li><li>如果不想使用上述任何一种方法,可以重写传播策略并且提供自己的代码在异常传播时调用。通过为静态的System.Threading.Tasks.TaskScheduler.UnobservedTaskException成员注册一个事件处理程序来实现该功能。(P41)<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">// create the new escalation policy</div><div class="line">TaskScheduler.UnobservedTaskException +=</div><div class="line">(object sender, UnobservedTaskExceptionEventArgs eventArgs) =></div><div class="line"> {</div><div class="line"> // mark the exception as being handled</div><div class="line"> eventArgs.SetObserved();</div><div class="line"> // get the aggregate exception and process the contents</div><div class="line"> ((AggregateException)eventArgs.Exception).Handle(ex => {</div><div class="line"> // write the type of the exception to the console</div><div class="line"> Console.WriteLine("Exception type: {0}", ex.GetType());</div><div class="line"> return true;</div><div class="line"> });</div><div class="line">};</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>jQuery插件编写</title>
<link href="/2017/08/08/JQuery%E6%8F%92%E4%BB%B6%E7%BC%96%E5%86%99/"/>
<url>/2017/08/08/JQuery%E6%8F%92%E4%BB%B6%E7%BC%96%E5%86%99/</url>
<content type="html"><![CDATA[<p>当需要在一个选择器上调用一个方法,就可以执行一系列操作时,可以使用jQuery插件。</p><ol><li>当使用$符号去选择元素时,它会返回一个包含所有你能够使用的方法(<code>.css()</code>,<code>.click()</code>,etc.)和匹配选择器的所有元素的一个jQuery对象。该对象从$.fn对象中获取这些方法。$.fn对象包含了所有的jQuery对象方法,如果我们想写自己的方法,也应该将其包含在$.fn对象中。<a id="more"></a></li><li><p>假设我们想要创建一个使检索到的元素的文字变成绿色的插件,所有我们要做的就是为$.fn添加一个自定义方法greenify,它将能够像其它jQuery对象方法一样被使用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$.fn.greenify=function () {</div><div class="line"> this.css('color','green');</div><div class="line">}</div><div class="line">$( "a" ).greenify();//使得所有链接颜色变成绿色</div></pre></td></tr></table></figure><p>在使用css()方法时,用的是this对象,而不是$(this)对象,因为greenify()方法和css()方法属于同一个对象。</p></li><li><p>jQuery的一个特性是支持链式编程,你可以在一个选择器上链接五或六个jQuery对象方法,这是通过所有的jQuery对象方法最后又返回了起初的jQuery对象来实现的(除了少数只获取值的jQuery对象方法不支持链式)。因此可以做如下操作,使得插件支持链式编程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$.fn.greenify=function () {</div><div class="line"> return this.css('color','green');</div><div class="line">}</div><div class="line">$( "a" ).greenify().addClass( "greenified" );</div></pre></td></tr></table></figure></li><li><p>$符号在JavaScript库之间是非常流行的,如果既使用了其他库,又使用了jQuery,则需要用jQuery.noConflict()来让jQuery不再使用$符号。然而插件的编写使用的是$符号,为了使其可用,可以将其放在一个模拟的块级作用域中。如下所示</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">(function ( $ ) {</div><div class="line"> </div><div class="line"> $.fn.greenify = function() {</div><div class="line"> this.css( "color", "green" );</div><div class="line"> return this;</div><div class="line"> };</div><div class="line"> </div><div class="line">}( jQuery ));</div></pre></td></tr></table></figure></li><li><p>典型的jQuery对象往往含有任意数量DOM元素的引用,这也就是为什么jQuery对象经常引用的是一个集合。如果想要操作某些特定的元素可以使用each()方法来循环整个集合</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$.fn.myNewPlugin = function() {</div><div class="line"> return this.each(function() {</div><div class="line"> // Do something to each element here.</div><div class="line"> });</div><div class="line">};</div></pre></td></tr></table></figure><p>我们返回的是each()方法的结果而不是this。因为each()方法将返回this,相比之前这是维护链式能力的更好的方式。</p></li><li><p>随着插件变得越来越复杂,通过接收可选项来让用户自定义插件也变得尤为重要,最简单的方式是通过接收一个对象字面量来维护可选项。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">(function ( $ ) {</div><div class="line"> $.fn.greenify = function( options ) {</div><div class="line"> //如果可选项没有值,则使用默认值,如果可选项有值,则使用可选项的值,同时会改变默认值的值。</div><div class="line"> var settings = $.extend({</div><div class="line"> // These are the defaults.</div><div class="line"> color: "#556b2f",</div><div class="line"> backgroundColor: "white"</div><div class="line"> }, options );</div><div class="line"> return this.css({</div><div class="line"> color: settings.color,</div><div class="line"> backgroundColor: settings.backgroundColor</div><div class="line"> });</div><div class="line"> };</div><div class="line">}( jQuery ));</div><div class="line"></div><div class="line">$( "div" ).greenify({</div><div class="line"> color: "orange"</div><div class="line">});</div></pre></td></tr></table></figure></li><li><p>对于默认值而言,最好的做法是将其暴露给用户,使得用户可以用尽量少的代码来自定义插件。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$.fn.hilight = function( options ) {</div><div class="line"> //第一个参数为空对象,目的是防止重写默认值。</div><div class="line"> var opts = $.extend( {}, $.fn.hilight.defaults, options );</div><div class="line"> </div><div class="line"> // Our plugin implementation code goes here.</div><div class="line"> </div><div class="line">};</div><div class="line">// 插件的默认值,作为插件方法的一个属性来进行使用。</div><div class="line">$.fn.hilight.defaults = {</div><div class="line"> foreground: "red",</div><div class="line"> background: "yellow"</div><div class="line">};</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> jQuery </tag>
</tags>
</entry>
<entry>
<title>任务并行库(一)</title>
<link href="/2017/08/04/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%B8%80/"/>
<url>/2017/08/04/%E4%BB%BB%E5%8A%A1%E5%B9%B6%E8%A1%8C%E5%BA%93-%E4%B8%80/</url>
<content type="html"><![CDATA[<h3 id="Task基础"><a href="#Task基础" class="headerlink" title="Task基础"></a>Task基础</h3><ol><li>System.Threading.Tasks命名空间中包含了并行编程的许多关键类,System.Threading命名空间主要包含了.Net经典线程操作类,同时也会含有一些用于协调几个任务之间工作的类。<a id="more"></a></li><li>执行一个简单任务的主要流程:创建一个Task类型的实例,并将需要执行的代码以委托的形式传递给Task构造函数,一旦拥有Task的实例,调用Start()方法,任务将被传递给任务调度器(用于分配线程来执行任务)中。</li><li>使用Task.Factory.StartNew()方法创建并开始一个任务和使用普通的Task构造函数创建任务区别并不大,对于简单且生命周期较短的任务推荐使用Task.Factory.StartNew()方法。</li><li>任务并不能复用,一旦一个Task实例在运行中,就不能再调用其Start()方法,只能重新创建另一个具有相同功能的Task实例。</li><li>通过传递Action类型的委托,可以为Task提供方式(即输入参数),设置Task方式可以使得用不同的数据完成相同的任务,注意方式默认类型是Object类型,若要在任务体中使用,需要显示转化该类型。</li><li>创建Task<t>类型的实例,可以使任务拥有返回结果,泛型T即为返回类型,通过读取Task<t>实例的Result属性可以获取任务的结果。读取Result属性时会阻塞当前线程直到该任务完成。同样Task.Factory也包含StartNew<t>方法。</t></t></t></li><li>Task构造函数中有些重载允许指定一个TaskCreationOptions枚举类型的值,该枚举有以下成员:<ul><li>None:默认任务创建选项</li><li>PreferFairness:告知任务调度器尽可能公平的调度该任务</li><li>LongRunning:告知任务调度器该任务将长时间运行</li><li>AttachedToParent:指定作为任务层次中某个任务的子任务</li></ul></li><li>Task.CurrentId属性返回一个用于标识当前任务的整形值,在任务体外读取该属性返回null值。</li></ol><h3 id="Task取消"><a href="#Task取消" class="headerlink" title="Task取消"></a>Task取消</h3><p>TPL中一个新的标准化区域是任务的取消。新的方法使得并行编程变得更简单且更有组织性,同时也减少了取消一个任务时经常遇到一些问题的风险。 </p><ul><li>创建一个能够取消的任务,需要四个基本步骤<ul><li>创建System.Threading.CancellationTokenSource的一个实例</li><li>调用CancellationTokenSource.Token属性来获取一个System.Threading.CancellationToken</li><li>创建一个任务并将CancellationToken作为构造函数的一个参数传进去</li><li>以正常的方式调用任务的Start()方法<br>取消一个任务,简单的调用CancellationTokenSource的Cancel方法即可。但是任务的取消是协作式的,也就意味着.Net Framework不会自动终止你的任务,你必须监测CancellationToken对象直到一个取消被请求,然后再停止你的任务。</li></ul></li><li><p>许多任务体包含循环迭代处理数据,可以在循环迭代中轮询CancellationToken的IsCancellationRequest属性来判断任务是否被取消。如果属性返回true,则需要打破循环并释放所使用的资源,同时,应该在任务体中抛出System.Threading.OperationCanceledException的一个实例,该步骤是为了确认取消了任务。如果并没有什么资源需要去释放,可以简化代码直接调用CancellationToken.ThrowIfCancellationRequest()方法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">while (true) {</div><div class="line"> if (token.IsCancellationRequested) {</div><div class="line"> // tidy up and release resources</div><div class="line"> throw new OperationCanceledException(token);</div><div class="line"> } else {</div><div class="line"> // do a unit of work</div><div class="line"> }</div><div class="line">}</div><div class="line">while (true) {</div><div class="line"> token.ThrowIfCancellationRequested();</div><div class="line"> // do a unit of work</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>向CancellationToken注册一个委托,当CancellationTokenSource.Cancel()方法被调用时会执行该委托,当任务依赖于其他异步操作时,该方法是很有用的,同时也能够使用该方法来通知一个取消被请求。</p></li><li>第三种监听任务取消的方式是调用CancellationToken.WaitHandle属性的WaitOne()方法。当调用WatiOne()方法时会阻塞当前线程,直到CancellationTokenSource的Cancel()方法被调用。</li><li>多个任务可以使用同一个CancellationToken,这样的话调用CancellationTokenSource.Cancel()方法会同时取消这些任务。</li><li>能够创建一个由少数CancellationToken共同组成的CancellationToken,一旦任何一个内在的CancellationToken被取消,该CancellationToken将被取消。通过调用System.Threading.CancellationTokenSource.CreateLinkedTokenSource()方法并传递少数CancellationToken能够返回一个依赖于这些Token的新CancellationToken对象,注意该方法为静态方法。</li><li>通过检查Task.IsCancelled属性可以确定一个任务是否被取消,如果任务被取消,则该属性返回true。</li></ul>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>替换Cursor方案</title>
<link href="/2017/07/05/%E6%9B%BF%E6%8D%A2Cursor%E6%96%B9%E6%A1%88/"/>
<url>/2017/07/05/%E6%9B%BF%E6%8D%A2Cursor%E6%96%B9%E6%A1%88/</url>
<content type="html"><![CDATA[<h3 id="基于集合操作-set-based"><a href="#基于集合操作-set-based" class="headerlink" title="基于集合操作(set-based)"></a>基于集合操作(set-based)</h3><p>游标每次操作一行数据然而SQL Server对于基于集合的操作是有所优化的(在一个操作中影响多行)。因此基于集合的操作通常比游标操作更快。</p><ol><li>插入,使用SELECT语句代替VALUES语句<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">INSERT INTO [USER](name,age) SELECT '张三',12</div></pre></td></tr></table></figure></li></ol><a id="more"></a><ol><li><p>修改或删除,当基于不同的表中的值来修改或删除当前表时,使用Join语句</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">UPDATE t SET t.name='王麻子' FROM [dbo].[User] t JOIN dbo.Test t1 ON t.id=t1.id</div><div class="line">DELETE t FROM Test t JOIN [user] t1 ON t.id=t1.id WHERE t1.age=13</div></pre></td></tr></table></figure></li><li><p>判断数据在表中是否存已在(存在更新不存在添加),使用Merge语句<br>Merge关键字在SQL Server2008被引入,它能将Insert,Update,Delete简单的并为一句。MSDN对于Merge的解释为:”根据与源表联结的结果,对目标表执行插入、更新或删除操作”,主要用于数据同步,数据转换,基于源表对目标表做CUD操作等</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">MERGE INTO [dbo].[User] AS target --目标表</div><div class="line">USING dbo.Test AS source --源表</div><div class="line">ON target.id=source.id --两者之间的关联点</div><div class="line">WHEN MATCHED THEN UPDATE SET target.name='天山童姥' --匹配操作</div><div class="line">WHEN NOT MATCHED THEN INSERT VALUES('婴儿','666',NULL,source.id,NULL) --不匹配操作</div><div class="line">WHEN NOT MATCHED BY SOURCE THEN DELETE; --不匹配且在源数据中不存在则删除</div></pre></td></tr></table></figure></li></ol><p>### </p>]]></content>
<tags>
<tag> T-SQL </tag>
</tags>
</entry>
<entry>
<title>OAuth2.0(一)</title>
<link href="/2017/06/28/OAuth2-0-%E4%B8%80/"/>
<url>/2017/06/28/OAuth2-0-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>OAuth是一个关于授权的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。</p><h3 id="名词定义"><a href="#名词定义" class="headerlink" title="名词定义"></a>名词定义</h3><ul><li>Third-party application:第三方应用程序(客户端)</li><li>Http service:HTTP服务提供商<a id="more"></a></li><li>Resource Owner:资源所有者(用户)</li><li>User Agent:用户代理(浏览器)</li><li>Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器</li><li>Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同服务器<br>从上述名词,可以得出OAuth的作用就是让”客户端”安全可控地获取”用户”的授权,与”服务提供商”进行互动。</li></ul><h3 id="OAuth思路"><a href="#OAuth思路" class="headerlink" title="OAuth思路"></a>OAuth思路</h3><p>OAuth在”客户端”与”服务提供商”之间,设置了一个授权层。”客户端”不能直接登录”服务提供商”,只能登录授权层,以此将用户与客户端区分开来。”客户端”登录授权层所用的令牌,与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。<br>“客户端”登录授权层以后,”服务提供商”根据令牌的权限范围和有效期,向”客户端”开发用户存储的资料。</p><h3 id="运行流程"><a href="#运行流程" class="headerlink" title="运行流程"></a>运行流程</h3><ol><li>用户打开客户端后,客户端要求用户给予授权。</li><li>用户同意给予客户端授权。(关键操作)</li><li>客户端使用上一步获得的授权,向认证服务器申请令牌。</li><li>认证服务器对客户端进行认证后,确认无误,同意发放令牌。</li><li>客户端使用令牌,向资源服务器申请获取资源。</li><li>资源服务器确认令牌无误,同意向客户端开放资源。</li></ol><h3 id="客户端授权模式"><a href="#客户端授权模式" class="headerlink" title="客户端授权模式"></a>客户端授权模式</h3><p>客户端必须得到用户的授权,才能获得令牌。OAuth 2.0定义了四种授权方式。</p><ul><li>授权码模式(authorization code)</li><li>简化模式(implicit)</li><li>密码模式(resource owner password credentials)</li><li>客户端模式(client credentials)</li></ul><h3 id="授权码模式"><a href="#授权码模式" class="headerlink" title="授权码模式"></a>授权码模式</h3><p>授权码模式是功能最完整、流程最严密的授权模式。它的特点是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。步骤如下:</p><ol><li><p>用户访问客户端,后者将其导向认证服务器。客户端申请认证的URI,包含以下参数:</p><ul><li>response_type:表示授权类型,必选项,此处的值固定为”code”</li><li>client_id:表示客户端的ID,必选项</li><li>redirect_uri:表示重定向URI(URI编码下),可选项</li><li>scope:表示申请的权限范围,可选项</li><li>state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz</div><div class="line"> &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb</div></pre></td></tr></table></figure></li></ul></li><li><p>用户选择是否给予客户端授权。</p></li><li><p>假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码,服务器回应客户端的URI,包含以下参数:</p><ul><li>code:授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI是一一对应的。</li><li>state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA</div><div class="line"> &state=xyz</div></pre></td></tr></table></figure></li></ul></li><li><p>客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌,这一步是在客户端的后台服务器上完成的,对用户不可见,客户端向认证服务器申请令牌的HTTP请求(注意请求方式),包含以下参数:</p><ul><li>grant_type:表示使用的授权模式,必选项,此处的值固定为”authorization_code”。</li><li>code:表示上一步获得的授权码,必选项。</li><li>redirect_uri:表示重定向URI,必选项,且必须与第一步中的该参数值保持一致。</li><li>client_id:表示客户端ID,必选项。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"> grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA</div><div class="line">&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb</div></pre></td></tr></table></figure></li></ul></li><li><p>认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh toke),认证服务器发送的HTTP回复(注意Content-Type),包含以下参数:</p><ul><li>access_token:表示访问令牌,必选项。</li><li>token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。</li><li>expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。</li><li>refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。</li><li>scope:表示权限方位,如果与客户端申请的范围一致,此项可省略。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> "access_token":"2YotnFZFEjr1zCsicMWpAA",</div><div class="line"> "token_type":"example",</div><div class="line"> "expires_in":3600,</div><div class="line"> "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",</div><div class="line"> "example_parameter":"example_value"</div><div class="line"> }</div></pre></td></tr></table></figure></li></ul></li></ol>]]></content>
<tags>
<tag> 认证授权 </tag>
</tags>
</entry>
<entry>
<title>多线程编程(三)</title>
<link href="/2017/06/14/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%B8%89/"/>
<url>/2017/06/14/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%B8%89/</url>
<content type="html"><![CDATA[<h3 id="System-Threading类基本使用"><a href="#System-Threading类基本使用" class="headerlink" title="System.Threading类基本使用"></a>System.Threading类基本使用</h3><p>可以使用Thread类创建一个线程。通过传递一个ThreadStart委托或ParameterizedThreadStart委托来为该线程分配一个任务。后者是一个可以传递输入参数的委托。两个委托都没有返回值。<br><a id="more"></a></p><ol><li>多线程的实际执行顺序与系统调度算法相关,与代码执行顺序无关。</li><li>每个线程都有自己的线程栈,因此可以自己维护自己的局部变量。</li><li>所有线程共享全局变量。</li><li>线程和线程池的抉择:<ul><li>当创建线程的代价比线程池要小(例如只打算创建一个线程时)</li><li>当希望自己管理线程的优先级时(线程池自动管理)</li><li>需要一个前台线程(线程池创建的线程都是后台的)</li></ul></li><li>当一个线程B调用Join方法时,外部线程(即执行Join方法的线程)A就被停止执行,直到线程B执行完毕。<br>当调用Thread.Sleep方法时,会使当前线程停止执行一定的时间,直到指定的时间间隔结束。</li></ol><h3 id="线程池基本使用"><a href="#线程池基本使用" class="headerlink" title="线程池基本使用"></a>线程池基本使用</h3><ol><li>线程池是由CLR自动管理的,包含若干线程的集合。CLR利用线程池自动进行多线程中线程的创建,执行任务和销毁<br>线程池的工作方法和普通的线程有所不同。它维护一个队列QueueUserWorkItem,当程序想要执行一个异步操作时,线程池将这个操作追加到队列中,并派遣给一个线程池线程。线程池创建开始是没有线程的。如果线程池中没有线程,就创建一个新线程。</li><li>C#运用了线程池的类和操作有:任务并行库、委托、BackgroundWorker等等。</li><li><p>可以通过创建一个任务来隐式的使用线程池,任务方法可以有返回值,可以通过访问Task.Result(会阻塞)来得到这个返回值。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">//无返回值</div><div class="line">Task.Factory.StartNew(()=> {Console.WriteLine("12345");});</div><div class="line">//有返回值</div><div class="line">Task<int> task= Task.Factory.StartNew(()=> {</div><div class="line"> for (int i = 0; i < 10; i++)</div><div class="line"> {</div><div class="line"> Console.WriteLine("bbbbb" + i);</div><div class="line"> }</div><div class="line"> return 1; });</div><div class="line">Console.WriteLine(task.Result);</div><div class="line">for (int i = 0; i < 10; i++)</div><div class="line">{</div><div class="line"> Console.WriteLine("aaaaa" + i);</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>可以通过显示操作ThreadPool.QueueUserWorkItem队列来操作线程池,为它添加任务。还可以使用其重载方法来为任务指派输入变量。和任务有所不同,ThreadPool.QueueUserWorkItem的方法没有返回值。而且,必须在方法的内部进行异常处理,否则将会出现执行时异常。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ThreadPool.QueueUserWorkItem(x => { Console.WriteLine(x); }, "张三");</div></pre></td></tr></table></figure></li><li><p>异步委托是一种解决ThreadPool.QueueUserWorkItem没有返回值的方法。异步调用一个方法也相当于给线程池分配了一个新的任务。可以通过访问EndInvoke来获得访问结果。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Func<string, string> test = x =></div><div class="line">{</div><div class="line"> return "我是" + x;</div><div class="line">};</div><div class="line">IAsyncResult intro = test.BeginInvoke("张三",null,null);</div><div class="line">Console.WriteLine(test.EndInvoke(intro));</div><div class="line">Console.ReadKey();</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>HTML常见问题</title>
<link href="/2017/06/05/HTML%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/"/>
<url>/2017/06/05/HTML%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<ol><li>input标签回车提交表单出现情况:<ul><li>默认情况下,单个输入框,无论按钮的类型是什么,回车即提交</li><li>当按钮的类型是<code>submit</code>,无论有几个输入框,回车均表示提交</li><li>当按钮的类型是<code>button</code>,且存在多个输入框,回车不提交<br>解决方案:<br>在form表单或input中监听回车的键盘事件,并阻止其默认行为。<a id="more"></a></li></ul></li><li><code>return false=preventDefalut()+stopPropagation()</code></li><li>一般浏览器会缓存常见文本的副本,如图片、网页等资源,可以在访问资源的路径中添加一个随机数来避免缓存。如下代码为所有的超链接添加一个随机数(由于该方法会改变所有的超链接,可能会增加服务器负担,最好只在需要禁止缓存的链接上添加类似代码)。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">function add_rand(){</div><div class="line"> rand = Math.random();</div><div class="line"> $('a').each(function(){</div><div class="line"> href = $(this).attr('href');</div><div class="line"> if (href.length == 0 || href.indexOf('javascript') > -1) return;</div><div class="line"> else if(href.indexOf('?') > -1){</div><div class="line"> $(this).attr('href', href + '&' + rand);</div><div class="line"> }else{</div><div class="line"> $(this).attr('href', href + '?' + rand);</div><div class="line"> }</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> 常见错误 </tag>
</tags>
</entry>
<entry>
<title>SQL触发器</title>
<link href="/2017/06/02/SQL%E8%A7%A6%E5%8F%91%E5%99%A8/"/>
<url>/2017/06/02/SQL%E8%A7%A6%E5%8F%91%E5%99%A8/</url>
<content type="html"><![CDATA[<p>触发器是对表进行插入、更新、删除的时候自动执行的特殊存储过程。触发器一般用在check约束更加复杂的约束上面。触发器和普通的存储过程区别是:触发器是对某一个表进行操作。诸如:update、insert、delete这些操作的时候,系统会自动调用执行该表上对应的触发器。<br><a id="more"></a></p><h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><ol><li>触发器可以分为两大类:DML触发器和DDL触发器,其中DDL触发器会因多种数据定义语言语句而激发,这些语句有create、alter、drop语句;DML触发器会因多种数据操作语言语句而激发,这些语句有insert、update、delete语句等。</li><li>触发器有两个特殊的表:插入表(inserted表)和删除表(deleted表)。这两张表是逻辑表也是虚表。由系统在内存中创建这两张表,不会存储在数据库中。而且这两张表的数据都是只读的。这两张表的结果总是与被该触发器应用的表的结构相同。当触发器完成工作后,这两张表就会被删除。inserted表的数据是插入或是更新后的数据,而deleted表的数据时更新前或是删除的数据。</li><li>After触发器的操作流程(能用于DML触发器和DDL触发器):<ul><li>insert、update、delete开始</li><li>约束处理、声明性引用操作、创建inserted和deleted表</li><li>触发操作</li><li>insert、update、delete结束</li></ul></li><li>Instead Of触发器的操作流程(仅能用于DML触发器):<ul><li>insert、update、delete开始</li><li>创建inserted和deleted表</li><li>触发操作(代替执行)</li><li>约束处理</li><li>insert、update、delete结束</li></ul></li><li>也就是说After触发器会在约束检查之后触发,因此当约束未通过时不会执行After触发器中的操作。而Instead Of触发器会先进行触发器中的操作,然后进行约束处理,此时约束未通过的话并不会抛出异常。</li><li>事务的创建语句格式<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">create trigger tgr_name</div><div class="line">on table_name</div><div class="line">with encrypion –加密触发器</div><div class="line">After update...</div><div class="line">as</div><div class="line"> Transact-SQL</div></pre></td></tr></table></figure></li></ol><h3 id="触发器与事务"><a href="#触发器与事务" class="headerlink" title="触发器与事务"></a>触发器与事务</h3><p>当执行触发器时,触发器的操作好像有一个未完成的事务在起作用,原因是由于自动提交事务的存在,因此在触发器中执行事务操作时应格外小心,可以通过以下两种方法来避免触发器中的事务问题。</p><ol><li>在触发器中使用事务时使用显式事务,当提交时无须做任何附加操作,当回滚时为了保持事务的完整性,则需要在回滚后再开启一个显式事务。</li><li>在触发器中使用事务点的概念,这样可以避免方法一中的麻烦操作。<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">--显式事务</div><div class="line">create trigger tr_user on [user] after insert</div><div class="line">as</div><div class="line">begin</div><div class="line"> begin tran</div><div class="line"> print '123'</div><div class="line"> commit tran</div><div class="line"> --ROLLBACK TRAN</div><div class="line"> --BEGIN TRAN</div><div class="line">end</div><div class="line">--事务点(事务点的提交回滚并不会改变@@trancount的值)</div><div class="line">alter trigger tr_user on [user] after insert</div><div class="line">as</div><div class="line">begin</div><div class="line"> SAVE TRAN temppoint</div><div class="line"> print '123'</div><div class="line"> ROLLBACK TRAN temppoint</div><div class="line">END</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> T-SQL </tag>
</tags>
</entry>
<entry>
<title>多线程编程(二)</title>
<link href="/2017/06/01/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%BA%8C/"/>
<url>/2017/06/01/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%BA%8C/</url>
<content type="html"><![CDATA[<h3 id="线程概念"><a href="#线程概念" class="headerlink" title="线程概念"></a>线程概念</h3><ol><li>当我们创建了一个线程后,线程里面主要包括线程内核对象、线程环境块、1M大小的用户模式栈和内核模式栈。线程有自己的线程栈,大小为1M,所以它可以维护自己的变量。线程是一个新的对象,它会增加系统上下文切换的次数,所以过多的线程将导致系统开销很大。<a id="more"></a></li><li>当某个线程一直空闲(例如一个开启的记事本但长时间无输入)时,他可以提前终止属于他的时间片。线程也可以进入挂起状态,此后任何时间片都不会分配到这个线程,除非发生了某个事件(例如用户进行了输入)。节省出来的时间可以让CPU调度其他线程,增加系统性能。</li><li>线程的主要状态有四种:就绪(Unstarted),运行(Running),阻塞(WaitSleepJoin)和停止(Stopped),还有一种Aborted就是被杀死了。通常,强制获得线程执行任务的结果,或者通过锁等同步工具,会令线程进入阻塞状态。当得到结果后,线程就解除阻塞,回到就绪状态。</li><li>阻塞(WaitSleepJoin),顾名思义,是使线程进入阻塞状态。当一个线程被阻塞之后,它立刻用尽它的时间片(即使还有时间),然后CPU将永远不会调度时间片给它直到它解除阻塞为止(在未来的多少毫秒内我不参与CPU竞争)。主要方式有:<code>Thread.Join</code>(其他线程都运行完了之后就解除阻塞),<code>Thread.Sleep</code>(时间到了就解除阻塞),<code>Task.Result</code>(得到结果了就解除阻塞),遭遇锁而拿不到锁的控制权(等到其他线程释放锁,自己拿到锁,就解除阻塞)等。当然,自旋也是阻塞的一种。</li><li>Thread类中的方法对线程状态的影响:<ul><li>Start:使线程从就绪状态进入运行状态</li><li>Sleep:使线程从运行状态进入阻塞状态,持续若干时间,然后阻塞自动解除回到运行状态</li><li>Join:使线程从运行状态进入阻塞状态,当其他线程都结束时阻塞解除。</li><li>Interrupt:当线程被阻塞时,即使阻塞解除的要求还没有达到,可以使用interrupt方法强行唤醒线程使线程进入运行状态。这将会引发一个异常。</li><li>Abort:使用Abort方法可以强行杀死一个处于任何状态的线程。</li></ul></li><li>操作系统为每个程序分配一定时间,然后中断当前运行程序并允许另外一个程序执行。处理器实际上为进程分配时间。进程可以执行的时间被称作”时间片”或者”限量”。时间片的值并非一个常量。</li><li>Windows是一个抢占式的操作系统。在抢占式操作系统中,较高优先级的进程总是抢占较低优先级的进程(即使时间片没有用完)。用户不能保证自己的线程一直运行,也不能阻止其他线程的运行。每个进程有一个优先级类,每个线程有一个优先级(0-31)。较高优先级的进程中的较高优先级的线程获得优先分配时间片的权利。这种调度方式会导致饥荒。</li><li>Thread类中的Priority允许用户改变线程的优先级(但并非直接指定数字,而是指定几个层次)。</li><li>一个进程可以有任意多个前台和后台线程。前台线程使得整个进程得以继续下去。一个进程的所有前台线程都结束了,进程也就结束了。当该进程的所有前台线程终止时,CLR将强制终止该进程的所有后台线程,这将会导致finally可能没来得及执行(从而导致一些垃圾回收的问题)。解决的方法是使用join等待。<br>使用thread类创建的线程默认都是前台线程。Thread的IsBackground类允许用户将一个线程置为后台线程。</li></ol><h3 id="多线程优缺点"><a href="#多线程优缺点" class="headerlink" title="多线程优缺点"></a>多线程优缺点</h3><ol><li>优点:<ul><li>更大限度的利用CPU和其他计算机资源</li><li>当一条线程冻结时,其他线程仍然可以运行</li><li>在后台执行长任务时,保持用户界面良好的响应。</li><li>并行计算(仅当这么做的好处大于对资源的损耗时)</li></ul></li><li>缺点:<ul><li>线程的创建和维护需要消耗计算机资源。(使用线程池,任务来抵消一部分损失)。一条线程至少需要耗费1M内存。</li><li>多个线程之间如果不同步,结果将会难以预料(使用锁和互斥)</li><li>线程的启动和运行时间是不确定的,由系统进行调度,所以可能会造成资源争用,同样造成难以预料的结果。(使用锁和互斥,或者进行原子操作) </li><li>为了避免上述缺点,需要开发者更精细的测试代码,增加了开发时间</li></ul></li></ol>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>SQL常见错误</title>
<link href="/2017/05/27/SQL%E5%B8%B8%E8%A7%81%E9%94%99%E8%AF%AF/"/>
<url>/2017/05/27/SQL%E5%B8%B8%E8%A7%81%E9%94%99%E8%AF%AF/</url>
<content type="html"><![CDATA[<ol><li>date、datetime、datetime2这三种类型对日期的精确程度各不相同,其中datetime2的精确程度最高,如果出现varchar转换datetime失败等类似错误,多半是超出了目标类型的精度范围。</li></ol>]]></content>
<tags>
<tag> 常见错误 </tag>
</tags>
</entry>
<entry>
<title>.Net常见错误</title>
<link href="/2017/05/27/Net%E5%B8%B8%E8%A7%81%E9%94%99%E8%AF%AF/"/>
<url>/2017/05/27/Net%E5%B8%B8%E8%A7%81%E9%94%99%E8%AF%AF/</url>
<content type="html"><![CDATA[<ol><li>由于未在配置文件中设置数据库连接字符串,导致的未将对象引用设置到对象的实例(此时会在堆栈跟踪中出现所谓的..ctor()字样)。</li><li>程序中用到的静态资源文件(xml,config),一定要将其属性中的生成操作和复制到输出目录设置为内容和始终复制,否则会出现意向不到的问题。</li></ol>]]></content>
<tags>
<tag> 常见错误 </tag>
</tags>
</entry>
<entry>
<title>多线程编程(一)</title>
<link href="/2017/05/26/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%B8%80/"/>
<url>/2017/05/26/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E7%BC%96%E7%A8%8B-%E4%B8%80/</url>
<content type="html"><![CDATA[<h3 id="操作系统发展史"><a href="#操作系统发展史" class="headerlink" title="操作系统发展史"></a>操作系统发展史</h3><ul><li>直到20世纪50年代中期,还没出现操作系统,计算机工作采用手工操作方式,手工操作方式有两个特点:用户独占全机,不会出现因资源已被其他用户占用而等待的现象,但资源的利用率低;CPU等待手工操作,CPU的利用不充分。<a id="more"></a></li><li>50年代后期出现了批处理系统,批处理系统的追求目标是提高系统资源利用率和系统吞吐量,以及作业流程的自动化。批处理系统的一个重要缺点是不提供人机交互能力,给用户使用计算机带来不便<br>批处理是指用户将一批作业提交给操作系统后就不再干预,由操作系统控制他们自动运行,这种采用批量处理作业技术的操作系统称为批处理操作系统。批处理操作系统分为单道批处理系统和多道批处理系统。<ul><li>早期的批处理系统属于单道批处理系统,其目的是减少作业间转换时的人工操作,从而减少CPU的等待时间。它的特征是内存中只允许存放一个作业,即当前正在运行的作业才能驻留内存,作业的执行顺序是先进先出。</li><li>为了提高CPU的利用率,在单道批处理系统的基础上引入了多道程序设计技术,这就形成了多道批处理系统,即在内存中可同时存在若干道作业,作业执行的次序与进入内存的次序无严格的对应关系,因为这些作业是通过一定的作业调度算法来使用CPU的,一个作业在等待I/O处理时,CPU调度另外一个作业运行,因此CPU的利用率得到显著提高。</li><li>批处理系统中,一个作业可以长时间占用CPU。而分时系统中,一个作业只能在一个时间片的时间内使用CPU。批处理系统不是严格意义上的操作系统。</li></ul></li><li>多道程序技术运行的特征(60年代中期):多道、宏观上并行、微观上串行。多道程序技术的提出是为了改善CPU的利用率。它需要硬件支持,并令CPU调度其他硬件工作。</li><li>分时操作系统是使一台计算机采用时间片轮转的方式同时为几个、几十个甚至几百个用户服务的一种操作系统。把计算机与许多终端用户连接起来,分时操作系统将系统处理机时间与内存空间按一定的时间间隔,轮流地切换给各终端用户的程序使用。由于时间间隔很短,每个用户的感觉就像他独占计算机一样。分时操作系统的特点是可有效增加资源的利用率。<br>实现多任务处理的方式有抢占式和协作式。协作式环境下,下一个进程被调度的前提是当前进程主动放弃时间片;抢占式环境下,操作系统完全决定进程调度方案,操作系统可以剥夺耗时长的进程的时间片,提供给其他进程。</li></ul><h3 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h3><ol><li>每个程序最终都会加载到内存中运行。多道程序设计的出现提供了进程的雏形–系统内部可以存放多于一个作业。分时操作系统中,系统内部也可以同时存在超过一个程序,这些同时存在于计算机内存中的程序就被称为进程。</li><li>进程的实现:通过操作系统的内存分配和调度。OS通过进程控制块管理进程。通过虚拟内存实现进程隔离,一个进程无法访问其他进程正在占有的资源。但不安全的操作系统例如DOS,任何进程都可以访问其他进程的资源。</li><li>虚拟内存是计算机系统内存管理的一种技术,它使得应用程序认为它拥有连续的可用的内存,而实际上,它通常是被分割成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换,目前,大部分操作系统都使用了虚拟内存,如Windows家族的”虚拟内存”;Linux的”交换空间”等。</li><li>在多进程的环境里,虽然从概念上看,有多个进程在同时执行,但在单个CPU下,在任何时刻只能有一个进程处于执行状态,而其他进程则处于非执行状态。通过进程调度来确定任意时刻到底由哪个进程执行,哪些不执行,其任务即选择下一个要运行的进程。</li><li>进程间通信,指至少两个进程或线程间传送数据或信号的一些技术或方法。每个进程都有自己的一部分独立的系统资源,彼此是隔离的。为了能使不同进程互相访问资源并进行协调工作,才有了进程间通信。进程间通信的途径有很多种:管道,套接字,信号与信号量,共享内存,消息队列等。</li></ol><h3 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h3><ul><li>微软决定在一个进程中运行应用程序的每个实例。进程是应用程序要使用的资源的一个集合。当程序在进程中开始运行时,它就如同被关进了一个密闭的空间,里面有所有它需要的东西。不同的密闭空间不会发生关系,任何一个进程死掉不会导致整个系统崩溃。进程有自己的虚拟地址空间,确保这个进程使用的代码不会被其他进程访问。并且当进程失去响应时,系统仍然可以工作,还可以用其他进程杀死失去响应的进程。</li><li>如果机器只有一个CPU,那么当某个应用程序进入无穷循环,那个唯一的CPU就会忙着跑无穷循环而无暇顾及其他应用程序。为了解决CPU无法分身的问题,线程便应运而生。</li><li>Windows通过线程来虚拟化CPU,线程使得每个进程(至少拥有一个线程)拥有CPU的一个”分身”(称为逻辑CPU,真正的CPU称为物理CPU)。当一个线程进入无限循环时,其他线程仍然可以继续运行。</li></ul><h3 id="线程与进程区别"><a href="#线程与进程区别" class="headerlink" title="线程与进程区别"></a>线程与进程区别</h3><ol><li>线程的任务是虚拟化CPU,令CPU不再是所有进程共享的一个互斥元件,进程的任务则是提高CPU的使用效率。</li><li>线程作为调度和分派的基本单位,而进程作为资源拥有的基本单位。</li><li>一个进程可以包括多个线程,而且至少要有一个前台线程。</li><li>进程之间通过虚拟内存隔离,但一个进程中的线程共享所有进程的资源。</li></ol>]]></content>
<tags>
<tag> 多线程 </tag>
</tags>
</entry>
<entry>
<title>捆绑包</title>
<link href="/2017/05/23/%E6%8D%86%E7%BB%91%E5%8C%85/"/>
<url>/2017/05/23/%E6%8D%86%E7%BB%91%E5%8C%85/</url>
<content type="html"><![CDATA[<p>捆绑包(Bundle)特性,是MVC框架为组织和优化CSS以及JavaScript文件而提供的一个特性,视图和布局会触发浏览器向服务器请求捆绑包。它可以减少向服务器请求的次数以及请求资源文件的大小<br><a id="more"></a></p><ol><li>用于创建捆绑包的这些类,属于System.Web.Optimization命名空间。</li><li>通常约定在名为BundleConfig.cs文件中定义捆绑包,该文件位于App_Start文件夹中,与路由配置、过滤器配置类似,如果是手动添加的该文件,需要在Global.asax文件中进行注册,并且还要在引用捆绑包的视图中引入System.Web.Optimization命名空间,可以在Views/Web.config中配置全局引入。</li><li>约定在App_Start文件夹中所定义的类在应用程序的顶层命名空间。</li><li>在创建捆绑包的时候,最好要区分开脚本文件和样式表,因为MVC框架对这些文件的优化是不同的。样式由StyleBundle类表示,而脚本则由ScriptBundle类所表示。</li><li>在创建一个新的捆绑包时,实际上就是创建StyleBundle或ScriptBundle类的一个实例,它们都有一个构造器参数,即引用捆绑包的路径。 该路径的作用是作为浏览器请求捆绑包内容的一个URL,重要的是,要为这些路径使用一个与应用程序所支持的路径无冲突的URL方案。此项工作最安全的方式是以~/bundles或~/Content作为起始路径。</li><li>一旦已经创建了StyleBundle或ScriptBundle对象,便可以使用Include方法添加所包含的样式表或脚本文件的细节。有一些很好的特性可用于灵活地构造捆绑包。(P611)</li><li>可以使用@Style.Render和@Scripts.Render来在视图中使用捆绑包,参数即为捆绑包的路径。</li><li>组织JavaScript和CSS文件到相关的组是有用的方法,以确保你不会忘记要包含的文件,以及你的布局包含项目中包含的任何版本的文件。但捆绑包的真正神奇之处在于它们可以用来优化向浏览器分发的JavaScript和CSS内容。</li><li>配置Web.config文件中的compilation元素的debug属性为false时,捆绑包中的各个文件的最小化版本被选择并连接到一起,以便以一个块的形式将它们分发到客户端。(P614)</li><li>最小化处理是删除JavaScript或CSS文件中的空白,以及在JavaScript文件情况下,缩短变量和函数名称,以使得需要更少的带宽来传输文件。</li></ol>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>Quartz.NET详解</title>
<link href="/2017/05/16/Quartz-NET%E8%AF%A6%E8%A7%A3/"/>
<url>/2017/05/16/Quartz-NET%E8%AF%A6%E8%A7%A3/</url>
<content type="html"><![CDATA[<h3 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h3><ol><li>scheduler:任务调度器<br>trigger:触发器,用于定义任务调度时间规则<br>job:任务,即被调度的任务<br>misfire:错过的,指本应该被执行但实际没有执行的任务调度<a id="more"></a></li><li>IJob:是一个接口,只有一个方法void execute(IJobExecutionContext context),开发者实现该接口定义运行任务,IJobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。</li><li>JobDetail:Quartz.NET在每次执行Job时,都重新创建一个Job实例,所以它不直接接收一个Job的实例,相反它接收一个Job实现类,以便运行时通过反射机制实例化Job,因此需要通过一个类来描述Job的实现类及其他相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色。</li><li>Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CromTrigger这两个子类。当仅需要触发一次或者以固定时间间隔周期执行,SimpleTrigger是最合适的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等。</li><li>Calendar:它是一些日历特定时间点的集合。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点。</li><li>Scheduler:代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一。</li><li>ThreadPool:Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率。</li><li>一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。</li></ol><h3 id="具体实现"><a href="#具体实现" class="headerlink" title="具体实现"></a>具体实现</h3><ul><li>任务调度器的生命周期从通过SchedulerFactory创建开始到调用Shutdown()方法结束,可以通过任务调度器添加Jobs和对应的Triggers,也可以执行一些其他与调度相关的操作。</li><li>Job实例的Execute方法可以通过JobDataMap类来访问外部的一些数据。</li><li><p>Job和Trigger的绑定方式有两种</p><ul><li><p>编程模式:Quartz使用”builder”类来声明领域特定语言(Domain Specific Language,或者叫做”fluent interface”)。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">//声明一个任务调度器</div><div class="line">IScheduler sched = StdSchedulerFactory.GetDefaultScheduler();</div><div class="line">sched.Start();</div><div class="line"> // 声明一个任务并绑定到对应的实现了IJob接口的类</div><div class="line">IJobDetail job = JobBuilder.Create<HelloJob>()</div><div class="line"> .WithIdentity("myJob", "group1") // name "myJob", group "group1"</div><div class="line"> .Build();</div><div class="line"> </div><div class="line">// 声明一个使用简单模式的触发器,从现在开始,时间间隔为40,重复执行</div><div class="line">ITrigger trigger = TriggerBuilder.Create()</div><div class="line"> .WithIdentity("myTrigger", "group1")</div><div class="line"> .StartNow()</div><div class="line"> .WithSimpleSchedule(x => x</div><div class="line"> .WithIntervalInSeconds(40)</div><div class="line"> .RepeatForever()) </div><div class="line"> .Build();</div><div class="line"> </div><div class="line">// 把任务和对应的触发器注册到任务调度器中</div><div class="line">sched.scheduleJob(job, trigger);</div></pre></td></tr></table></figure></li><li><p>配置模式:使用配置模式,需要创建两个配置文件,一个是quartz.config(用于Quartz.Net框架自动读取),另一个是任务与触发器对应的文件(文件名来自于quartz.config中quartz.plugin.xml.fileNames的指向),该文件为xml格式。<br>quartz.config: </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"># You can configure your scheduler in either <quartz> configuration section</div><div class="line"># or in quartz properties file</div><div class="line"># Configuration section has precedence</div><div class="line">quartz.scheduler.instanceName = ServerScheduler</div><div class="line"># configure thread pool info</div><div class="line">quartz.threadPool.type = Quartz.Simpl.SimpleThreadPool, Quartz</div><div class="line">quartz.threadPool.threadCount = 10</div><div class="line">quartz.threadPool.threadPriority = Normal</div><div class="line"># job initialization plugin handles our xml reading, without it defaults are</div><div class="line">used</div><div class="line">quartz.plugin.xml.type = Quartz.Plugin.Xml.XMLSchedulingDataProcessorPlugin,</div><div class="line">Quartz</div><div class="line">quartz.plugin.xml.fileNames = ~/quartz_jobs.xml</div><div class="line"># export this server to remoting context</div><div class="line">quartz.scheduler.exporter.type = Quartz.Simpl.RemotingSchedulerExporter,</div><div class="line">Quartz</div><div class="line">quartz.scheduler.exporter.port = 555</div><div class="line">quartz.scheduler.exporter.bindName = QuartzScheduler</div><div class="line">quartz.scheduler.exporter.channelType = tcp</div><div class="line">quartz.scheduler.exporter.channelName = httpQuartz</div></pre></td></tr></table></figure><p>quartz_jobs.xml</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line"> <?xml version="1.0" encoding="UTF-8"?></div><div class="line"></div><div class="line"> <!-- This file contains job definitions in schema version 2.0 format --></div><div class="line"></div><div class="line"> <job-scheduling-data xmlns="http://quartznet.sourceforge.net/JobSchedulingData" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"></div><div class="line"></div><div class="line"> <processing-directives></div><div class="line"> <overwrite-existing-data>true</overwrite-existing-data></div><div class="line"> </processing-directives></div><div class="line"></div><div class="line"> <schedule></div><div class="line"> <!--TestJob测试 任务配置--></div><div class="line"> <job></div><div class="line"> <name>TestJob</name></div><div class="line"> <group>Test</group></div><div class="line"> <description>TestJob测试</description></div><div class="line"> <job-type>Quartz1.TestJob,Quartz1</job-type></div><div class="line"> <durable>true</durable></div><div class="line"> <recover>false</recover></div><div class="line"> </job></div><div class="line"> <trigger></div><div class="line"> <cron></div><div class="line"> <name>TestJobTrigger</name></div><div class="line"> <group>Test</group></div><div class="line"> <job-name>TestJob</job-name></div><div class="line"> <job-group>Test</job-group></div><div class="line"> <start-time>2015-01-22T00:00:00+08:00</start-time></div><div class="line"> <cron-expression>0/3 * * * * ?</cron-expression></div><div class="line"> </cron></div><div class="line"> </trigger></div><div class="line"> </schedule></div><div class="line"></job-scheduling-data></div></pre></td></tr></table></figure></li></ul></li></ul><h3 id="cron表达式"><a href="#cron表达式" class="headerlink" title="cron表达式"></a>cron表达式</h3><p>cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每个域代表一个含有,语法格式为:秒 分 时 日 月 周 [年],其中年域可写可不写。</p><ol><li>每个域可出现的字符如下:<ul><li>秒:可出现”,-*/“四个字符,有效范围为0-59的整数</li><li>分:可出现”,-*/“四个字符,有效范围为0-59的整数</li><li>时:可出现”,-*/“四个字符,有效范围为0-23的整数</li><li>日:可出现”,-*/?LWC”八个字符,有效范围为0-31的整数</li><li>月:可出现”,-*/“四个字符,有效范围为1-12的整数或JAN-DEC</li><li>周:可出现”,-*/?LC#”八个字符,有效范围为1-7的整数或SUN-SAT</li><li>年:可出现”,-*/“四个字符,有效范围为1970-2099</li></ul></li><li>每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是:<ul><li>*:表示匹配该域的任意值,假如在分域中使用,则表示每分钟都会触发事件</li><li>?:只能在日和周两个域中使用,它也匹配域中的任意值,但实际不会。因为日和周会相互影响。例如想在每月的20日触发事件,不管20日到底是星期几,则只能使用?</li><li>-:表示范围,例如在分域中使用5-20,表示从5分钟到20分钟每分钟触发一次</li><li>/:表示起始时间开始触发,然后每隔固定时间触发一次,例如在分域中使用5/20,则意味着5分时开始触发,然后每隔20分钟触发一次</li><li>,:表示列出枚举值。例如在分域中使用5,20,则表示在5分和20分钟时触发</li><li>L:表示最后,如在周域中使用5L,意味着在最后的一个星期四触发。</li><li>W:表示有效工作日,只能出现在日域中,系统将在离指定日期最近的有效工作日触发事件。</li><li>#:用于确定每个月第几个星期几,只能出现在周域中,例如4#2,表示某月的第二个星期三。</li></ul></li></ol>]]></content>
<tags>
<tag> .NET第三方类库 </tag>
</tags>
</entry>
<entry>
<title>Windows服务</title>
<link href="/2017/05/15/Windows%E6%9C%8D%E5%8A%A1/"/>
<url>/2017/05/15/Windows%E6%9C%8D%E5%8A%A1/</url>
<content type="html"><![CDATA[<h3 id="Topshelf"><a href="#Topshelf" class="headerlink" title="Topshelf"></a>Topshelf</h3><p>Topshelf是一款基于.NET框架编写的应用于宿主服务的框架。其创建服务的方式非常简单,允许开发者使用它来创建一个能够被用来安装为服务的简单控制台应用程序,毕竟控制台应用程序比一个服务更容易测试。一旦测试通过,Topshelf便很容易将其安装为一个服务。<br><a id="more"></a></p><ol><li>通过HostFactory.Run方法可以设置一个宿主,该方法接收一个Action类型的委托参数,委托只包含一个HostConfigurator类型的参数且该参数暴露了宿主级别的所有配置,通过该方法可以获取环境变量中的命令参数。</li><li>通过调用HostConfigurator类型变量的Service泛型方法,可以告诉Topshelf要服务的类型,同时该方法也接受一个Action类型的委托参数,委托包含一个ServiceConfigurator类型的参数且该参数暴露了服务级别的配置。</li><li>使用ServiceConfigurator类型变量的ConstructUsing方法来实例化要服务的类型。也可以通过IOC来实例化该类型。</li><li>通过调用ServiceConfigurator类型变量的WhenStarted和WhenStopped方法来注册服务开始和停止时的事件处理程序。</li><li>通过HostConfigurator类型变量的RunAsLocalSystem方法,可以设置宿主服务以本地系统用户身份运行。</li><li>通过HostConfigurator类型变量的SetDescription、SetDisplayName、SetServiceName方法来设置宿主服务的服务描述、服务显示名称、服务名称。</li><li>上述是常用的Topshelf使用配置,除此之外还可以设置宿主服务的开始模式、服务依赖、日志集成等。[<a href="https://topshelf.readthedocs.io/en/latest/configuration/config_api.html#service-dependencies" target="_blank" rel="external">https://topshelf.readthedocs.io/en/latest/configuration/config_api.html#service-dependencies</a>]</li><li>一旦通过Topshelf创建了服务,便可以使用一系列扩展的命令行命令来安装、卸载、启用等配置服务。[<a href="https://topshelf.readthedocs.io/en/latest/overview/commandline.html" target="_blank" rel="external">https://topshelf.readthedocs.io/en/latest/overview/commandline.html</a>]<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"> HostFactory.Run(x =></div><div class="line"> {</div><div class="line"> x.Service<TownCrier>(s => </div><div class="line"> {</div><div class="line"> s.ConstructUsing(name => new TownCrier());</div><div class="line"> s.WhenStarted(tc => tc.Start());</div><div class="line"> s.WhenStopped(tc => tc.Stop());</div><div class="line"> });</div><div class="line"> x.RunAsLocalSystem();</div><div class="line"> x.Disabled();</div><div class="line"></div><div class="line"> x.SetServiceName("Sample Topshelf Host");</div><div class="line"> x.SetDisplayName("Stuff");</div><div class="line"> x.SetDescription("Stuff");</div><div class="line"> });</div><div class="line">//服务类型</div><div class="line">public class TownCrier</div><div class="line"> {</div><div class="line"> readonly Timer _timer;</div><div class="line"> public TownCrier()</div><div class="line"> {</div><div class="line"> _timer = new Timer(1000) { AutoReset = true };</div><div class="line"> _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well",DateTime.Now);</div><div class="line"> }</div><div class="line"> public void Start() { _timer.Start(); }</div><div class="line"> public void Stop() { _timer.Stop(); }</div><div class="line"> }</div></pre></td></tr></table></figure></li></ol>]]></content>
<tags>
<tag> .NET第三方类库 </tag>
</tags>
</entry>
<entry>
<title>模型验证(二)</title>
<link href="/2017/05/09/%E6%A8%A1%E5%9E%8B%E9%AA%8C%E8%AF%81-%E4%BA%8C/"/>
<url>/2017/05/09/%E6%A8%A1%E5%9E%8B%E9%AA%8C%E8%AF%81-%E4%BA%8C/</url>
<content type="html"><![CDATA[<p>在Web应用程序中,用户会期望得到及时的验证反馈–对服务器不作任何递交。这称为客户端验证,客户端验证可以减少服务器负载,缩短用户等待时间,但兼容性难。</p><ol><li>MVC框架支持渐进式客户端验证,也就是在生成的HTML元素上添加验证标签属性来表示验证规则。这些标签属性由包含在MVC框架中的JavaScript库进行解释,框架又转而对JQuery验证库进行配置,由验证库完成实际的验证工作。<a id="more"></a></li><li><p>客户端验证是由web.config文件中的两个设置来控制的,为了使客户端验证生效,这两个设置都必须为true。当然这两个属性默认就是true。也可以配置基于个别视图的客户端验证,只需要在视图的一个razor代码块中设置HtmlHelper.ClientValidationEnabled和HtmlHelper.UnobtrusiveJavaScriptEnabled即可。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><add key="ClientValidationEnabled" value="true" /></div><div class="line"><add key="UnobtrusiveJavaScriptEnabled" value="true" /></div></pre></td></tr></table></figure></li><li><p>客户端验证是由JavaScript实现的,因此要引用JQuery、Jquery.validate、Jquery.validate.unobtrusive这三个类库。</p></li><li>目前,部分HTML5浏览器版本支持基于对运用于input元素的标签属性的简单客户端验证,为了避免JQuery验证和浏览器验证都会对表单进行操作,从而使用户感到迷惑,可以向form元素添加novalidate标签属性。</li><li>远程验证,是一项客户端验证技术,但是会调用服务器上的一个行为方法来执行验证。在MVC中使用远程验证的第一步是创建一个能够验证某个模型属性的行为方法,支持远程验证的行为方法必须返回JsonResult类型,它告诉MVC框架,我们正在使用JSON数据,并且参数必须与要验证的模型属性同名。第二步是在要验证的模型属性上添加Remote特性,并传递相应的行为方法名作为参数</li><li>在远程验证中,行为方法的参数类型最好是string类型,然后在方法中执行各种类型的转换、解析,或明确的模型绑定。(P602)</li></ol>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>模型验证(一)</title>
<link href="/2017/05/05/%E6%A8%A1%E5%9E%8B%E9%AA%8C%E8%AF%81-%E4%B8%80/"/>
<url>/2017/05/05/%E6%A8%A1%E5%9E%8B%E9%AA%8C%E8%AF%81-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>实际情况下,用户通常输入一些不能验证和使用的数据。模型验证是,确保应用程序所接收的数据适合于绑定到模型,并且在不合适时给用户提供有用的信息,以帮助他们修正其问题的过程。<br><a id="more"></a><br>该过程的第一部分,检查接收到的数据是保持域模型完整性的方式之一。通过拒绝域环境中无意义的数据,可以防止应用程序中出现奇怪和意想不到的情况。第二部分,帮助用户修正问题,同样是重要的。如果不给用户提供一些与应用程序进行交互所需要的信息和反馈,他们会感到受挫和困惑。在面向公众的应用程序中(Internet应用程序),这意味着用户会直接停用这个应用程序;在社团应用程序中(Intranet应用程序),这意味着会妨碍用户的工作流程。</p><h3 id="明确地验证模型"><a href="#明确地验证模型" class="headerlink" title="明确地验证模型"></a>明确地验证模型</h3><ol><li><p>验证一个模型最直接的方式,是在行为方法中做这件事。通过检查模型绑定器赋给参数对象的各个属性的值,并且用ModelState属性注册所发现的错误,ModelState属性是控制器从它的基类继承而来的。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">if (string.IsNullOrEmpty(appt.ClientName)) {</div><div class="line"> ModelState.AddModelError("ClientName", "Please enter your name");</div><div class="line">}</div></pre></td></tr></table></figure><p>上述例子是希望用户为此属性提供一个值,因此,用静态的string.IsNullOrEmpty方法对该属性进行了检查。如果未接收到一个值,则使用ModelState.AddModelError方法指定有问题的属性名,和一条应该显示给用户的消息,以帮助他们改正这一错误。</p></li><li>可以通过使用ModelState.IsValidField属性,来检查模型绑定器是否能够对一个属性赋值。以确保模型绑定器能够解析用户所递交的值。如果无法从请求数据中解析到一个值,执行额外检查或报告其他错误是没有意义的。</li><li>在验证了模型对象的所有属性之后,读取ModelState.IsValid属性,以考察是否有错误发生。如果在检查期间调用ModelState.AddModelError方法,或在创建对象时模型绑定器遇到了问题,该方法会返回true。</li><li>如果相关属性有错误报告,那么辅助器对应的input元素会添加一个CSS的class标签属性,其值为input-validation-error。可以通过该类来为验证失败的属性添加特定的样式。</li><li>样式只能指定一个字段有问题,但并没有告诉用户这个问题是什么。Html.ValidationSummary辅助器会给用户显示验证错误的摘要。如果没有错误,那么该辅助器不会生成任何HTML。该辅助器生成的HTML中包含validation-summary-errors类。(有关属性级错误和模型级错误详见P585)</li><li>希望把验证摘要限制到模型级错误的原因是,属性级错误可以显示在相应字段的旁边。通过Html.ValidationMessageFor辅助器为单个模型属性显示验证错误。</li></ol><h3 id="其他验证技术"><a href="#其他验证技术" class="headerlink" title="其他验证技术"></a>其他验证技术</h3><ol><li>默认模型绑定器会将验证作为绑定过程的一部分,模型绑定器会为模型对象中的每一个属性执行一些基本的验证。内建的默认模型绑定器DefaultModelBinder提供了一些可以重写的有用方法,以便对一个绑定器添加验证。(P589)</li><li>MVC框架也支持用特性来表达模型验证规则。使用特性的优点是,在整个应用程序中运用绑定过程的任何地方,都会强制执行验证规则,而不止存在于个别行为方法中。所有验证特性都可以给ErrorMessage属性设置一个值,以指定一个自定义的错误消息。如果不提供自定义错误消息,那么将采用默认消息。(P590)</li><li>内建的验证特性很基本,而且只能做属性级验证。在使用复选框辅助器方法时,要注意非空判断的验证,并非简单的使用Required特性,因为复选框无论是否选中,都会像后台传递一个值。(P591)</li><li>通过派生ValidationAttribute类,可以创建自定义的属性验证特性或者创建模型验证特性。也可以通过内建的验证特性来派生新类,从而扩展内建验证特性行为的能力。(P591)</li><li>另一种验证技术是创建自验证模型,即验证逻辑是模型类的一部分。一个自验证模型类实现了IValidatableObject接口。(P596)</li></ol>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>Python程序设计(一)</title>
<link href="/2017/05/05/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%80/"/>
<url>/2017/05/05/Python%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1-%E4%B8%80/</url>
<content type="html"><![CDATA[<ol><li>Python适合开发的应用类型有很多:<ul><li>首选是网络应用,包括网站、后台服务等等。</li><li>其次是许多日常需要的小工具,包括系统管理员需要的脚本任务等等。</li><li>另外就是把其他语言开发的程序再包装起来,方便使用。<a id="more"></a></li></ul></li><li>Python也有一些缺点:<ul><li>由于Python是解释性语言,代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。(有些时候不必在意程序运行速度)</li><li>代码不能加密,如果发布Python程序,实际上就是发布源代码。凡是编译型语言,都不存在这个问题。而解释型语言,则必须把源码发布出去。</li></ul></li><li>Python是跨平台的,要学习Python编程,首先要把Python安装到电脑中。安装后,你会得到Python解释器,一个命令行交互环境,还有一个简单的集成开发环境。</li><li>只需要打开命令提示符窗口,输入python并回车,则进入了Python交互式环境中,输入exit()并回车,可退出Python交互式环境。</li><li>当我们编写Python代码时,我们得到的是一个包含Python代码的以<code>.py</code>为扩展名的文本文件。要运行代码,就需要Python解释器去执行<code>.py</code>文件。事实上,存在多种Python解释器。官方版本的解释器是CPython。如果要和Java或.Net平台交互,最好的办法不是使用解释器,而是通过网络调用来交互,确保各程序之间的独立性。</li><li>在命令行模式下,可以执行<code>python</code>进入Python交互式环境,亦可以执行<code>python hello.py</code>运行一个<code>.py</code>文件;看到<code>>>></code>是在Python交互式环境下,在Python交互式环境下,只能输入Python代码并立刻执行。</li><li>任何计算机程序都是为了执行特定的任务,有了输入,用户才能告诉计算机程序所需要的信息;有了输出,程序运行后才能告诉用户任务的结果。input()和print()是Python在命令行下面最基本的输入和输出。也可以通过其他更高级的图形界面完成输入和输出。</li><li>Python与ECMAScript类似,并不需要在每行的结尾加上分号,如果加上分号,也不会有什么作用。 </li><li><p>可以把模块想象成导入到Python以增强其功能的扩展。需要使用特殊的命令import来导入模块,例如地板函数floor,则位于名为math的模块中。注意在导入该模块后,仍需要按照”模块.函数”的格式来使用这个模块的函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> import math</div><div class="line">>>> math.floor(3.8)</div></pre></td></tr></table></figure><p>在确定自己不会导入多个同名函数(从不同模块导入)的情况下,可以使用import命令的另外一种形式”from模块import函数”,这样就可以直接使用函数,而不需要模块名作为前缀。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">>>> from math import floor</div><div class="line">>>> floor(3.8)</div></pre></td></tr></table></figure></li><li><p>运行Python程序,既可以通过IDLE来运行;也可以在命令行模式下,通过跳转至该文件目录下,然后执行相应命令来运行。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">C:\Users\xuxing\Desktop>python a.py</div></pre></td></tr></table></figure></li><li><p>如果希望自己的代码能够在UNIX下顺利执行,并且不需要显式使用Python解释器的话,可以把下面的内容放在脚本的首行即可:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">#!/usr/bin/env python</div></pre></td></tr></table></figure><p>不管Python二进制文件在哪里,程序都会自动执行(在文件具有可执行权限下)。在Windows系统中,让代码像普通程序一样运行的关键在于扩展名.py。</p></li><li>Python中的注释使用 <code>#</code>开头(汝应注释)。</li></ol>]]></content>
<tags>
<tag> Python </tag>
</tags>
</entry>
<entry>
<title>模型绑定(一)</title>
<link href="/2017/05/02/%E6%A8%A1%E5%9E%8B%E7%BB%91%E5%AE%9A-%E4%B8%80/"/>
<url>/2017/05/02/%E6%A8%A1%E5%9E%8B%E7%BB%91%E5%AE%9A-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>模型绑定是指,用浏览器以HTTP请求发送的数据来创建.NET对象的过程。每当定义具有参数的行为方法时,一直是依赖着这种模型绑定过程。这个参数对象是通过模型绑定来自请求中的数据创建的。<br><a id="more"></a></p><h3 id="理解模型绑定"><a href="#理解模型绑定" class="headerlink" title="理解模型绑定"></a>理解模型绑定</h3><ol><li>模型绑定是HTTP请求与C#方法之间的一个巧妙的销量,其中的C#方法在MVC中即为行为方法。</li><li>行为调用器会使用路由系统所给定的路由,判断为该请求进行服务所需要的行为方法。但此刻还不能调用,需要等待直到该方法的参数已经有了有用的值。默认的行为调用器ControllerActionInvoker,要依靠模型绑定器来生成调用行为所需要的数据对象。模型绑定器由IModelBinder接口所定义。</li><li>在一个MVC应用程序中,可以有多个模型绑定器,而每个绑定器负责绑定一个或多个模型类型。当行为调用器需要调用一个行为方法时,它会考察该方法所定义的参数,并查找各个参数类型所依赖的模型绑定器。</li><li>模型绑定必然经过一些步骤<ul><li>检测目标对象(要创建的对象,这种对象通常是行为方法的参数)的名称和类型。</li><li>通过对象名称查找数据源(请求),并找到可用数据(通常是字符串)。</li><li>根据对象类型将找到的数据值转换成目标类型。</li><li>通过对象名称、对象类型和这种经过处理的数据来构造目标对象。</li><li>将构造好的对象送给行为调用器,并由行为调用器将对象注入到目标行为方法中去。</li></ul></li><li>当行为调用器找不到绑定某个类型的自定义绑定器时,默认的模型绑定DefaultModelBinder便是由行为调用器所使用的一个绑定器。默认情况下,这个模型绑定器会搜索四个位置:Request.Form、RouteData.Values、Request.QueryString、Request.files。以获取与被绑定的参数名匹配的数据。这些位置被依序搜索。只要找到一个值,搜索便停止。</li></ol><h3 id="绑定简单类型"><a href="#绑定简单类型" class="headerlink" title="绑定简单类型"></a>绑定简单类型</h3><p>当处理简单参数类型时,DefaultModelBinder会尝试使用System.ComponentModel.TypeDescriptor类,将已经从请求数据中获得的字符串数值转换成参数类型。如果无法转换这个值,那么DefaultModelBinder便不能绑定该模型。对于值类型的参数,可以通过设置为可空类型或默认值来处理类型转换失败的问题。</p><h3 id="绑定复杂类型"><a href="#绑定复杂类型" class="headerlink" title="绑定复杂类型"></a>绑定复杂类型</h3><ol><li>当行为方法参数是复合类型时(即不能用TypeConverter类进行转换的属性)(能够用TypeConverter类进行转换的类型称为基元类型或简单类型)。DefaultModelBinder类将用反射来获取public属性集,然后依次进行绑定。</li><li><p>当复杂类型的某个属性是另外一个复杂类型时,在为该属性中的属性查找值时,模型绑定器查找的是该属性名与该属性中的属性名的组合。下面代码中,如果要查找grade属性中的name值,模型绑定器会去查找grade.name的值。(P560)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">public ActionResult Index(Person person)</div><div class="line">{</div><div class="line"> return View();</div><div class="line">}</div><div class="line">public class Person</div><div class="line">{</div><div class="line"> [DisplayName("姓名")]</div><div class="line"> public string name { set; get; }</div><div class="line"> [HiddenInput(DisplayValue =false)]</div><div class="line"> public string age { set; get; }</div><div class="line"> public bool gender { set; get; }</div><div class="line"></div><div class="line"> public Grade grade { set; get; }</div><div class="line">}</div><div class="line">public class Grade</div><div class="line">{</div><div class="line"> public string name { set; get; }</div><div class="line"> public string type { set; get; }</div><div class="line">}</div></pre></td></tr></table></figure></li><li><p>当参数为简单类型的数组或集合时,模型绑定器会去查找与参数具有同样名称的数据项,并创建一个包含这些值的数组。对于同步提交表单而言,只需要将表单的name设置为参数名称即可;对于异步提交表单而言,可以直接将数组赋值给参数名,此时http请求参数名的后面会多一个[],但是不影响后台获取集合的值。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">public ActionResult Index(IList<string> names)</div><div class="line">{</div><div class="line"> return View();</div><div class="line">}</div><div class="line"><input id="names" name="names" type="text" value=""></div><div class="line"><input id="names" name="names" type="text" value=""></div><div class="line"><input id="names" name="names" type="text" value=""> </div><div class="line">$.post("@Url.Action("Index")", {names:["张三","李四","王五"]},function(data)</div><div class="line">{</div><div class="line"> alert("123");</div><div class="line">})</div></pre></td></tr></table></figure></li><li><p>当参数为复杂类型的数组或集合时,模型绑定器会利用name标签属性中的数组索引前缀获取对象的属性值。以[0]为前缀的那些属性表示第一个对象,以[1]为前缀的那些属性则表示第二个对象,以此类推。</p></li><li>有些时候,生成的HTML与一种类型的对象有关,但希望将其绑定到另一个对象。这意味着,视图包含的前缀与模型绑定器期望的结构不对应,于是对数据不能做适当的处理。可通过对行为方法的参数运用Bind特性来解决此类问题,目的是用它来告诉绑定器,应该查找哪一个前缀。(P563)</li><li>通过在行为方法参数上使用Bind特性也可以有选择性地绑定属性,可以防止一定的恶意攻击。(P563)</li></ol><h3 id="手工调用模型绑定"><a href="#手工调用模型绑定" class="headerlink" title="手工调用模型绑定"></a>手工调用模型绑定</h3><p>当行为方法定义了参数时,模型绑定过程是自动执行的,但也可以直接控制这一个过程。通过向UpdateModel方法传递想要绑定的类型实例参数,会试图用标准的绑定过程来获取其public属性的值。当手工调用绑定过程时,可以将绑定过程限制到一个单一的数据源。(P570)<br>用户难免会提供一些不能绑定到相应模型属性的值–如,无效日期或对数值输入文本等。当使用手工调用模型绑定时,需要负责处理诸如此类的错误。模型绑定器通过抛出InvalidOperationException异常来表示绑定错误。错误的细节通过ModelState特性来进行检查。</p><h3 id="定制模型绑定系统-P571"><a href="#定制模型绑定系统-P571" class="headerlink" title="定制模型绑定系统(P571)"></a>定制模型绑定系统(P571)</h3>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>辅助器方法(三)</title>
<link href="/2017/04/14/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%B8%89/"/>
<url>/2017/04/14/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%B8%89/</url>
<content type="html"><![CDATA[<h3 id="URL和Ajax辅助器方法"><a href="#URL和Ajax辅助器方法" class="headerlink" title="URL和Ajax辅助器方法"></a>URL和Ajax辅助器方法</h3><ol><li>视图最基本的任务之一是创建链接或URL,使用户能随之进入应用程序的其他部分。有很多用于创建链接和URL的方法。(P527)</li><li>MVC框架包含了对渐进式Ajax(Unobtrusive Ajax)内建的支持这意味着你将使用辅助器方法来定义Ajax特性,而不必在整个视图中添加代码块。<a id="more"></a></li><li>使用渐进式Ajax特性需要两个前置条件,首先需要在Web.config文件中,configuration/appSettings元素含有一个用于UnobtrusiveJavaScriptEnabled属性的条目,并将其设置为true(创建项目时,默认就是true)。其次在使用之前要引用响应的JavaScript库</li><li>MVC框架支持Ajax表单的核心在于Ajax.BeginForm辅助器方法,它可以接受一个AjaxOptions对象作为其参数,可以在视图的开始处,以Razor代码块的形式创建AjaxOptions对象,也可以在调用Ajax.BeginForm时,内联地创建它们。</li><li>System.Web.Mvc.Ajax命名空间中的AjaxOptions类定义了一组属性,能够用来配置如何形成发送给服务器的异步请求,以及对取回的数据做哪些处理。(P534)</li></ol><h3 id="设置Ajax选项"><a href="#设置Ajax选项" class="headerlink" title="设置Ajax选项"></a>设置Ajax选项</h3><ol><li>确保优雅降级:如果用户禁用JavaScript时,可以通过表单同步提交的形式提交数据。解决这一问题最简单的办法是使用AjaxOptions.Url属性,以便指定异步请求的目标URL作为Ajax.BeginForm方法的参数,而不是以行为名称作为其参数(主要用于安全性要求比较高的用户)。</li><li>在Ajax请求期间给用户提供反馈:使用Ajax的一个缺点是用户观察不到正在发生的事情,因为发送给服务器的请求是在后台形成的。通过使用AjaxOptions.LoadingElementId和AjaxOptions.LoadingElementDuration属性,可以通知用户,此刻正在执行一个请求。</li><li>请求之前对用户进行提示:AjaxOptions.Confirm属性可以用来指定一条消息,用来在每个异步请求之前对用户进行提示。用户可以选择继续或取消该请求。由于每次请求都会进行提示,意味着这一特性应当保守使用,以免惹恼用户。</li><li>创建Ajax链接:除了表单之外,渐进式Ajax也可以用于创建异步执行的a元素。这一机制十分类似于Ajax表单的工作方式(P539)。</li><li>使用Ajax回调:AjaxOptions类定义了一组属性,能够在Ajax请求生命周期的各个点上调用JavaScript函数。每个AjaxOptions回调属性都与JQuery库所支持的一个Ajax事件相关联。这些事件可以用于任何目的:操作HTML DOM、触发额外请求等。可以用这种回调去做的最有用的事情之一是处理JSON数据。</li></ol><h3 id="使用Json"><a href="#使用Json" class="headerlink" title="使用Json"></a>使用Json</h3><ol><li>到目前为止,服务器都是渲染HTML片段,并把它们发送给浏览器。这是一种完全可接受的技术,但有点冗长(因为服务器随数据一起发送了HTML元素),而且它限制了浏览器端用这些数据可做的事情。</li><li>如果返回的数据是非私有的(private),才应该使用JsonRequestBehavior.AllowGet。由于许多Web浏览器中的安全性问题,第三方网站有可能截取响应GET请求所返回的JSON数据,这是JsonResult默认不响应GET请求的原因。</li><li><p>为了处理从MVC框架的应用程序服务器接收到的JSON数据,可以用AjaxOptions类中的OnSuccess回调属性,以指定一个JavaScript函数。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">//回调函数:</div><div class="line"> function handleJson(data)</div><div class="line">{</div><div class="line"> $("#hhss").text(data.name);</div><div class="line">}</div><div class="line">//为OnSuccess属性赋值:</div><div class="line">new AjaxOptions { HttpMethod="Post",OnSuccess="handlejson",LoadingElementId= "loading",Confirm="确认提交吗" }</div></pre></td></tr></table></figure></li><li><p>如果想要处理JSON数据,则不能同时指定UpdateTargetId属性。否则渐进式Ajax特性会尝试将其取自服务器的JSON数据作为HTML来处理。(P546)</p></li></ol>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>杂项(一)</title>
<link href="/2017/03/29/%E6%9D%82%E9%A1%B9-%E4%B8%80/"/>
<url>/2017/03/29/%E6%9D%82%E9%A1%B9-%E4%B8%80/</url>
<content type="html"><![CDATA[<h3 id="Newtonsoft"><a href="#Newtonsoft" class="headerlink" title="Newtonsoft"></a>Newtonsoft</h3><ol><li>JsonConvert对象下的SerializeObject()和DeserializeObject()方法为序列化和反序列化提供了简单支持。其内部还是使用了JsonSerializer对象。<a id="more"></a></li><li><p>为了更好的控制对象的序列化,可以直接使用JsonSerializer对象,JsonSerializer可以通过JsonTextReader和JsonTextWriter对象直接对一个包含JSON文本的流进行读和写操作。JsonSerializer有很多属性用来自定义序列化的格式。同时这些属性也可以通过JsonSerializerSettings对象作为JsonConvert中方法的参数来进行使用。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Person personObject = new Person { Name = "张三", CurrentTime = DateTime.Now };</div><div class="line">JsonSerializerSettings jsonsettings = new JsonSerializerSettings()</div><div class="line">{</div><div class="line"> DateFormatHandling = DateFormatHandling.MicrosoftDateFormat,</div><div class="line"> DefaultValueHandling = DefaultValueHandling.Ignore</div><div class="line">};</div><div class="line">string person = JsonConvert.SerializeObject(personObject,jsonsettings);</div></pre></td></tr></table></figure></li><li><p>JsonSerializer对象的部分属性解释:</p><ul><li><p>DateFormatHandling:控制日期的序列化格式。IsoDateFormat,默认Json.NET输出ISO 8601标准格式的日期字符串;MicrosoftDateFormat,输出微软JSON格式的日期字符串。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">IsoDateFormat:"2012-03-21T05:40Z" </div><div class="line">MiscrosoftDateFormat:"\/Date(1198908717056)\/"</div></pre></td></tr></table></figure></li><li><p>MissingMemberHandling:控制对缺少成员的处理(即JSON包含不存在于对象之中的成员,用于处理反序列化)。Ignore,默认Json.NET在反序列化时会忽略掉不存在于对象之中的成员;Error,当出现缺少的成员时抛出异常。</p></li><li>ReferenceLoopHandling:控制对循环引用的处理。</li><li>NullValueHandling:控制对null的处理。Include,默认Json.NET会在序列化和反序列化中包含空值;Ignore,在序列化和反序列化的时候会忽略掉空值属性。</li><li>DefaultValueHandling:控制对默认值的处理。和NullValueHandling的处理一样.</li><li>其余的一些属性并不是很常用。(<a href="http://www.newtonsoft.com/json/help/html/SerializationSettings.htm" target="_blank" rel="external">http://www.newtonsoft.com/json/help/html/SerializationSettings.htm</a>)</li></ul></li></ol>]]></content>
<tags>
<tag> 杂项 </tag>
</tags>
</entry>
<entry>
<title>辅助器方法(二)</title>
<link href="/2017/03/24/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%BA%8C/"/>
<url>/2017/03/24/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%BA%8C/</url>
<content type="html"><![CDATA[<h3 id="内建Form辅助器方法"><a href="#内建Form辅助器方法" class="headerlink" title="内建Form辅助器方法"></a>内建Form辅助器方法</h3><ol><li>和ActionLink方法一样,BeginForm方法中的action属性值也是由MVC框架去判断进行路由选择。可以使用BeginRouteForm方法来指定使用特定的路由。</li><li><a id="more"></a></li><li>有一些基本的HTML辅助器方法用于创建input元素(P495),其中要注意CheckBox辅助器渲染了两个input元素。它渲染了一个checkbox和随后的一个同名的隐藏域。这是因为浏览器在检查checkbox未作出选择时,不会递交checkbox的值。有了这个隐藏控件,可以确保MVC框架在作出选择后,从这个隐藏域中获取一个值。</li><li>可以根据模型属性创建input元素,即在辅助器方法中只接收一个单一的字符串参数,该参数即属性名,MVC框架会尝试找出与这个属性名相关联的某个数据项。这将检查以下几个位置:ViewBag.DataValue和@Model.DataValue(P479)。</li><li>每个基本的input辅助器,都有一个对应的强类型辅助器(P498)。这些辅助器只能用于强类型视图,它们以lambda表达式进行工作。传递给表达式的值是视图模型对象,以及可选的字段或属性,它们将被用于设置value标签属性。</li><li>也有用来创建select元素的辅助器方法,这些可以用于从一个下拉列表选择一个单项,或表现一个允许多选的多项select元素。和其他表单元素一样,这些辅助器也有弱类型和强类型版本(P499)。</li></ol><h3 id="模板辅助器方法"><a href="#模板辅助器方法" class="headerlink" title="模板辅助器方法"></a>模板辅助器方法</h3><p>类似Html.CheckBoxFor和Html.TextBoxFor等辅助器,它们生成了特定类型的HTML元素,这意味着笔者必须进一步决定,该使用哪种类型的元素去表现模型属性,并且在属性的类型发生变化时,要手动更新视图。利用模板辅助器方法,我们可以指定想要显示的属性,而让MVC框架去判定应该使用什么样的HTML元素。</p><ol><li>Html.Editor和Html.EditorFor是用来生成编辑器或编辑器元素的。(编辑器是指对模型对象进行数据编辑的HTML元素或标记)。</li><li>Html.Label和Html.LabelFor是用来生成标签的,默认情况下,将属性名作为标签的内容。</li><li>Html.Display和Html.DisplayFor方法默认情况下并未生成HTML元素。它们只是生成了它们所操作的属性值。</li><li>采用模型元数据为这些辅助器提供指导,告诉它们如何处理模型类型,可以使模板辅助器得到改善。元数据是用C#特性来表示的,通过特性及其参数值,给视图辅助器提供一系列指令。将元数据运用于模型类,辅助器方法在生成HTML元素时,会参考这些元数据。(512)<ul><li>大多数模型类至少都有一个这样的属性,它们通常与底层存储机制有关——由关系数据库管理的主键就是这样的。可以使用HiddenInput特性让辅助器渲染一个隐藏的input字段。此时辅助器会将该属性渲染成一个只读标签,如果想完全隐藏一个属性,可以将特性中的DisplayValue属性的值设置为false。</li><li>为了将一个属性从生成的HTML中排除掉,可以使用ScaffoldColumn特性。HiddenInput特性是将一个属性的值包含在一个隐藏的input元素中,而ScaffoldColumn特性允许开发人员针对支架过程将一个属性标记为完全禁止。(该特性对单属性辅助器无效)</li><li>默认情况下,Label、LabelFor、LabelForModel,以及EditorForModel辅助器会以属性名作为它们生成的标签元素的内容。可以通过DisplayName特性和Display特性来指定标签元素的内容。</li><li>也可以使用元数据来格式化模型属性的值。即DataType特性,DataType特性以DataType枚举中的一个值作为参数,注意:这些值的效果依赖于它们所关联的属性类型。如DataType.Date(P516)</li><li>为了渲染一个属性的HTML,可以用UIHint特性来指定希望使用的模板。(P517)</li></ul></li><li>模板辅助器方法只对简单类型(Primitive Type)进行操作,即那些能够用System.ComponentModel.TypeDescription类中的GetConverter方法通过一个字符串值进行解析的那些类型。因此,如果要渲染一个复合属性的HTML,必须通过单独调用模板辅助器方法,明确地处理复合属性。(P520)</li></ol><h3 id="定制模板视图辅助器系统"><a href="#定制模板视图辅助器系统" class="headerlink" title="定制模板视图辅助器系统"></a>定制模板视图辅助器系统</h3><p>我们可以创建自定义编辑器模板,也可以把内建的模板替换掉。(P521)</p>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>辅助器方法(一)</title>
<link href="/2017/03/22/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%B8%80/"/>
<url>/2017/03/22/%E8%BE%85%E5%8A%A9%E5%99%A8%E6%96%B9%E6%B3%95-%E4%B8%80/</url>
<content type="html"><![CDATA[<p>辅助器方法,其作用是对代码块和标记进行打包,以便能够在整个MVC框架应用程序中重用。<br><a id="more"></a></p><h3 id="自定义辅助器方法"><a href="#自定义辅助器方法" class="headerlink" title="自定义辅助器方法"></a>自定义辅助器方法</h3><ol><li><p>可以采用两种不同的技术来创建自定义辅助器方法:</p><ul><li><p>最简单的一种辅助器方法是内联辅助器,它是在视图中进行定义的。可以使用@helper标签创建一个内联辅助器。类似于规则的C#方法,内联辅助器具有名称和参数。虽然内联辅助器看上去像一个方法,但它没有返回值。辅助器体的内容被处理,并被放到对客户端的响应之中。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">@*定义内联辅助器方法*@</div><div class="line">@helper ListArrayItems(string[] items)</div><div class="line">{</div><div class="line"> foreach (var item in items)</div><div class="line"> {</div><div class="line"> <b>@item</b></div><div class="line"> }</div><div class="line">}</div><div class="line">@*使用内联辅助器方法(这里不需要将ViewBag的动态属性转换成相应的类型)*@</div><div class="line">@ListArrayItems(ViewBag.Fruits)</div></pre></td></tr></table></figure></li><li><p>内联辅助器只能在一个视图中使用,且如果内联辅助器过于复杂,可能会占据视图,而使视图难以阅读。一个可选的办法是创建外部的HTML辅助器方法,它被表示成C#的扩展方法。外部辅助器方法使用更为广泛,但编写起来也更难一些,因为C#不能自然地处理HTML元素的生成。(P481)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">public static MvcHtmlString ListArrayItems(this HtmlHelper htmlHelper,string[] items)//this关键字为前缀,扩展方法</div><div class="line">{</div><div class="line"> TagBuilder tag = new TagBuilder("ul");</div><div class="line"> foreach (var item in items)</div><div class="line"> {</div><div class="line"> TagBuilder itemTag = new TagBuilder("li");</div><div class="line"> itemTag.SetInnerText(item);</div><div class="line"> tag.InnerHtml += itemTag.ToString();</div><div class="line"> }</div><div class="line"> return new MvcHtmlString(tag.ToString());</div><div class="line">}</div></pre></td></tr></table></figure></li></ul><p>通过HtmlHelper对象的ViewContext属性可以获取与请求相关的一些数据。在一个辅助器方法中,创建HTML最容易的方式是使用TagBuilder类,它能够建立HTML字符串,而不需要处理各种转移及特殊字符。(P482)<br>这里需要确保引用了辅助器扩展方法所在的命名空间。可以使用@using标签实现,也可以将命名空间添加到/Views/Web.config文件,以使它们在视图中总是可用的。</p></li><li>Razor会自动对视图中使用的数据值进行编码,它可以防止数据值被浏览器解释为有效的标记–这是一种常见的攻击形式的基础,恶意用户会试图添加他们自己的HTML标记或JavaScript代码,以试图破坏应用程序的行为。但是,辅助器却需要生成HTML。因此,视图引擎对它们给予更高的信任等级。</li><li>有两种不同的方式可以对辅助器方法的内容进行编码:一种是将辅助剂方法的返回类型改为string,这会警告视图引擎所生成的内容是不安全的,在将其添加到视图以前,应该先进行编码;以上技术会使Razor对辅助器返回的所有内容那个进行编码,有时这并不是我们所需要的,我们可能只想对部分内容进行编码,此时可以使用HtmlHelper类的Encode方法。</li></ol><h3 id="内建Form辅助器方法"><a href="#内建Form辅助器方法" class="headerlink" title="内建Form辅助器方法"></a>内建Form辅助器方法</h3><ol><li><p>Html.BeginForm和Html.EndForm是用来创建HTML的form标签的,并且会根据应用程序的路由机制,为这个form生成一个有效的action标签属性。EndForm辅助器只有一个定义,它只是给视图添加<code></form></code>,以关闭表单元素。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">@{Html.BeginForm();}@*这里需要将辅助器方法的调用处理成一条C#语句*@</div><div class="line">@{ Html.EndForm();}</div></pre></td></tr></table></figure></li><li><p>创建form标签最常用的方式是将BeginForm辅助器方法的调用封装在一个using表达式中。在using块的最后,.NET运行时会在BeginForm方法返回的对象上调用Dispose方法,这会为你调用EndForm方法。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">@using (Html.BeginForm("Index","Home",FormMethod.Post,new { @class="btn",data_type="123"}))</div><div class="line"> {</div><div class="line"> @: 123</div><div class="line"> }</div></pre></td></tr></table></figure></li><li><p>在代码中不能指定带连字符的动态对象,此时可以使用下划线,该下划线会自动地映射为连字符。同时添加标签属性时,在class前面要使用@前缀,因为class为C#的保留关键字。</p></li></ol>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>视图(一)</title>
<link href="/2017/03/20/%E8%A7%86%E5%9B%BE-%E4%B8%80/"/>
<url>/2017/03/20/%E8%A7%86%E5%9B%BE-%E4%B8%80/</url>
<content type="html"><![CDATA[<h3 id="视图基础"><a href="#视图基础" class="headerlink" title="视图基础"></a>视图基础</h3><ol><li>通过实现两个接口,便能够创建一个自定义视图引擎(P455)。视图引擎的复杂性来自于视图模板系统,这包括代码片段、支持布局,以及为优化性能而对模板进行的编译等。在Razor中,几乎所有MVC应用程序所需要的功能都是可用的。<a id="more"></a></li><li>视图的目的,是让开发人员将域模型部分的内容渲染成用户界面,为了实现这一目的,需要对视图添加动态内容。动态内容是在运行时生成的,并且可以随每个请求而不同。</li><li>可以以不同方式对视图添加动态内容:<ul><li>内联代码:用于小型的、自包含视图逻辑的片段,如if和foreach语句。这是在视图中创建动态内容的基本手段。</li><li>HTML辅助器方法:用于生成一个独立的HTML元素或小片元素集合,典型地,是基于视图模型或视图数据的值。</li><li>分段:用于创建内容分段,这种分段用于插入到布局中的特定位置。</li><li>分部视图:用于在视图之间共享的子片段标记。分部视图也可以含有内联代码、HTML辅助器方法,以及引用其他分部视图。分部视图不调用行为方法,因此它们不能用来执行事务逻辑。</li><li>子行为:用于创建可重用的UI控件,或需要含有事务逻辑的小部件。当使用子行为时,它调用一个行为方法,返回一个视图,并把结果注入到响应流中。</li></ul></li></ol><h3 id="使用分段"><a href="#使用分段" class="headerlink" title="使用分段"></a>使用分段</h3><p>Razor引擎支持分段的概念,这让你能够提供一个布局中的内容区域。Razor分段能够灵活地控制将视图的哪一部分插入到布局之中,以及把他们插入到哪儿。通过使用Razor的@section标签后跟一个分段名称的办法定义了分段。分段的内容可以混合HTML标记和Razor标签。可以在布局中使用@RenderSection辅助器方法来指定分段要插入的位置。</p><ul><li>一个视图只能定义在布局中被引用的分段。如果试图在视图中定义布局中无对应@RenderSection辅助器调用的分段,MVC框架会抛出异常。</li><li>一般情况下不要把分段和视图中的其余部分混杂在一起。其约定是,在视图的开始或结尾定义分段,以便更容易看到哪些内容区域被处理成分段,以及哪些将要由RenderBody辅助器来捕捉。</li><li>把视图定义成一个个独立的分段,有利于建立更清晰的视图,并减少了RenderBody捕捉无关内容的情况。为了使用这种方法,我们需要用RenderSection替换RenderBody辅助器的调用</li><li>可以在布局页中使用IsSectionDefined方法,来避免RenderSecion调用视图中未定义的分段。但更优雅的方法是使用可选分段,即给RenderSection方法传递一个附加的bool值,false代表该分段可选。</li></ul><h3 id="使用分部视图"><a href="#使用分部视图" class="headerlink" title="使用分部视图"></a>使用分部视图</h3><p>通常需要在应用程序中多个不同的地方,使用同样的Razor标签和HTML标记片段。采取的办法不是重复这些标记,而是采用分部视图。分部视图是含有标签和标记片段的独立的视图,并且可以被包含在其他视图之中。可以在另一个视图中调用Html.Partial这一辅助器方法,来使用一个分部视图。</p><ul><li>Razor视图引擎对分部视图的查找方式,与规则视图相同(即在视图中的当前控制器所在文件夹和shared文件夹中进行查找),这意味着,你可以创建控制器专用的特殊版本的分部视图。但分部视图最经常的用途之一就是在布局页中渲染内容。</li><li>可以创建强类型分部视图,然后在渲染这个分部视图时,传递要使用的视图模型对象。</li></ul><h3 id="使用子行为"><a href="#使用子行为" class="headerlink" title="使用子行为"></a>使用子行为</h3><p>子行为是通过视图调用的行为方法。当希望将某种控制器逻辑用于应用程序的多个地方时,子行为可以让你避免重复的控制器逻辑。子行为与行为之间的关系,如同分部视图与视图一样。无论何时,当希望显示某些数据驱动的”小部件”,这些”小部件”要出现在多个页面上,而且含有与主行为无关的数据时,可能会希望使用子行为。</p><ul><li>任何一个行为都可以作为子行为,使用ChildActionOnly特性可以确保一个行为方法只能在一个视图中作为一个子行为进行调用。使用该特性,可以防止该行为方法作为用户请求的一个结果被调用。渲染子行为的方法为Html.Action辅助器。</li></ul>]]></content>
<tags>
<tag> MVC </tag>
</tags>
</entry>
<entry>
<title>游标</title>
<link href="/2017/03/20/%E6%B8%B8%E6%A0%87/"/>
<url>/2017/03/20/%E6%B8%B8%E6%A0%87/</url>
<content type="html"><![CDATA[<p>在关系型数据库中,我们对于查询的思考是面向集合的。而游标打破了这一规则,游标使得我们思考方式变为逐行进行。<br>游标使得开发人员变懒,懒得去想用面向集合的查询方式实现某些功能;同样的,在性能上,游标会吃更多的内存,减少可用的并发,占用带宽,锁定资源,当然还有更多的代码量。(因此避免使用游标)<br><a id="more"></a></p><h3 id="游标实现"><a href="#游标实现" class="headerlink" title="游标实现"></a>游标实现</h3><ol><li>定义游标:游标其实可以理解成一个定义在特定数据集上的指针,我们可以控制这个指针遍历数据集,或者仅仅是指定特定的行,所以游标是定义在以select开始的数据集上的</li><li>游标分为游标类型和游标变量,对于游标变量来说,遵循T-SQL变量的定义方法。游标变量支持两种赋值方法,定义时赋值和先定义后赋值,对于全局游标变量,只支持定义时赋值且不能在游标名称前加”@”。</li><li><p>实现游标的过程</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">declare @ResourceId varchar(10),@ResourceName varchar(20);</div><div class="line">declare customcursor cursor --声明游标并赋值(默认是全局)</div><div class="line">for </div><div class="line">select top 5 ResourceId,ResourceName from ResourceMenu;</div><div class="line"></div><div class="line">open customcursor; --打开游标</div><div class="line"></div><div class="line">fetch next from customcursor into @ResourceId,@ResourceName;</div><div class="line">while @@FETCH_STATUS=0</div><div class="line">begin</div><div class="line"> print @ResourceId+'--------------'+@ResourceName;</div><div class="line"> fetch next from customcursor into @ResourceId,@ResourceName;</div><div class="line">end</div><div class="line">close customcursor; --关闭游标</div><div class="line">deallocate customcursor; --释放游标</div></pre></td></tr></table></figure></li><li><p>游标的使用分为两部分,一部分是操作游标在数据集中的指向,另一部分是对游标所指向的行的部分或全部内容进行操作。可以通过into关键字将该行的值传入局部变量。</p></li><li>游标经常会和全局变量@@FETCH_STATUS与WHILE循环来共同使用,以达到遍历游标所在数据集的目的。</li></ol>]]></content>
<tags>
<tag> T-SQL </tag>
</tags>
</entry>
<entry>
<title>过滤器</title>
<link href="/2017/03/16/%E8%BF%87%E6%BB%A4%E5%99%A8/"/>
<url>/2017/03/16/%E8%BF%87%E6%BB%A4%E5%99%A8/</url>
<content type="html"><![CDATA[<p>过滤器把附加逻辑注入到MVC框架的请求处理。它们提供了一种简单而雅致的方式,实现了面向切面编程。所谓面向切面,是指可以用于整个应用程序,而又不适合放置在某个局部位置的功能,否则会打破关注分离模式。过滤器是.NET的特性,它们对请求处理管道添加了额外的步骤。<br><a id="more"></a></p><h3 id="过滤器类型"><a href="#过滤器类型" class="headerlink" title="过滤器类型"></a>过滤器类型</h3><p>过滤器可以应用于个别行为方法,也可以应用于整个控制器。MVC框架支持5种不同类型的过滤器:</p><ol><li>认证过滤器,最先运行,在任何其他过滤器或行为方法之前,但在授权过滤器之后可以再次运行。</li><li>授权过滤器,在认证过后,其他过滤器或行为方法之前,第二个运行。</li><li>行为过滤器,在行为方法之前和之后运行。</li><li>结果过滤器,在行为结果被执行之前和之后运行。</li><li>异常过滤器,仅在其他过滤器、行为方法或行为结果抛出异常时运行。<br>在MVC框架调用一个行为之前,会首先检测该方法的定义,以查看它是否有实现上述过滤器的特性。如果有,那么便在请求处理程序的相应点上调用这些接口所定义的方法。框架包含了一些默认的特性,它们实现了过滤器的接口。<br>ActionFilterAttribute类既实现了IActionFilter接口,也实现了IResultFilter接口。这是一个抽象类,它要求你必须提供一个实现。</li></ol><h3 id="授权过滤器"><a href="#授权过滤器" class="headerlink" title="授权过滤器"></a>授权过滤器</h3><p>这些过滤器执行你的授权策略,以确保行为方法只被以认证用户所调用。授权过滤器需要实现IAuthorizationFilter接口,尽量不要直接实现该接口来完成授权功能,因为编写安全性代码是非常危险的,更好的做法是,继承AuthorizeAttribute类。<br>可通过重写AuthorizeCore方法,来进行授权逻辑的编写,想要对请求进行授权则返回true,否则返回false。<br>对于大多数应用程序而言,AuthorizeAttribute提供的授权策略已经足够。</p><h3 id="异常过滤器"><a href="#异常过滤器" class="headerlink" title="异常过滤器"></a>异常过滤器</h3><p>异常过滤器必须实现IExceptionFilter接口,当一个未处理异常出现时,OnException方法被调用。<br>被抛出的异常可以通过Exception属性进行操作,将ExceptionHandled属性设置为true,异常过滤器可以报告它已经处理了该异常。但即使这个属性被设置为true,应用于一个行为的所有异常过滤器仍会被调用。因此,良好的习惯做法是检查另一个过滤器是否已经处理了该问题,以免恢复另一个过滤器已经解决的问题。<br>Result属性由异常过滤器使用,以告诉MVC框架要做什么。异常过滤器的两个主要应用是对异常进行日志记录,并将适当的消息显示给用户。<br>MVC框架中包含了HandleErrorAttribute类,它是内建的IExceptionFilter接口实现,通过它,你可以使用ExceptionType、View、Master属性来指定一个特定的异常、视图和布局名称。只有在Web.config文件中启用了自定义错误时,HandleErrorAttribute过滤器才会生效,通过在system.web节点中添加一个customErrors属性实现。<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><system.web></div><div class="line"> <customErrors mode="On" defaultRedirect="~/404.html"/><!--默认mode为RemoteOnly--></div><div class="line"></system.web></div></pre></td></tr></table></figure></p><p>在渲染错误页面视图时,HandleError过滤器会传递一个HandleErrorInfo视图模型对象,这是一个封装了异常细节的对象,它提供了可在视图中使用的附加信息。(P413)</p><h3 id="行为过滤器"><a href="#行为过滤器" class="headerlink" title="行为过滤器"></a>行为过滤器</h3><p>行为过滤器是可以被用于任何目的的多用途过滤器,创建这种类型过滤器的内建类是IActionFilter。可以在行为过滤器中取消请求、修改请求,或启动一些跨越行为调用期间的活动。系统内建了一个可以用来创建行为过滤器和结果过滤器的类,该类为ActionFilterAttribute,注意该类是一个抽象类(P414)。</p><h3 id="其他过滤器特性"><a href="#其他过滤器特性" class="headerlink" title="其他过滤器特性"></a>其他过滤器特性</h3><ol><li>使用过滤器的常规办法是在行为或控制器上加相应的特性。但是Controller基类也实现了IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter接口。它还对每一个OnXXX方法提供了空白虚拟实现,因此直接重写这些方法即可。当你要创建一个基类,以派生项目中的多个控制器时,这项技术是最有用的。</li><li>全局过滤器被运用于应用程序的所有行为方法,一般App_Start文件夹用于应用程序范围的配置,如路由,为了创建等效的过滤器,可以在App_Start文件夹中的FilterConfig类中进行全局过滤器的添加(默认创建MVC项目时自动添加,没有可手动添加,添加后需要检查全局文件中是否注册了过滤器配置)。–在这里要注意命名空间(P422)</li></ol>]]></content>
<tags>
<tag> MVC </tag>