.NET应用程序调试—原理、工具、方法

222 篇文章 1 订阅
订阅专栏

阅读目录:

  • 1.背景介绍
  • 2.基本原理(Windows调试工具箱、.NET调试扩展SOS.DLL、SOSEX.DLL)
    • 2.1.Windows调试工具箱
    • 2.2..NET调试扩展包,SOS.DLL、SOSEX.DLL
    • 2.3.调试系统的基本流程及架构(.NETDAC概念、mscordacwks.dll)
    • 2.4.VisualStudio中集成扩展调试(更加细粒度的调试程序)
  • 3.调试程序类型(客户端程序、服务端程序)
  • 4.调试方式及场景
    • 4.1.本机调试(Attach Process,调试器启动)
    • 4.2.不中断调试或者称事后调试(对Dump文件进行调试)
  • 5.一般调试步骤
    • 5.1.设置符号文件(公有符号、私有符号)
    • 5.2.加载.NET程序扩展调试包(SOS.DLL、SOSEX.DLL)
    • 5.3.调试的三种命令类型(标准命令、元命令、扩展命令)
  • 6.调试扩展的几个比较常用的命令(SOS.DLL、SOSEX.DLL)
  • 7.简单示例,常见的线上两类问题
    • 7.1.内存问题(内存偏高,内存溢出)
    • 7.2.线程问题(CPU过高,线程死锁)
  • 8.获取Dump文件时的重要注意事项
  • 9.总结

1.背景介绍

随着应用程序的复杂度不断上升,要想将好的设计思想稳定的落实到线上,我们需要具备解决问题的能力。需要具备对运行时的错误进行定位且快速的解决它的能力。本篇文章我将分享一下我对.NET应用程序调试方面的学习和使用总结。

其实对调试程序的使用是不难的,关键是知道它的调试原理才行,因为调试一个程序或者dump文件,都需要了解一定的.NET调试的原理才行,比如你在附加到进程调试时在执行某个SOS扩展命令是需要切换到指定线程上的,而调试dump文件就不需要,但是对Dump文件的分析有些SOS扩展命令是不能用的,类似这样的问题,一旦出现你就一头雾水,所以花点时间学习一下原理是有必要的。

2.基本原理(Windows调试工具箱、.NET调试扩展SOS.DLL、SOSEX.DLL)

在Windows平台上调试应用程序首选Windows调试工具箱,该工具箱包含了一套专门用来针对Windows进行很多复杂场景调试所需要的工具和组件。需要注意的是此工具箱是针对于非托管.NET平台用的,意思就是说此工具箱的所有工具和组件默认是不能够进行.NET应用程序调试的,只能用来对原生Windows程序进行调试。

那么.NET平台也并不是有自己一套专用的调试工具箱,毕竟.NET还是属于Windows平台的,所以很大部分的运行时原理还是基于Windows的,要想在原生的调试器中对.NET这个具有虚拟运行时程序进行调试就需要专门的翻译器才能够执行。SOS.DLL、SOSEX.DLL这两个就是用来对.NET程序在Windows调试工具中起到翻译作用的调试器扩展。简单讲就是,这两个组件是.NET项目组专门开发出来用来对.NET应用程序进行方便调试用的,当然不用这两个扩展也能调试.NET程序,只不过就会很困难,会被很多细节束缚住。有了这个调试扩展之后,我们就可以让原生Windows调试器正确的翻译出.NET相关概念。

图1:(Windows调试工具执行流程)

所有对.NET程序发起的调试会话都要经过.NET调试扩展组件进行翻译才行,也就是要使用.NET调试扩展的调试命令来调试.NET程序。上图中,我们如果要想调试.NET程序就需要将.NET调试扩展组件加载到Windows调试工具中去,然后才能方便在Windows调试工具中使用。

2.1.Windows调试工具箱

Windows调试工具箱中包含了很多调试工具,都是用来辅助于我们进行方便调试用的。Windows调试工具箱分为两个执行版本,X86、X64这两个版本是专门用来分析不同的运行时环境的,如果你的分析环境是32位的你就需要使用X86的版本,同理,如果是用64位的环境就需要使用X64的版本。

下载地址为:http://www.microsoft.com/whdc/devtools/debugging/default.aspx

记住选择你需要的版本,建议你两个版本都下载,因为你随时需要针对Dump文件进行分析,而Dump文件是随时都有可能是两个版本。

Windows工具箱中的默认使用WinDbg.exe作为调试首选,它是一个GUI程序。

图2:(默认的Windows调试工具,WinDbg)

安装过后的菜单中就只有WinDbg作为调试选择。

这里需要注意的是,当你启动了WinDbg之后要留意程序的名字和标题,因为当你存在两个版本的WinDbg时会容易搞错,在调试时会有各种奇怪的问题出现,当你找了半天之后结果发现是因为用错了版本,那就正的无语了。

图3:(注意运行WinDbg的环境版本)

WinDbg是默认的调试工具,但是在工具箱中还有几个控制台调试工具,他们行必之下比较轻量简单,有些任务比较好执行,在配合cmd使用会很方便,比如工具箱中的tlist.exe用来查看进程信息的小工具就非常方便。

图4:(方便查看进程ID)

这样我们就可以很方便的attach到一个指定的进程进行调试。

Windows调试工具箱中有很多其他的工具,需要用的话可以使用cmd切换到当前安装的目录下:C:\Program Files\Debugging Tools for Windows (x86),或者你直接到工具的安装目录运行也行,这就看此工具是不是支持手动无参数启动了。

2.2..NET调试扩展包,SOS.DLL、SOSEX.DLL

.NET调试扩展包分为两个,一个是SOS.DLL,该扩展包是.NET平台的一部分,属于官方版本。而SOSEX.DLL是微软的一名叫“Steve Johnson”软件工程师开发,属于个人维护的,用来增强SOS.DLL功能的,在SOSEX.DLL有很多功能比较强大的扩展命令。

下载地址为:

32位: http://www.stevestechspot.com/downloads/sosex_32.zip

64位: http://www.stevestechspot.com/downloads/sosex_64.zip

具体的帮助文档可以查看该工程师的博客来了解详情。这两个版本用来调试不同环境的程序的,如果你的程序是运行在32位环境下,就用32位的SOSEX,同理,用在64位下就用64位SOSEX。

而SOS.DLL扩展包是跟着.NETFramework一起安装的,地址位于:C:\Windows\Microsoft.NET\Framework\v4.0.30319。如果你是64位系统的话地址就是:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319。在这两个地址下面都可以找到SOS.dll文件,不同的目录下对应于调试不同机器类型的.NET程序。

有了这两个扩展包之后就可以在WinDbg中对.NET程序进行分析了,具体使用我们后面会介绍。

2.3.调试系统的基本流程及架构(.NETDAC概念、mscordacwks.dll)

有一个很重要的原理我觉得很有必要讲一下,就是.NETDAC概念。

其实.NETDAC也就是.NET Data Access .NET数据访问层,这个是专门用来提供给SOS.DLL\SOSEXDLL或者其他调试扩展包使用的,所有的调试扩展组件必须通过这个DAC才能访问到.NET运行时的数据,所以在初次使用SOS的时候会经常碰见加载错误的mscordacwks.dll文件,此文件就是DAC的物理文件。

这个文件和SOS扩展文件一样,都有这不同的版本,当加载不同类型的.NET程序时会使用到不同版本的mscordacwks.dll文件,当然大部分情况下此文件时自动加载的,只有出现你分析的文件与生成调试文件的环境不一致时才会出现头疼的问题。

图5:(mscordacwks.dll位置)

当你知道这个组件是工作于此位置时,当出现跟它相关的错误提示时你就不需要担心了,无非就是文件加载的位置或者版本不匹配而已。

调试器会话、调试器注入线程

还有一点我觉得也很有必要介绍的就是有关调试器如何调试.NET程序的,当我们在使用调试器启动被调试程序或者将调试器附加到被调试进程时,其实调试器会注入一些线程到.NET程序中,让调试线程与.NET程序原本的线程在一个.NET执行环境中,这样的目的是能够起到最.NET程序在执行时的控制,比如中断执行,设置断点。当我们需要执行某些跟线程上下文相关的扩展命令时就需要切换到正确的线程上去。

图6:(调试器注入线程)

此时,调试器使用一个注入线程将.NET程序在执行时中断,原理就是通过发送线程中断命令来达到控制目标线程,那么首先要能够与原线程通讯才行,所以需要注入托管线程。(注意:注入的线程不一定就是托管.NET线程,严重它最好的方法就是查看所有所有的进程内线程和所有托管线程,对比一下就知道了。),其实这个ID为3的线程是调试器会话线程。

图7:(切换到原托管线程)

我们通过~0s命令切换到我们需要调试的原托管线程中,比如,在执行!ClrStack命令时,就需要切换到当前线程上执行。

我们需要验证它是否是注入了托管线程还是非托管线程。

图8:(托管线程列表)

使用!Threads命令可以查看进程内所有的托管线程,仅仅是托管线程,此命令是无法查看非托管线程的,接下来我们使用另外一个命令来查看所有的线程。

图9:(所有的执行时线程)

这样我们就可以判断出,调试器使用了ID位7的作为目前的调试会话线程。知道这些背后的原理很重要,当你在执行某个调试命令时你就会发现此命令是否需要在.NET线程中执行,还是说可以在调试器会话线程中执行,一般dump类的命令都是可以远程执行的,也就是说在调试器会话中执行,当需要跟踪.NET线程内部过程时就需要切换到.NET线程上去执行。

2.4.VisualStudio中集成扩展调试(更加细粒度的调试程序)

SOS扩展也是可以和VisualStudio进行集成的,这样真的方便了我们调试一些性能要求比较高的程序,当程序运行一段时间后我们用VS附加到进程,然后查看一些重要的对象数据,但是此时我们看不到.NET运行时的一些数据,比如:对象的代龄,托管堆的大小,线程池的任务等。通过集成SOS扩展会让我们对程序的运行时有了一个更加方便的跟踪。

图10:(打开本地代码调试)

设置断点,然后在”即时窗口“(调试->窗口->即时)中加载扩展SOS.DLL。

图11:(在VisualStudio2012中加载SOS.dll扩展)

这样的便利性大大提高我们在调试程序内存方面、线程方面的好处,我们可以适当的做压力测试,然后Attach process,执行SOS扩展命名来查看内存问题,当需要调试程序逻辑时在单步调式C#代码,一举两得。

3.调试程序类型(客户端程序、服务端程序)

.NET程序主要分为两类,一类是客户端程序,另一类是服务端程序。对于这两类程序来说前者调试时基本上可以通过附加进程的方式进行调试,而对于服务端程序则不行,因为服务程序通常是运行在一个复杂的线上环境中,我们没有任何权限或机会去接触,此时是通过获取进程的dump文件来进行分析。

客户端程序也大概分为控制台、Winform两种,服务端程序都是基于ASP.NET框架,宿主与IIS进程中。

4.调试方式及场景

针对不同类型的程序及场景需要使用不同的方式进行调试,客户端程序中的控制台程序基本上可以通过在调试器中启动的方式进行调试。如果是GUI程序则需要附加进程方式。服务端程序如果在条件允许下也是可以使用附加进程的方式进行调试的,但是这一般不太可能,因为一旦附加进程将block住所有的线程活动。

4.1.本机调试(Attach Process,调试器启动)

本机调试可以直接在调试器中启动程序,WinDbg打开后,在文件中有一个Open Executable,可以打开一个可执行文件。如果是使用NTSD控制台调试器,则需要在NTSD后面跟上程序的执行路径。

图12:(ntsd.exe打开调试程序)

同样,在WinDbg中也有一个附加进程的选项,NTSD也是一样,操作起来都比较简单,需要注意的是当你对进程进行附加时要清楚此进程是多少位的,然后你需要选择正确的调试器进行调试。

4.2.不中断调试或者称事后调试(对Dump文件进行调试)

在不能够对被调试程序直接调试时我们就需要此程序的进程镜像文件,此镜像文件就是进程在某一个时刻的快照,通过分析这个快照,我们也是可以定位出问题的。首先我们需要使用适当的工具来获取进程的dump文件,操作系统本身的任务管理器就有这个功能,dump文件的存放位置默认在用户信息临时文件下面,比如:XXX\Users\Administrator\AppData\Local\Temp,获取完dump文件后任务管理器会有提示路径的。

图13:(使用任务管理器获取dump文件)

图14:

使用任务管理器获取dump文件固然很方便,但是有一个问题就是如果当前机器是64位的,并且你的进程是以32位方式运行的,那么此时你获取出来的dump文件是64位的,当你通过32位的调试器无法进行分析,甚至会有各种其他的问题,这些问题就是因为获取dump文件的机器环境和你预想的不一致。这个时候我们希望能够通过很明了的方式来获取dump文件,就是通过调试器来获取dump文件。

通过调试器来获取dump文件有很多好处,可以设置很多选项,包括只获取进程的哪部分镜像数据等。

先通过tlist.exe查看所有进程列表,会有一个进程ID号,有了ID号才能进行获取。

图15:(tlist、ntsd 进入到指定进程中)

进入到ntsd调试器中,然后使用.dump/mf d:\order.dmp 命令获取dump文件到D盘。

图16:(使用NTSD.exe获取dump文件)

此时我们就成功的获取到了dump文件。

通过调试器获取dump文件比较稳定可靠,因为机器运行环境的不同,通过任务管理器获取的dump文件会存在一些无法预知的问题,你并不清楚,当前任务管理器是使用哪个版本的环境输出调试信息的。

有了dump文件之后就是通过调试工具打开就行了,WinDbg就有一个菜单专门打开dump文件的,Open Crash Dump。使用ntsd需要使用命令ntsd -z d:\order.dmp。

5.一般调试步骤

知道了调试的一些原理和工具之后我们来看一下调试的基本步骤,这些步骤都具体是指的什么意思,有哪些好处。

5.1.设置符号文件(公有符号、私有符号)

设置符号文件的目的是为了能够在调试器中正确的对应到源代码的位置和一些元数据信息。符号文件都是*.pdb文件名。符号文件分为公有和私有两种,公有的都是公司公开出去用于帮助调试用的,而私有的是公司内部使用的,为什么要区分公有和私有,是为了防止逆向工程。

图17:(设置符号文件路径)

首先通过.sympath d:,设置了符号路径为D盘,然后又使用.symfix+ d:,是设置私有符号路径,并且使用d盘为缓存路径。在最后一个红线中我们能看出来。

为什么使用.symfix 时要带上一个+号,其实是告诉调试器我们是多加一个符号位置,而不是覆盖原有符号位置。

设置好了两个符号位置后需要使用.reload命令来重新加载模块,这样调试器才会去符号位置去加载这些符号。

图18:(加载的符号文件)

调试器会自动的将公有符号下载到你刚才设置的缓存目录中。

5.2.加载.NET程序扩展调试包(SOS.DLL、SOSEX.DLL)

对.NET程序分析当然是需要加载SOS扩展了。加载SOS扩展有两个命令可以使用,第一个是.load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll,.load命令是要给出sos.dll绝对路径的。第二个是.loadby sos modulename,.loadby 命令是可以根据已经加载的模块名称来加载SOS.dll扩展。使用第一个命令有一个问题就是,我们需要人工的判断当前环境到底是需要什么版本的SOS扩展,而使用.loadby是可以根据已经加载的模块来自动的查找对应的SOS扩展。

0:000> .load C:\Windows\Microsoft.NET\Framework\v4.0.30319\SOS.dll

0:000> .loadby sos.dll clrjit

使用.loadby 命令很容易的就可以加载SOS扩展,而不需要自己去判断当前程序是.NET什么版本的。

5.3.调试的三种命令类型(标准命令、元命令、扩展命令)

在使用调试器调试程序时,所要使用的命令主要分为三类。

第一类是标准命令,就是不带任何符号开始的命令,比如:pb、lmvm。这一类命令是所有Windows调试工具箱中的调试工具通用的,不管你是使用ntsd还是winDbg都可以。

第二类命令是元命令,就是使用"."号开始的命令,这一类命令并不是在所有调试工具中通用的。第三类是扩展命令,扩展命令就是各个调试器扩展出来的命令,也就是以"!"开始的命令,如:!dumpheap -stat,!dumpstatcobjects。

6.调试扩展的几个比较常用的命令(SOS.DLL、SOSEX.DLL)

当然这个纯粹是我的个人感觉,排名不分先后。

!dumpheap -stat (查看托管堆统计信息)

0:000> !dumpheap -stat
Statistics:
      MT    Count    TotalSize Class Name
65366e78        1           12 
System.Collections.Generic.EnumEqualityComparer`1[[System.Web.Compilation.FolderLevelBuildProviderAppliesTo, 
System.Web]]
653667cc        1           12 
System.Collections.Generic.ObjectEqualityComparer`1[[System.Web.WebSockets.IAsyncAbortableWebSocket, 
System.Web]]
65365f08        1           12 
System.Lazy`1+Boxed[[System.Web.Security.Cryptography.AspNetCryptoServiceProvider, 
System.Web]]
65365a34        1           12 
System.Web.Security.Cryptography.HomogenizingCryptoServiceWrapper
65361e20        
1           12 System.Web.Configuration.CustomErrorsMode

!dumpheap -type  (查看某个类型在堆中的信息)

0:000> !dumpheap -type System.String

Address       MT     Size
10731228 624aacc0       14     
107312c4 624aacc0 22 
107312dc 624aacc0       78     
10731370 
624aacc0       28 

可以一眼看出哪些对象过大,这里我是为了演示而用,一般在项目开发中,我们都大概知道哪些对象可能会有内存问题,比如:同步数据时的缓存对象。

!dumpobj 10731228 (查看对象详情)

0:000> !dumpobj 10731228 
Name:        
System.String
MethodTable: 624aacc0
EEClass:     620b486c
Size:        
14(0xe) bytes
File:        
C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String:      

Fields:
      MT    Field   Offset                 Type VT     Attr    
Value Name
624ac480  40000aa        4         System.Int32  1 instance        
0 m_stringLength
624ab6b8  40000ab        8          System.Char  1 instance        0 m_firstChar
624aacc0  40000ac        c        
System.String  0   shared   static Empty
    >> Domain:Value  
00dbe558:NotInit  00e11c90:NotInit  00e5f040:NotInit  <<

!threads(查看托管线程)

0:000> !threads
ThreadCount:      17
UnstartedThread:  0
BackgroundThread: 12
PendingThread:    0
DeadThread:       5
Hosted Runtime: no
                                                    
Lock  
       ID OSID ThreadOBJ    State GC Mode     GC Alloc Context  Domain   Count Apt Exception
   7    1 43a8 00dc2620     28220 Preemptive  1484CA40:00000000 00dbe558 0     Ukn 
  15    2 4414 00dd38d0     2b220 Preemptive  00000000:00000000 00dbe558 0     MTA (Finalizer) 
  17    3 441c 00e09e88   102a220 Preemptive  00000000:00000000 00dbe558 0     MTA (Threadpool 
Worker) 
  18    4 4420 00e0ce80     21220 Preemptive  00000000:00000000 00dbe558 0     Ukn

当然还有很多其他很不错的命令,这里我个人觉得这几个比较常用,要想了解所有的命令可是在调试器中使用扩展命令!help来查看所有的命令帮助。

0:000> !help

-------------------------------------------------------------------------------

SOS is a debugger extension DLL designed to aid in the debugging of managed programs. Functions are listed by category, then roughly in order of importance. Shortcut names for popular functions are listed in parenthesis. Type "!help <functionname>" for detailed info on that function.

Object Inspection                  Examining code and stacks

-----------------------------      -----------------------------

DumpObj (do)                       Threads

DumpArray (da)                     ThreadState

DumpStackObjects (dso)             IP2MD

DumpHeap                           U

DumpVC                       DumpStack

GCRoot                             EEStack

ObjSize                            CLRStack

FinalizeQueue                      GCInfo

PrintException (pe)                EHInfo

TraverseHeap                       BPMD 
                                   COMState

 

Examining CLR data structures      Diagnostic Utilities

-----------------------------      -----------------------------

DumpDomain                         VerifyHeap EEHeap                             VerifyObj Name2EE                            FindRoots SyncBlk                            HeapStat DumpMT                             GCWhere DumpClass                          ListNearObj (lno) DumpMD                             GCHandles Token2EE                           GCHandleLeaks EEVersion                          FinalizeQueue (fq) DumpModule                         FindAppDomain ThreadPool                         SaveModule DumpAssembly                       ProcInfo 
DumpSigElem                        StopOnException (soe) DumpRuntimeTypes                   DumpLog DumpSig                            VMMap RCWCleanupList                     VMStat DumpIL                             MinidumpMode 
DumpRCW                            AnalyzeOOM (ao) DumpCCW

 

Examining the GC history           Other

-----------------------------      -----------------------------

HistInit                           FAQ HistRoot HistObj HistObjFind HistClear

7.简单示例,常见的线上两类问题

这里我们使用两个小示例直观的感受一下接触.NET运行时状态的感受,尽管真实的问题可能比这个复杂很多,但是解决问题的思路是一样的。

7.1.内存问题(内存偏高,内存溢出)

服务程序最怕的性能问题之一就是内存,当内存很高的情况下我们能够通过对dump文件进行查看,看哪些对象导致内存一直高。当内存一直高的情况下就会容易导致内存溢出异常,甚至是GC频繁的执行,当GC一执行就会导致服务并发下降,因为它要挂起所有的线程(这里指的是服务器模式的.NETCLR,相对应的还有工作站模式的.NETCLR)。

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
namespace  OrderManager
{
     class  Program
     {
         static  void  Main( string [] args)
         {
             Console.WriteLine( "app begin..." );
             Console.ReadLine();
 
             List< byte []> l =  new  List< byte []>();
 
             for  ( int  i = 0; i < 9999999; i++)
             {
                 byte [] b =  new  byte [1000];
 
                 l.Add(b);
 
                 Console.WriteLine(i);
             }
 
             Console.WriteLine( "end begin..." );
             Console.ReadLine();
         }
     }
}

这一段代码会一直分配内存直到最后内存溢出异常终止程序,我们在内存比较的情况下来获取一个dump文件,然后通过适当的命令来定位哪个对象占用内存过高。

在不知道对象类型的情况下比较简单的方式就是使用:0:000> !dumpheap -stat,命令,该命令的意思是统计当前堆的信息,在这里就可以一眼找到哪个对象占用多少内存。

0:000> !dumpheap -stat
Statistics:
      MT    Count    TotalSize Class 
Name
624ad6a8        1           12 System.Collections.Generic.GenericEqualityComparer`1[[System.String, 
mscorlib]]
624ac480        1           12 System.Int32

624aa58c        1           12 System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, 
mscorlib]]
624adec0        1           16 System.Security.Policy.AssemblyEvidenceFactory
624ace34        1           16 System.Text.DecoderReplacementFallback
624acde4        1           16 System.Text.EncoderReplacementFallback
6247a840        1           16 System.IO.TextReader+SyncTextReader
624ade0c        1           20 Microsoft.Win32.SafeHandles.SafePEFileHandle
6245fe58        1           20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle
6245fe08        1           20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle
6245fd74        1           20 System.Text.InternalEncoderBestFitFallback
6245f714        1           20 System.IO.Stream+NullStream
624ad3d4        1           24 System.Version
6245fdc4        1           24 System.Text.InternalDecoderBestFitFallback
6245fa8c        1           24 System.IO.TextWriter+SyncTextWriter
00163170        1           24 System.Collections.Generic.List`1[[System.Byte[], mscorlib]]
624ad4b4        1           28 System.Text.StringBuilder
624ab0b4        1           28 System.SharedStatics
6247c1b8        1           28 System.Text.DBCSCodePageEncoding+DBCSDecoder
6245f94c        1           28 Microsoft.Win32.Win32Native+InputRecord
6245f664        1           28 System.Text.EncoderNLS
624ade68        1           32 System.Security.Policy.PEFileEvidenceFactory
624acc10        1           32 System.Text.UnicodeEncoding
624ab938        1           36 System.Security.PermissionSet
624aced8        2           40 Microsoft.Win32.SafeHandles.SafeFileHandle
624ab7b0        1           40 System.Security.Policy.Evidence
624aaa64        1           44 System.Threading.ReaderWriterLock
6247cd1c        1           44 System.Text.InternalEncoderBestFitFallbackBuffer
624aab90        1           48 System.Collections.Hashtable+bucket[]
620c2348        1           48 System.Collections.Generic.Dictionary`2[[System.String, 
mscorlib],[System.Globalization.CultureData, mscorlib]]
620c2268        
1           48 System.Collections.Generic.Dictionary`2[[System.Type, 
mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, 
mscorlib]]
624acf98        1           52 System.Collections.Hashtable
624ab8d8        1           52 System.Threading.Thread
624acb20        2           56 System.Reflection.RuntimeAssembly
6245f994        2           56 System.IO.__ConsoleStream
624adaa8        1           60 System.IO.StreamWriter
624ad7b4        1           60 System.Collections.Generic.Dictionary`2+Entry[[System.String, 
mscorlib],[System.Globalization.CultureData, mscorlib]][]
6249fbec        
1           64 System.IO.StreamReader
624ab4e4        1           68 System.AppDomainSetup
6247c624        1           76 System.Text.DBCSCodePageEncoding
624ad474        1           84 System.Globalization.CalendarData
624ab060        7           84 System.Object
624aafe4        1           84 System.ExecutionEngineException
624aafa0        1           84 System.StackOverflowException
624aaf5c        1           84 System.OutOfMemoryException
624aae08        1           84 System.Exception
624ab130        1          112 System.AppDomain
624ad164        2          144 System.Globalization.CultureInfo
624ab028        2          168 System.Threading.ThreadAbortException
624ad82c        2          264 System.Globalization.NumberFormatInfo
624aa9f8        1          284 System.Collections.Generic.Dictionary`2+Entry[[System.Type, 
mscorlib],[System.Security.Policy.EvidenceTypeDescriptor, 
mscorlib]][]
624ac448        8          484 System.Int32[]
624ad3a0        2          616 System.Globalization.CultureData
624abe78       26          728 System.RuntimeType
624ab680        7         2910 System.Char[]
6245ab98       25        18064 System.Object[]
624aacc0     3283        85972 System.String
00363a78        7      2031754 Free
624696f8        2      2097184 System.Byte[][]
624acf54   301232    304844554 System.Byte[]

最后一个显然内存占用比较高,占了304844554 byte,如果你想在此情况下知道对象的内存地址你就直接使用!dumpheap ,不带任何参数。由于此命令会导致很多输出,我这里就写出输出内容了。通过!dumpheap 会得到内存很高的对象地址,02d55368,这个地址就是System.Byte[]对象,为了找到对象在哪里分配的,我们需要使用!gcroot 02d55368,命令,查看对象的根在哪里。

 0:000> !gcroot 02d55368
Thread 143310:    0028f364 
004f0100 OrderManager.Program.Main(System.String[]) 
[e:\NETDebug\DebugDemoProject\OrderManager\Program.cs @ 22]
        ebp+18: 0028f380
            ->  01b746c0 System.Collections.Generic.List`1[[System.Byte[], mscorlib]]
            ->  02d55368 System.Byte[][]

 知道了根就好办多了,直接看源代码就能发现问题。如果你还不死心的话可以使用!dumpobj 查看List对象。

 0:000> !dumpobj 01b746c0 
Name:        
System.Collections.Generic.List`1[[System.Byte[], mscorlib]]
MethodTable: 
00163170
EEClass:     6211c8b0
Size:        24(0x18) bytes
File:        
C:\Windows\Microsoft.Net\assembly\GAC_32\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name

6245ab98  4000c75        4      System.Object[]  0 instance 02d55368 _items

624ac480  4000c76        c         System.Int32  1 instance   301229 _size
624ac480  4000c77       10         System.Int32  1 instance   301229 _version
624ab060 4000c78 8 System.Object 0 instance 00000000 _syncRoot

6245ab98  4000c79        0      System.Object[]  0   shared   static _emptyArray

     >> Domain:Value dynamic statics NYI 00359520:NotInit  <<

这里需要注意的是,如果你是想执行!Clrstack -a 命令的话,当你使用调试器启动或者是附加进程的方式的化,要记住切换到适当的线程上才能看行。

7.2.线程问题(CPU过高,线程死锁)

CPU过高也是线上比较棘手的问题之一,查看CPU过高的步骤一般分为两步,查看线程的执行时间,然后切换到线程上下文,执行!ClrStack -a,看当前线程在哪里工作,到底做什么操作呢。

0:004> !runaway
User Mode Time
  Thread       Time
0:143310      0 days 0:00:01.934
4:142ac0      0 days 0:00:00.046
7:143874      0 days 0:00:00.000
6:143870      0 days 0:00:00.000
5:14386c      0 days 0:00:00.000
3:1432ec      0 days 0:00:00.000
2:143384      0 days 0:00:00.000
1:143254      0 days 0:00:00.000

测试线程ID为0的执行时间比较大,我们需要切换到线程0上去执行查看调用堆栈信息,~0s。

0:000> !ClrStack -a

0028f348 62b897f9 System.IO.TextWriter+SyncTextWriter.WriteLine(Int32)    

PARAMETERS:        

this (<CLR reg>) = 0x01b74258         value = <no data>

0028f358 62a66313 System.Console.WriteLine(Int32)    

PARAMETERS:        

value = <no data>

0028f364 004f0100 OrderManager.Program.Main(System.String[]) [e:\NETDebug\DebugDemoProject\OrderManager\Program.cs @ 22]     PARAMETERS:        

args (0x0028f38c) = 0x01b71fe4    

LOCALS:        

0x0028f380 = 0x01b746c0        

0x0028f388 = 0x000498ac        

0x0028f37c = 0x16a2e338        

0x0028f384 = 0x00000001

0028f51c 63162952 [GCFrame: 0028f51c]

我们会发现在Main方法中有一个本地变量0x0028f380 ,保存的值是0x01b746c0,它就是指向刚才分配很多内存的List<byte[]>对象。

线程死锁比较复杂,这里只给我认为比较简单的命令,通过此命令可以一眼看出哪个线程持有了哪个锁,目前在等待哪个锁。

0:000> !syncblk
Index         SyncBlock MonitorHeld Recursion Owning Thread Info          SyncBlock Owner
    4 0021fb20            3         1  00221f98 14974c   3   01ae2394 OrderManager.ImportOrder
  5 0021fb54           3          1 002234a8 149754    4   01ae23a0 OrderManager.ImportOrder
-----------------------------
Total           
5
CCW             0
RCW             0
ComClassFactory 
0
Free            0

这是两个锁,也就是两个对象同步块。进一步使用SOSEX.dll中的!dlk查看死锁的自动化检查信息。

0:000> !dlk

Examining SyncBlocks... Scanning for ReaderWriterLock instances... Scanning for holders of ReaderWriterLock locks... Scanning for ReaderWriterLockSlim instances... Scanning for holders of ReaderWriterLockSlim locks... Examining CriticalSections... Could not find symbol ntdll!RtlCriticalSectionList. Scanning for threads waiting on SyncBlocks... Scanning for threads waiting on ReaderWriterLock locks... Scanning for threads waiting on ReaderWriterLocksSlim locks... Scanning for threads waiting on CriticalSections... *DEADLOCK DETECTED* CLR thread 0x3 holds the lock on SyncBlock 0021fb20 OBJ:01ae2394[OrderManager.ImportOrder] ...and is waiting for the lock on SyncBlock 0021fb54 OBJ:01ae23a0[OrderManager.ImportOrder] CLR thread 0x4 holds the lock on SyncBlock 0021fb54 OBJ:01ae23a0[OrderManager.ImportOrder] ...and is waiting for the lock on SyncBlock 0021fb20 OBJ:01ae2394[OrderManager.ImportOrder] CLR Thread 0x3 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x17 Native) CLR Thread 0x4 is waiting at System.Threading.Monitor.Enter(System.Object, Boolean ByRef)(+0x17 Native)

1 deadlock detected.

注意我加粗的那段话,检测到死锁。

8.获取Dump文件时的重要注意事项

在获取dump文件方面我也要分享一下重要的注意事项。如果获取dump文件不正确的话是无法进行分析的,会出现任何奇怪的问题。

第一个就是使用64位机器上的任务管理获取32位进程dump文件,这通常是发生在服务器上,由于服务器IIS默认的启动进程方式是64位的,但是也有些情况下会变成32位的。

图19:

如果进程是以32位方式运行的,那么这个时候获取出来的dump文件是不好分析的,此时应该使用调试器工具进行dump的获取。获取出来的dump文件和分析机器上的调试器环境不一致的情况下会出现如下几个错误。

图20:

这个问题是未能加载正确版本的mscordacwks.dll .NETDAC调式组件。

图21:

这个问题是当前SOS.dll和.NET程序所使用的.NET版本不一致,这个问题的出现一般都是我们通过.load xx\xx\SOS.dll,手动方式加载的。

图22:

这个问题出现有好几种可能性,对常见的问题就是未能使用正确的方法或者工具获取dump文件,导致dum文件获取的机器和本地调试的机器整个环境不一致。

9.总结

本篇文章分享我对.NET应用程序调试方面学习和实践的一些经验,供广大博友参考。如果想系统的学习一下这方面的知识可以参考《.NET高级调试》一书,此书非常底层,对.NET运行时原理讲的很透彻,可以作为深入学习.NET的一门参考书。

ASP.NET4高级程序设计第4版 带目录PDF 分卷压缩包 part1
08-19
手动优化了PDF的书签,书签可折叠,书签链接以目录方式保存,多达1000多页,每页都做了书签定位,手都累酸啦。 ============================== 因权限只能到60MB,分卷压缩了,共3个压缩包,需下载完3个一起解压, ============================== ASP.NET 4高级程序设计(第4版)》【原版书为:Pro ASP.NET 4 in C# 2010】是ASP.NET领域的鸿篇巨制,全面讲解了ASP.NET4的各种特性及其背后的工作原理,并给出了许多针对如何构建复杂、可扩展的网站从实践中得出的建议。书中还深入讲述了其他ASP.NET图书遗漏的高级主题,如自定义控件的创建、图像处理、加密等。此外,《ASP.NET 4高级程序设计(第4版)》专门提供了两章的内容来教你如何用Ajax 技术制作快速响应的页面,以及如何使用微软的ASP.NETAJAX平台。另外,还专门介绍了ASP.NET4 新增的功能,如MVC 和动态数据等。   《ASP.NET 4高级程序设计(第4版)》适合各层次的ASP.NET程序员阅读。 =================== 第一部分 核心概念 第1章 ASP.NET简介 1.1 ASP.NET的7大要点 1.1.1 要点1:ASP.NET是同.NETFramework集成在一起的 1.1.2 要点2:ASP.NET是编译执行的,而不是解释执行的 1.1.3 要点3:ASP.NET支持多语言 1.1.4 要点4:ASP.NET运行在公共语言运行库内 1.1.5 要点5:ASP.NET是面向对象的 1.1.6 要点6:ASP.NET支持所有的浏览器 1.1.7 要点7:ASP.NET易于部署和配置 1.2 ASP.NET的演变 1.2.1 ASP.NET1.0和ASP.NET1.1 1.2.2  ASP.NET2.0 1.2.3 ASP.NET3.5 1.2.4 ASP.NET4 1.2.5 Silverlight 1.3 总结 第2章 Visual Studio 2.1 Visual Studio 2.1.1 网站和Web项目 2.1.2 创建无项目文件的网站 2.1.3 设计网页 2.2 Visual StudioIDE 2.2.1 解决方案资源管理器 2.2.2 文档窗口 2.2.3 工具箱 2.2.4 错误列表和任务列表 2.2.5 服务器资源管理器 2.3 代码编辑器 2.3.1 添加程序集引用 2.3.2 智能感知和大纲显示 2.3.3 Visual Studio 2010改进 2.4 编码模型 2.4.1 代码隐藏文件如何与页面连接 2.4.2 控件标签如何与页面变量连接 2.4.3 事件如何与事件处理程序连接 2.5 Web项目 2.5.1 基于项目的开发 2.5.2 创建Web项目 2.5.3 迁移旧版Visual Studio创建的网站 2.6 Visual Studio调试 2.6.1 单步调试 2.6.2 变量监视 2.6.3 高级断点 2.7 WebDevelopment Helper 2.8 总结 第3章 Web窗体 3.1 页面处理 3.1.1 HTML表单 3.1.2 动态用户界面 3.1.3 ASP.NET事件模型 3.1.4 自动回送 3.1.5 视图状态 3.1.6 XHTML兼容性 3.1.7 客户端控件ID 3.2 Web窗体处理阶段 3.2.1 页面框架初始化 3.2.2 用户代码初始化 3.2.3 验证 3.2.4 事件处理 3.2.5 自动数据绑定 3.2.6 清除 3.2.7 页面流示例 3.3 作为控件容器的页面 3.3.1 显示控件树 3.3.2 页头 3.3.3 动态控件的创建 3.4 Page类 3.4.1 Session、Application和Cache 3.4.2 Request 3.4.3 Response 3.4.4 Server 3.4.5 User 3.4.6 Trace 3.4.7 访问其他类中的HTTP上下文 3.5 总结 第4章 服务器控件 4.1 服务器控件的类型 4.2 HTML服务器控件 4.2.1 HtmlControl类 4.2.2 HtmlContainerControl类 4.2.3 HtmlInputControl类 4.2.4 HTML服务器控件类 4.2.5 设置Style特性和其他属性 4.2.6 通过编程创建服务器控件 4.2.7 处理服务器端事件 4.3 Web控件 4.3.1 WebControl基类 4.3.2 基本的Web控件类 4.3.3 单位 4.3.4 枚举 4.3.5 颜色 4.3.6 字体 4.3.7 焦点 4.3.8 默认按钮 4.3.9 可滚动面板 4.3.10 处理Web控件事件 4.4 List控件 4.4.1 Selectable列表控件 4.4.2 BulletedList控件 4.5 输入验证控件 4.5.1 验证控件 4.5.2 验证流程 4.5.3 BaseValidator类 4.5.4 RequiredFieldValidator控件 4.5.5 RangeValidator控件 4.5.6 CompareValidator控件 4.5.7 RegularExpressionValidator控件 4.5.8 CustomValidator控件 4.5.9 ValidationSummary控件 4.5.10 通过编程使用验证器 4.5.11 验证组 4.6 富控件 4.6.1 AdRotator控件 4.6.2 Calendar控件 4.7 总结 第5章 ASP.NET应用程序 5.1 剖析ASP.NET应用程序 5.1.1 应用程序域 5.1.2 应用程序生命周期 5.1.3 应用程序更新 5.1.4 应用程序目录结构 5.2 global.asax应用程序文件 5.2.1 应用程序事件 5.2.2 演示应用程序事件 5.3 ASP.NET配置 5.3.1 machine.config文件 5.3.2 web.config文件 5.3.3 设置 5.3.4 5.3.5 5.3.6 5.3.7 通过编程读写配置节 5.3.8 网站管理工具(WAT) 5.3.9 扩展配置文件结构 5.3.10 加密配置节 5.4 .NET组件 5.4.1 创建组件 5.4.2 通过App_Code目录使用组件 5.4.3 通过Bin目录使用组件 5.5 扩展HTTP管道 5.5.1 HTTP处理程序 5.5.2 创建自定义的HTTP处理程序 5.5.3 配置自定义的HTTP处理程序 5.5.4 使用无须配置的HTTP处理程序 5.5.5 创建高级的HTTP处理程序 5.5.6 为非HTML内容创建HTTP处理程序 5.5.7 HTTP模块 5.5.8 创建自定义HTTP模块 5.6 总结 第6章 状态管理 6.1 ASP.NET状态管理 6.2 视图状态 6.2.1 视图状态示例 6.2.2 在视图状态中保存对象 6.2.3 视图状态评估 6.2.4 有选择地禁用视图状态 6.2.5 视图状态安全 6.3 在页面间传送信息 6.3.1 查询字符串 6.3.2 跨页回发 6.4 cookie 6.5 会话状态 6.5.1 会话架构 6.5.2 使用会话状态 6.5.3 配置会话状态 6.5.4 会话状态安全 6.6 应用程序状态 6.7 总结 第二部分 数据访问 第7章 基础 7.1 ADO.NET架构 7.1.1 ADO.NET数据提供程序 7.1.2 ADO.NET的标准化 7.1.3 基本ADO.NET类 7.2 Connection类 7.2.1 连接字符串 7.2.2 测试连接 7.2.3 连接池 7.3 Command类和DataReader类 7.3.1 Command基础 7.3.2 DataReader类 7.3.3  ExecuteReader()方法和DataReader 7.3.4 ExecuteScalar()方法 7.3.5 ExecuteNonQuery()方法 7.3.6 SQL注入攻击 7.3.7 使用参数化命令 7.3.8 调用存储过程 7.4 事务 7.4.1 事务和ASP.NET应用程序 7.4.2 隔离级别 7.4.3 保存点 7.5 提供程序无关的代码 7.5.1 创建工厂 7.5.2 用工厂创建对象 7.5.3 使用提供程序无关的代码的查询 7.6 总结 第8章 数据组件和DataSet 8.1 构建数据访问组件 8.1.1 数据包 8.1.2 存储过程 8.1.3 数据应用类 8.1.4 测试数据库组件 8.2 非连接的数据 8.2.1 Web应用程序和DataSet 8.2.2 XML集成 8.3 DataSet类 8.4  DataAdapter类 8.4.1 填充DataSet 8.4.2 使用多个表和关系 8.4.3 查找特定行 8.4.4 在数据访问类里使用DataSet 8.4.5 数据绑定 8.5 DataView类 8.5.1 使用DataView排序 8.5.2 使用DataView过滤 8.5.3 使用关系的高级过滤 8.5.4 计算列 8.6 总结 第9章 数据绑定 9.1 基本的数据绑定 9.1.1 单值绑定 9.1.2 其他类型的表达式 9.1.3 重复值绑定 9.2 数据源控件 9.3 SqlDataSource 9.3.1 选择记录 9.3.2 参数化命令 9.3.3 错误处理 9.3.4 更新记录 9.3.5 删除记录 9.3.6 插入记录 9.3.7 SqlDataSource的不足 9.4 ObjectDataSource 9.4.1 选择记录 9.4.2 更新记录 9.4.3 使用数据对象执行更新 9.5 数据源控件的限制 9.5.1 问题 9.5.2 添加其他项 9.5.3 使用SqlDataSource处理其他选项 9.5.4 使用ObjectDataSource处理其他选项 9.6 总结 第10章 富数据控件 10.1 GridView 10.2 格式化GridView 10.2.1 格式化字段 10.2.2 样式 10.2.3 格式化特定值 10.3 GridView行选择 10.3.1 使用选择来创建主-从表单 10.3.2 SelectedIndexChanged事件 10.3.3 将数据字段用作选择按钮 10.4 对GridView排序 10.4.1 使用SqlDataSource排序 10.4.2 使用ObjectDataSource排序 10.4.3 排序和选择 10.4.4 高级排序 10.5 GridView分页 10.5.1 自动分页 10.5.2 分页和选择 10.5.3 ObjectDataSource的自定义分页 10.5.4 定制分页栏 10.6 GridView模板 10.6.1 使用多个模板 10.6.2 在Visual Studio中编辑模板 10.6.3 绑定到方法 10.6.4 处理模板中的事件 10.6.5 使用模板编辑 10.6.6 模板中的客户端ID 10.7 ListView 10.7.1 分组 10.7.2 分页 10.8 DetailsView和FormView 10.8.1 DetailsView 10.8.2 FormView 10.9 高级网格 10.9.1 GridView中的汇总 10.9.2 单个表中的父/子视图 10.9.3 使用查找表编辑字段 10.9.4 处理来自数据库的图片 10.9.5 探测并发冲突 10.10 总结 第11章 缓存和异步页面 11.1 理解ASP.NET缓存 11.2 输出缓存 11.2.1 声明性的输出缓存 11.2.2 缓存和查询字符串 11.2.3 使用特定查询字符串参数的缓存 11.2.4 自定义缓存控制 11.2.5 使用HttpCachePolicy类进行缓存 11.2.6 缓存后替换和部分页缓存 11.2.7 缓存用户配置 11.2.8 缓存配置 11.2.9 输出缓存扩展 11.3 数据缓存 11.3.1 向缓存添加项目 11.3.2 简单的缓存测试 11.3.3 缓存优先级 11.3.4 使用数据源控件的缓存 11.4 缓存依赖 11.4.1 文件和缓存项目依赖 11.4.2 聚合依赖 11.4.3 移除项目回调 11.4.4 理解SQL缓存通知 11.4.5 缓存通知的工作方式 11.4.6 启用通知 11.4.7 创建缓存依赖 11.5 自定义缓存依赖 11.5.1 基本的自定义缓存依赖 11.5.2 使用消息队列的自定义缓存依赖 11.6 异步页面 11.6.1 创建异步页面 11.6.2 在异步页面中查询数据 11.6.3 错误处理 11.6.4 在异步任务中使用缓存 11.6.5 多异步任务和超时 11.7 总结 第12章 文件和流 12.1 使用文件系统 12.1.1 Directory类和File类 12.1.2 DirectoryInfo类和FileInfo类 12.1.3 DriveInfo类 12.1.4 使用Attributes 12.1.5 使用通配符过滤文件 12.1.6 获取文件的版本信息 12.1.7 Path类 12.1.8 文件浏览器 12.2 使用流读写文件 12.2.1 文本文件 12.2.2 二进制文件 12.2.3 上传文件 12.2.4 使文件对多用户安全 12.2.5 压缩 12.3 序列化 12.4 总结 第13章 LINQ 13.1 LINQ基础 13.1.1 延迟执行 13.1.2 LINQ是如何工作的 13.1.3 LINQ表达式 13.1.4 揭秘LINQ表达式 13.2 LINQ to DataSet 13.2.1 强类型的DataSet 13.2.2 空值 13.3 LINQ to Entities 13.3.1 生成数据模型 13.3.2 数据模型类 13.3.3 实体关系 13.3.4 查询存储过程 13.3.5 LINQ to Entities查询揭秘 13.4 数据库操作 13.4.1 插入 13.4.2 更新 13.4.3 删除 13.4.4 管理并发 13.4.5 处理并发冲突 13.5 EntityDataSource控件 13.5.1 显示数据 13.5.2 获取关联数据 13.5.3 编辑数据 13.5.4 验证 13.6 使用QueryExtender控件 13.6.1 使用SearchExperssion 13.6.2 使用RangeExpression 13.6.3 使用PropertyExpression 13.6.4 使用MethodExpression 13.7 总结 第14章 XML 14.1 什么时候使用XML 14.2 XML简介 14.2.1 XML的优点 14.2.2 格式良好的XML 14.2.3 XML命名空间 14.2.4 XML架构 14.3 基于流的XML处理 14.3.1 写XML文件 14.3.2 读取XML文件 14.4 内存中的XML处理 14.4.1 XmlDocument 14.4.2 XPathNavigator 14.4.3 XDocument 14.5 搜索XML内容 14.5.1 搜索XmlDocument 14.5.2 使用XPath搜索XmlDocument 14.5.3 使用LINQ搜索XDocument 14.6 验证XML内容 14.6.1 基本架构 14.6.2 验证XmlDocument 14.6.3 使用XDocument进行验证 14.7 转换XML内容 14.7.1 基本的样式表 14.7.2 使用XslCompiledTransform 14.7.3 使用Xml控件 14.7.4 使用LINQ to XML转换XML 14.8 XML数据绑定 14.8.1 非层次化绑定 14.8.2 使用XPath 14.8.3 嵌套的网格 14.8.4 使用TreeView的层次化绑定 14.8.5 使用XSLT 14.8.6 绑定到来自其他源的XML内容 14.8.7 通过XmlDataSource更新XML 14.9 XML和ADO.NET数据集 14.9.1 把DataSet转换为XML 14.9.2 把DataSet作为XML访问 14.10 总结 第三部分 构建ASP.NET网站 第15章 用户控件 15.1 用户控件基础 15.1.1 创建简单的用户控件 15.1.2 把页面转换成用户控件 15.2 为用户控件添加代码 15.2.1 处理事件 15.2.2 添加属性 15.2.3 使用自定义对象 15.2.4 添加事件 15.2.5 公开内部Web控件 15.3 动态加载用户控件 15.4 局部页面缓存 15.4.1 VaryByControl 15.4.2 共享缓存控件 15.5 总结 第16章 主题和母版页 16.1 层叠样式表 16.1.1 创建样式表 16.1.2 应用样式表规则 16.2 主题 16.2.1 主题文件夹和外观 16.2.2 应用简单主题 16.2.3 处理主题冲突 16.2.4 为同一控件创建多个外观 16.2.5 含有模板和图片的外观 16.2.6 在主题里使用CSS 16.2.7 通过配置文件来应用主题 16.2.8 动态应用主题 16.3 标准化网站布局 16.4 母版页基础 16.4.1 简单的母版页 16.4.2 简单的内容页 16.4.3 默认内容 16.4.4 具有表格和CSS布局的母版页 16.4.5 母版页和相对路径 16.4.6 通过配置文件应用母版页 16.5 高级母版页 16.5.1 和母版页类交互 16.5.2 动态设置母版页 16.5.3 嵌套母版页 16.6 总结 第17章 网站导航 17.1 多视图页面 17.1.1 MultiView控件 17.1.2 Wizard控件 17.2 站点地图 17.2.1 定义站点地图 17.2.2 绑定站点地图 17.2.3 导航路径 17.2.4 显示站点地图的一部分 17.2.5 站点地图对象 17.2.6 加入自定义站点地图信息 17.2.7 创建自定义的SiteMapProvider 17.2.8 安全调整 17.3 URL映射和路由 17.3.1 URL映射 17.3.2 URL路由 17.4 TreeView控件 17.4.1 TreeNode 17.4.2 按需填充节点 17.4.3 TreeView样式 17.5 Menu控件 17.5.1 Menu样式 17.5.2 Menu模板 17.6 总结 第18章 网站部署 18.1 安装和配置IIS 18.1.1 安装IIS 7 18.1.2 管理IIS 7 18.2 部署网站 18.2.1 通过复制文件进行部署 18.2.2 使用Web部署 18.2.3 通过FTP部署 18.3 管理网站 18.3.1 创建新站点 18.3.2 创建虚拟目录 18.3.3 使用VirtualPathProvider 18.3.4 使用应用程序池 18.3.5 使用应用程序预热 18.4 扩展集成管道 18.4.1 创建处理程序 18.4.2 部署处理程序 18.4.3 配置处理程序 18.4.4 测试处理程序 18.5 总结 第四部分 安全 第19章 ASP.NET安全模型 19.1 为何创建安全软件 19.1.1 理解潜在的威胁 19.1.2 安全编程原则 19.1.3 理解Gatekeeper 19.2 理解安全级别 19.2.1 验证 19.2.2 授权 19.2.3 机密性和完整性 19.2.4 互相协作 19.3 理解SSL 19.3.1 理解证书 19.3.2 理解SSL 19.3.3 在IIS 7.x中配置SSL 19.4 总结 第20章 表单验证 20.1 表单验证介绍 20.1.1 为什么使用表单验证 20.1.2 为什么不使用表单验证 20.1.3 为什么不自己实现cookie验证 20.1.4 表单验证类 20.2 实现表单验证 20.2.1 配置表单验证 20.2.2 禁止匿名用户访问 20.2.3 创建自定义登录页面 20.2.4 自定义凭证存储 20.2.5 表单验证中的持久化cookie 20.3 IIS 7.x和表单验证 20.4 总结 第21章 成员资格 21.1 介绍ASP.NET的成员资格API 21.2 使用成员资格API 21.2.1 配置表单验证 21.2.2 创建数据存储 21.2.3 配置连接字符串和成员资格提供程序 21.2.4 创建并验证用户 21.3 使用安全控件 21.3.1 Login控件 21.3.2 LoginStatus控件 21.3.3 LoginView控件 21.3.4 PasswordRecovery控件 21.3.5 ChangePassword控件 21.3.6 CreateUserWizard控件 21.4 在IIS 7.x里配置成员资格 21.4.1 配置提供程序和用户 21.4.2 在其他应用程序里使用成员资格API 21.5 使用Membership类 21.5.1 从存储中获得用户 21.5.2 更新存储中的用户 21.5.3 创建和删除用户 21.5.4 检验用户 21.6 总结 第22章 Windows验证 22.1 介绍Windows验证 22.1.1 为什么使用Windows验证 22.1.2 为什么不使用Windows验证 22.1.3 Windows验证机制 22.2 实现Windows验证 22.2.1 配置IIS 7.x 22.2.2 配置ASP.NET 22.2.3 IIS 7.x管道 22.2.4 禁止匿名用户访问 22.2.5 访问Windows用户信息 22.3 身份模拟 22.3.1 Windows中的身份模拟和委托 22.3.2 经过配置的身份模拟 22.3.3 可编程的身份模拟 22.4 总结 第23章 授权和角色 23.1 URL 授权 23.2 文件授权 23.3 在代码中检查授权 23.3.1 使用IsInRole()方法 23.3.2 使用PrincipalPermission类 23.4 为基于角色的授权使用角色API 23.4.1 和角色一起使用LoginView控件 23.4.2 通过编程访问角色 23.4.3 在Windows验证中使用角色API 23.5 IIS 7.x里的授权和角色 23.5.1 在IIS 7.x里使用ASP.NET角色授权 23.5.2 在IIS 7.x中管理ASP.NET角色 23.6 总结 第24章 用户配置 24.1 理解用户配置 24.1.1 用户配置的性能 24.1.2 用户配置如何存储数据 24.1.3 用户配置和验证 24.1.4 用户配置与自定义的数据组件 24.2 使用SqlProfileProvider 24.2.1 创建用户配置表 24.2.2 配置提供程序 24.2.3 定义用户配置属性 24.2.4 使用用户配置属性 24.2.5 用户配置序列化 24.2.6 用户配置组 24.2.7 用户配置和自定义的数据类型 24.2.8 用户配置API 24.2.9 匿名用户配置 24.3 自定义的用户配置提供程序 24.3.1 自定义的用户配置提供程序类 24.3.2 设计FactoredProfileProvider 24.3.3 通过编码实现FactoredProfileProvider 24.3.4 测试FactoredProfileProvider 24.4 总结 第25章 加密 25.1 加密数据:机密性问题 25.2 .NET加密命名空间 25.3 理解.NET加密类 25.3.1 对称加密算法 25.3.2 非对称加密 25.3.3 抽象加密类 25.3.4 ICryptoTransform接口 25.3.5 CryptoStream类 25.4 加密敏感数据 25.4.1 管理秘密信息 25.4.2 使用对称算法 25.4.3 使用非对称算法 25.4.4 加密数据库中的敏感数据 25.5 加密查询字符串 25.5.1 包装查询字符串 25.5.2 创建一个测试页面 25.6 总结 第26章 自定义成员资格提供程序 26.1 自定义提供程序的架构 26.2 创建自定义提供程序的基本步骤 26.2.1 自定义提供程序的总体设计 26.2.2 设计并实现自定义存储 26.2.3 实现提供程序类 26.2.4 使用自定义提供程序类 26.3 总结 第五部分 高级用户界面 第27章 自定义服务器控件 27.1 自定义服务器控件入门 27.1.1 创建简单的自定义控件 27.1.2 使用自定义控件 27.1.3 工具箱中的自定义控件 27.1.4 创建支持样式属性的Web控件 27.1.5 呈现过程 27.2 处理不同的浏览器 27.2.1 HtmlTextWriter 27.2.2 浏览器检测 27.2.3 浏览器属性 27.2.4 覆盖浏览器类型侦测 27.2.5 自适应呈现 27.3 控件状态和事件 27.3.1 视图状态 27.3.2 控件状态 27.3.3 回传数据和change事件 27.3.4 触发回传 27.4 扩展现有的Web控件 27.4.1 组合控件 27.4.2 派生控件 27.5 总结 第28章 图形、GDI+和图表 28.1 ImageMap控件 28.1.1 创建热点 28.1.2 处理热点单击 28.1.3 自定义热点 28.2 使用GDI+画图 28.2.1 简单绘制 28.2.2 图像格式和质量 28.2.3 Graphics类 28.2.4 使用GraphicsPath 28.2.5 画笔 28.2.6 画刷 28.3 在网页上嵌入动态图形 28.3.1 使用PNG格式 28.3.2 传递信息给动态图像 28.3.3 使用GDI+的自定义控件 28.4 使用Chart控件 28.4.1 创建基本的图表 28.4.2 用数据填充图表 28.5 总结 第29章 JavaScript和Ajax技术 29.1 JavaScript本质 29.1.1 HTML DOM 29.1.2 客户端事件 29.1.3 脚本块 29.1.4 操纵HTML元素 29.1.5 调试JavaScript 29.2 基本的JavaScript示例 29.2.1 创建JavaScript页面处理器 29.2.2 使用JavaScript异步下载图片 29.2.3 呈现脚本块 29.3 脚本注入攻击 29.3.1 请求验证 29.3.2 禁用请求验证 29.3.3 扩展请求验证 29.4 带有JavaScript的自定义控件 29.4.1 弹出窗口 29.4.2 滚动按钮 29.5 框架 29.5.1 框架导航 29.5.2 内联框架 29.6 理解Ajax 29.6.1 XMLHttpRequest对象 29.6.2 Ajax示例 29.7 在客户端回调中使用Ajax 29.7.1 创建客户端回调 29.7.2 深入了解客户端回调 29.7.3 自定义控件里的客户端回调 29.8 总结 第30章 ASP.NETAJAX 30.1 介绍ASP.NETAJAX 30.1.1 客户端的ASP.NETAJAX:脚本库 30.1.2 服务器端的ASP.NETAJAX:ScriptManager 30.2 服务器回调 30.2.1 ASP.NETAJAX中的Web服务 30.2.2 在页面里放置Web方法 30.2.3 ASP.NETAJAX应用程序服务 30.3 ASP.NETAJAX服务器控件 30.3.1 使用UpdatePanel的局部呈现 30.3.2 使用Timer的定时刷新 30.3.3 使用UpdateProgress的耗时更新 30.3.4 管理浏览器历史 30.4 深入客户端库 30.4.1 理解客户端模型 30.4.2 JavaScript的面向对象编程 30.4.3 网页框架 30.5 控件扩展器 30.5.1 安装ASP.NETAJAX控件工具包 30.5.2 AutoCompleteExtender 30.5.3 ASP.NETAJAX控件工具包 30.6 总结 第31章 使用Web部件页面的门户网站 31.1 典型门户页面 31.2 基本Web部件页面 31.2.1 创建页面设计 31.2.2 WebPartManager和WebPartZone 31.2.3 向页面添加Web部件 31.2.4 定制页面 31.3 创建Web部件 31.3.1 简单的Web部件任务 31.3.2 开发高级Web部件 31.3.3 Web部件编辑器 31.3.4 连接Web部件 31.3.5 自定义动词和Web部件 31.3.6 用户控件和高级Web部件 31.3.7 动态上传Web部件 31.3.8 对Web部件的授权 31.3.9 个性化的最后任务 31.4 总结 第32章 MVC 32.1 MVC和Web表单的选择 32.2 创建基本的MVC应用程序 32.2.1 创建模型 32.2.2 创建控制器 32.2.3 创建Index视图 32.2.4 测试(未完成的)应用程序 32.2.5 完成控制器和视图 32.2.6 修改Site.Master文件 32.3 扩展基础的MVC应用程序 32.3.1 配置路由 32.3.2 增加错误处理 32.3.3 增加验证 32.3.4 增强数据存储访问 32.3.5 增加对外键约束的支持 32.4 自定义视图 32.4.1 修改视图 32.4.2 增加视图数据 32.5 强化模型 32.6 验证数据 32.6.1 执行基本的验证 32.6.2 增加验证标注 32.7 使用动作结果 32.7.1 返回JSON数据 32.7.2 调用其他控制器方法 32.8 总结 第33章 动态数据 33.1 创建动态数据应用程序 33.1.1 创建动态数据网站 33.1.2 检验动态数据网站 33.2 深入理解动态数据项目 33.3 自定义动态数据网站 33.3.1 使用模板进行自定义 33.3.2 使用路由进行自定义 33.3.3 使用元数据进行自定义 33.3.4 自定义验证 33.4 总结 第34章 Silverlight 34.1 理解Silverlight 34.1.1 Silverlight与Flash对比 34.1.2 Silverlight系统需求 34.2 创建Silverlight解决方案 34.2. 1 Silverlight编译 34.2.2 入口页面 34.3 创建Silverlight项目 34.3.1 设计Silverlight页面 34.3.2 理解XAML 34.3.3 设置属性 34.3.4 XAML 代码隐藏 34.3.5 处理事件 34.3.6 了解Silverlight类库 34.4 布局 34.4.1 Canvas 34.4.2 网格 34.5 动画 34.5.1 动画基础知识 34.5.2 定义动画 34.5.3 StoryBoard类 34.5.4 交互式动画实例 34.5.5 变换 34.6 Silverlight使用Web服务 34.6.1 创建Web服务 34.6.2 添加Web引用 34.6.3 调用Web服务 34.6.4 配置Web服务URL 34.6.5 跨域Web服务调用 34.7 总结
ASP.NET《数据库原理应用技术》课程指导平台的开发(源代码+论文).rar
12-29
资源内容:项目全套源码+完整文档 源码说明: 全部项目源码都是经过测试校正后百分百成功运行。 SpringBoot 毕业设计,SpringBoot 课程设计,基于SpringBoot+Vue开发的,含有代码注释,新手也可看懂。ssm整合开发,小程序毕业设计、期末大作业、课程设计、高分必看,下载下来,简单部署,就可以使用。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行! 1. 技术组成 前端:Vue/JSP/React/HTML+JS+CSS/ASP 后台框架代码:java/c/c++/php/VB/lun/Andorid/Python 开发环境:idea 数据库:MySql(建议用 5.7,8.0 有时候会有坑) 部署环境:Tomcat(建议用 7.x 或者 8.x b版本),maven 
如何让 WinDebug Preview 加载 Dotnet Core 的 SOS.dll 进行调试
Gefangenes的博客
06-09 538
我安装好Windebug 软件,尝试加载 VS2019 创建的一个控制台项目,说明一下,不是跨平台版本,但是就是没有办法获取我想要的东西,后来,仔细对比,才发现,没有加载 SOS.DLL,所以使用命令也不起作用。以前听过 WinDebug 调试器,但是没有使用过,由于最近想起来了,就好好的研究一下,学习一下。关于 WinDebug 最新版的安装方法,可以在“微软商店”里面,直接查询“WinDebug”,找到后,点击安装就好。但是学的越多,好像懂得越少,没办法,我们只能无限努力,不忘初心,重新启程。
WinDBG之SOSEX扩展
weixin_30567471的博客
09-25 326
这个是07年的文章了,比较旧,http://www.stevestechspot.com/SOSEXANewDebuggingExtensionForManagedCode.aspx 最新的SOSEX可以到作者的主页去下载:http://www.stevestechspot.com/, 下载的ZIP里面有一个文本的帮助文件,可惜没有格式啊。。。 我还是贴一个旧的帮助吧: ...
共有25款Windows 程序调试工具开源软件
Wanda && Aidem
03-31 1751
http://m.baidu.com/from=1012852q/bd_page_type=1/ssid=0/uid=0/pu=usm%400%2Csz%401320_1004%2Cta%40iphone_2_5.0_11_2.1/baiduid=6279330F7A62F3346EED1B1893DAEE87/w=0_10_%E7%A8%8B%E5%BA%8Fbug%E8%B0%83%E8%AF
Memory,CPU,IO Debug Tools
佛系程序员
03-27 1057
这个工具调试内存,CPU,IO 看运行情况.非常的方便. 特此记录一下 下载 编译 运行查看效果 1.http://nmon.sourceforge.net/pmwiki.php?n=Site.CompilingNmon 下载代码 一个.c 和一个Makefile 编译 make nmon_power_ubuntu1604 运行 root@ubuntu:/usr/src/linux-
软件测试的自动化调试工具,DT10自动化的软件测试及调试工具
weixin_36432451的博客
07-22 967
DT10是一款自动化的软件测试及调试工具,无需依赖任何OS和CPU,支持C/C++、Java、C#多种语言,用于动态测试和调试工具,可对运行时目标程序进行长时间跟踪,适用于各个开发阶段。DT10是动态测试工具中唯一能实现灰盒测试的工具。不只软件内部的测试,连同CPU周边的传感器 , 端口等硬件状态也都会和执行路径一起被验证,所以可以在同一个时间测试软件和硬件的整个运行过程。测试结果,会自动产生报...
程序员最喜欢的22款软件开发工具
Used the same
11-17 1万+
市面上有海量的软件开发工具,因此,选择最佳软件开发工具可能是一项挑战。本文是 22 款顶级软件开发工具的精选列表。 我们将软件开发工具分为以下几类: 推荐工具 IDE (集成开发环境) 框架 云工具 数据科学 版本控制 原型设计 DevOps 通知 UML(统一建模语言) 推荐工具 1) Linx: Linx 是一款低代码的 IDE 和服务器。IT 专业人员使用 Linx 可以快..
PID调试软件(C#、模拟、仿真)
热门推荐
lin381825673的专栏
09-07 2万+
突然想起要玩下PID,于是又想到强大的C#,好吧,搞个小软件玩下。 首先花点时间学习PID,用的都是网上资料,开始是先看http://www.amobbs.com/thread-5043342-1-1.html 后来发现这代码弄不懂,而且说明也不多(特别对于T),代码也比较复杂的说。 最好发现一个好文档:http://download.csdn.net/detail/lin381825673...
调试器扩展SOSEX
weixin_30906701的博客
01-30 1699
SOSEX是另一个调试器扩展,可用于非托管代码和托管代码的调试。 SOSEX的安装包是一个ZIP文件,只需把ZIP文件中压缩的文件释放到指定的位置即可完成安装。如果将这些文件放到调试器的安装目录下,可以避免在加载SOSEX时指定完整路径。如下: 0:000> .load sosex.dll 0:000> !sosex.help SOSEX - Copyrig...
超全的常用串口调试软件
ben392797097的专栏
11-25 1万+
常用嵌入式串口调试软件
[草稿]Windbg常用命令及分析套路
:: Dotnet Fantasy ::
12-23 582
前段时间陆陆续续在追查服务端程序卡死的问题,最终通过dump分析找到原因。 过程中整理了一些Windbg的常用命令和基本套路,先把草稿发来备忘。 常用 !threadpool               查看线程池CPU使用量 !threads                     查看所有托管线程情况 !clrstack                     某个线程托管代
windbg查找c#高内存原因
xiaoxianerqq的专栏
10-14 1946
1.WinDbg.exe WinDbg.exe是windows平台的debug工具, 但是c#的程序需要SOS.DLL和SOSEX.DLL来支持。 其中SOS.DLL,该扩展包是.NET平台的一部分,是于官方版本.     SOSEX.DLL是微软的一名叫“Steve Johnson”软件工程师开发,属于个人维护的,用来增强SOS.DLL功能的 注意WinDbg
CLR Debugging
a1120111341的博客
01-30 116
1. CLR Process Source code -> Compiler -> .NET Assembly -> CLR JIT Application domains (System, Shared, Default) Assemblies (Private, Shared) (logical constructs) Modules (physical...
软件调试详解
hongduilanjun的博客
05-31 2075
首发于奇安信攻防社区:https://forum.butian.net/share/1478在windows里面调试跟异常息息相关,如果想要对调试得心应手,异常处理的知识是必不可少的,本文主要介绍的是软件调试方面的有关知识,讲解调试程序和被调试程序之间如何建立联系调试器和被调试程序调试器与被调试程序之间建立起联系的两种方式CreateProcessDebugActiveProcess首先看一下调用的再调用通过调用号进入0环进入0环创建结构体 然后到里面看一下然后调用了创建结构返回句柄再回到,当前线程回0环创
node-v7.7.2-linux-x86.tar.xz
最新发布
05-08
Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。
.net 高级调试 pdf
06-20
常见的.NET应用程序调试工具有Visual Studio、WinDBG、CLRDBG等。而.NET高级调试需要掌握.NET程序执行流程、内存管理等知识,以及掌握调试工具的使用方法。 其中PDF文件是常见的文件格式之一,也有涉及到.NET高级...

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • jquery 动态为ul添加li 56006
  • MySQL: Starting MySQL….. ERROR! The server quit without updating PID file解决办法 52454
  • 第四部分 方法3--------------- 40924
  • div,li,span中加入span右对齐方法 39943
  • mysql服务性能优化—my.cnf配置说明详解(16G内存) 37170

分类专栏

  • 架构 11篇
  • spring cloud 14篇
  • IOS 1篇
  • beetl
  • asp 2篇
  • asp.net 78篇
  • atlas&webpart 1篇
  • Azure
  • c# 222篇
  • C++ 53篇
  • com 2篇
  • com+ 1篇
  • com,com+,dcom
  • CSS 80篇
  • dcom 2篇
  • javascript 112篇
  • MFC 1篇
  • NET架构 18篇
  • SEO 6篇
  • sharepoint
  • torque
  • web service 3篇
  • win32 3篇
  • windows phone7
  • WPF 2篇
  • 个人随笔 24篇
  • 吉他 1篇
  • 密码学 10篇
  • 数据压缩技术
  • 数据库 35篇
  • 游戏开发 6篇
  • 算法 8篇
  • network program 10篇
  • 自定义组件开发
  • 英文学习 1篇
  • 设计模式 2篇
  • 阅读流水帐 41篇
  • directx 1篇
  • JQUERY 43篇
  • CQUERY
  • PINVOKE & Interoperability 3篇
  • Visual studio Extensibility
  • VSTO
  • WCF
  • SQL SERVER 6篇
  • DirectShow
  • 数字图像技术
  • PostSharp 1篇
  • Durandual
  • knockoutjs
  • requirejs
  • chartIQ
  • HighChart
  • StructMap
  • AutoMap
  • DDD领域驱动设计 3篇
  • ASP.NET Web API 2
  • MYSQL 27篇
  • PLSQL(oracle) 5篇
  • MSSQLSERVER 2篇
  • Linux(CentOS) 61篇
  • C# Generic
  • AOP 16篇
  • Design Pattern 4篇
  • Load Runner 1篇
  • HTML5 54篇
  • log4jnet 3篇
  • SignalR
  • Lucene
  • Mongo
  • Linux(Ubuntu) 4篇
  • Redis 5篇
  • MVC 12篇
  • prototype 2篇
  • 架构设计 7篇
  • nginx
  • node.js
  • zepto
  • Underscore.js 1篇
  • sass 1篇
  • backbone.js
  • IIS性能相关 1篇
  • Spring.NET 8篇
  • .NET CORE
  • ELASTICSEARCH 1篇
  • Log4NET 6篇
  • JenKins 5篇
  • MSBUILD 4篇
  • Razor 1篇
  • 分布式session 2篇
  • Shiro 1篇
  • JAVA 10篇
  • autofac
  • SSO 10篇
  • avalonjs
  • MUI
  • SPRING.NET
  • 葡萄城ActiveReport
  • K2
  • Activiti工作流

最新评论

  • securitystring与string的互相转换

    Oops635: 巧克力

  • form表单下的button按钮会自动提交表单的问题

    Latrion: 多谢兄弟

  • 数据权限简介

    wangchen24: 数据权限这几个权限中最复杂的,你这写死了不利于扩展,数据权限根据不同的维度,有不同的分法

  • C# foreach 中获取索引index的方法

    wenshinlee: 确实这样

  • 数据权限简介

    nigx2013: 大佬,数据权限的配置,有没有具体的实现步骤

您愿意向朋友推荐“博客详情页”吗?

  • 强烈不推荐
  • 不推荐
  • 一般般
  • 推荐
  • 强烈推荐
提交

最新文章

  • 权限体系设计:融合了组织和岗位的权限模型长啥样?
  • 与接口相关的设计模式(1):定制服务模式和适配器模式详解
  • B端设计实战:基于角色&属性的权限设计
2022年3篇
2021年21篇
2020年6篇
2018年28篇
2017年38篇
2016年184篇
2015年258篇
2014年22篇
2013年4篇
2012年12篇
2011年21篇
2010年6篇
2009年28篇
2008年20篇
2007年85篇
2006年109篇
2005年1篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值

深圳SEO优化公司吉祥网站优化推广价格金华网站优化软件哪家好运城网站改版深圳网站推广工具哪家好鄂州seo网站优化推荐太原关键词按天计费霍邱营销型网站建设价格西宁网站设计模板推荐忻州SEO按天收费哪家好衡阳网站优化排名价格重庆网站排名优化多少钱扬州百姓网标王推广多少钱吉林外贸网站设计哪家好安庆企业网站制作价格平湖网站优化软件甘孜百度竞价莱芜网站推广方案报价横岗高端网站设计哪家好南澳营销型网站建设报价肇庆网站优化排名多少钱温州seo网站推广价格西宁SEO按天计费甘孜百度爱采购哪家好宜宾关键词按天收费公司濮阳推广网站报价果洛网站制作设计价格海南百姓网标王报价普洱模板网站建设价格伊犁建站公司汉中网站建设哪家好歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化