<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_happyhippy</title><subtitle type="text">这个世界的问题在于聪明人充满疑惑，而傻子们坚信不疑。--罗素</subtitle><id>http://feed.cnblogs.com/blog/u/23645/rss</id><updated>2012-03-15T05:20:01Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/23645/rss"/><entry><id>http://www.cnblogs.com/happyhippy/archive/2012/03/15/2397901.html</id><title type="text">Asp.net 4.0，首次请求目录下的文件时响应很慢</title><summary type="text">1. 问题起因2. 尝试过的处理思路3. 解决方法 1. 问题起因 一个从VS2003(.Net Framework 1.1)升级到.net framework 4.0的项目，每次编译或者部署到服务器上后，首次请求任何一个目录下的默认页面时，都要耗时3~5秒；而以前使用.net framework 1.1的时候，没有这个问题。 我在页面上开启Trace="true"来跟踪，发现页面的处理时间并...</summary><published>2012-03-15T05:20:00Z</published><updated>2012-03-15T05:20:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2012/03/15/2397901.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2012/03/15/2397901.html"/><content type="html">&lt;p&gt;1. 问题起因&lt;br&gt;2. 尝试过的处理思路&lt;br&gt;3. 解决方法&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. 问题起因&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一个从VS2003(.Net Framework 1.1)升级到.net framework 4.0的项目，每次编译或者部署到服务器上后，首次请求任何一个目录下的默认页面时，都要耗时3~5秒；而以前使用.net framework 1.1的时候，没有这个问题。&lt;/p&gt; &lt;p&gt;我在页面上开启Trace="true"来跟踪，发现页面的处理时间并不久(IIS重启，首次打开页面时截获的信息)：&lt;img alt="" src="http://pic002.cnblogs.com/images/2012/20086/2012031111041795.png"&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp; 但是IIS日志中，显示&lt;strong&gt;首次请求页面的耗时接近4.635秒&lt;/strong&gt;。后续连续刷新页面，处理时间比较正常。。 &lt;p&gt;&lt;img alt="" src="http://pic002.cnblogs.com/images/2012/20086/2012031111071325.png"&gt; &lt;p&gt;虽然耗时比较久，但页面能够打开；然后我重启IIS，再次打开站点，用VS附加W3WP.exe来进行调试，再次打开这个页面的时候，发现抛出异常了：发生了 System.ArgumentException， Message=已存在具有相同键的条目。&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; 发生了 System.ArgumentException&lt;span style="color: #606060"&gt;   2:&lt;/span&gt;   Message=已存在具有相同键的条目。&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;   Source=System&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;   StackTrace:&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;        在 System.Collections.Specialized.ListDictionary.Add(Object key, Object &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;)&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;   InnerException: &lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     System.dll!System.Collections.Specialized.ListDictionary.Add(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; key, &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;) + 0x134 字节    &lt;span style="color: #606060"&gt;   8:&lt;/span&gt;      System.Web.dll!System.Web.UI.ParsedAttributeCollection.AddFilteredAttribute(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; filter, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; name, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;) + 0xba 字节    &lt;span style="color: #606060"&gt;   9:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ProcessAttributes(System.Text.RegularExpressions.Match match, &lt;span style="color: #0000ff"&gt;out&lt;/span&gt; System.Web.UI.ParsedAttributeCollection attribs = {System.Web.UI.ParsedAttributeCollection}, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; fDirective = &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;out&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; duplicateAttribute = &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;) + 0x221 字节    &lt;span style="color: #606060"&gt;  10:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ProcessBeginTag(System.Text.RegularExpressions.Match match = {System.Text.RegularExpressions.Match}, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; inputText) + 0x68 字节    &lt;span style="color: #606060"&gt;  11:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ParseStringInternal(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; text, System.Text.Encoding fileEncoding) + 0x3d0 字节    &lt;span style="color: #606060"&gt;  12:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ParseString(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; text, System.Web.VirtualPath virtualPath, System.Text.Encoding fileEncoding) + 0x6f 字节    &lt;span style="color: #606060"&gt;  13:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ParseFile(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; physicalPath, System.Web.VirtualPath virtualPath) + 0x115 字节    &lt;span style="color: #606060"&gt;  14:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.ParseInternal() + 0x57 字节    &lt;span style="color: #606060"&gt;  15:&lt;/span&gt;      System.Web.dll!System.Web.UI.TemplateParser.Parse() + 0x64 字节    &lt;span style="color: #606060"&gt;  16:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BaseTemplateBuildProvider.CodeCompilerType.get() + 0x6f 字节    &lt;span style="color: #606060"&gt;  17:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildProvider.GetCompilerTypeFromBuildProvider(System.Web.Compilation.BuildProvider buildProvider) + 0x42 字节    &lt;span style="color: #606060"&gt;  18:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.WebDirectoryBatchCompiler.CompileNonDependentBuildProviders(System.Collections.ICollection buildProviders) + 0xca 字节    &lt;span style="color: #606060"&gt;  19:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.WebDirectoryBatchCompiler.Process() + 0x5d 字节    &lt;span style="color: #606060"&gt;  20:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.BatchCompileWebDirectoryInternal(System.Web.Hosting.VirtualDirectory vdir, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; ignoreErrors) + 0x48 字节    &lt;span style="color: #606060"&gt;  21:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.BatchCompileWebDirectory(System.Web.Hosting.VirtualDirectory vdir, System.Web.VirtualPath virtualDir, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; ignoreErrors) + 0xbc 字节    &lt;span style="color: #606060"&gt;  22:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.CompileWebFile(System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}) + 0x5d 字节    &lt;span style="color: #606060"&gt;  23:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; noBuild, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowCrossApp, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowBuildInPrecompile, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; throwIfNotFound, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; ensureIsUpToDate) + 0x141 字节    &lt;span style="color: #606060"&gt;  24:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(System.Web.HttpContext context, System.Web.VirtualPath virtualPath, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; noBuild, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowCrossApp, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowBuildInPrecompile, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; throwIfNotFound, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; ensureIsUpToDate) + 0x70 字节    &lt;span style="color: #606060"&gt;  25:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(System.Web.VirtualPath virtualPath, System.Web.HttpContext context, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowCrossApp, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; throwIfNotFound) + 0x7e 字节    &lt;span style="color: #606060"&gt;  26:&lt;/span&gt;      System.Web.dll!System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(System.Web.VirtualPath virtualPath, System.Type requiredBaseType = {Name = &lt;span style="color: #006080"&gt;"Page"&lt;/span&gt; FullName = &lt;span style="color: #006080"&gt;"System.Web.UI.Page"&lt;/span&gt;}, System.Web.HttpContext context = {System.Web.HttpContext}, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; allowCrossApp) + 0x35 字节    &lt;span style="color: #606060"&gt;  27:&lt;/span&gt;      System.Web.dll!System.Web.UI.PageHandlerFactory.GetHandlerHelper(System.Web.HttpContext context, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; requestType, System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; physicalPath) + 0x20 字节    &lt;span style="color: #606060"&gt;  28:&lt;/span&gt;      System.Web.dll!System.Web.UI.PageHandlerFactory.System.Web.IHttpHandlerFactory2.GetHandler(System.Web.HttpContext context, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; requestType, System.Web.VirtualPath virtualPath, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; physicalPath) + 0x29 字节    &lt;span style="color: #606060"&gt;  29:&lt;/span&gt;      System.Web.dll!System.Web.HttpApplication.MapHttpHandler(System.Web.HttpContext context, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; requestType, System.Web.VirtualPath path, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; pathTranslated = &lt;span style="color: #006080"&gt;"。。。。。。。。。。\\order\\default.aspx"&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; useAppConfig) + 0xa8 字节    &lt;span style="color: #606060"&gt;  30:&lt;/span&gt;      System.Web.dll!System.Web.HttpApplication.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() + 0x81 字节    &lt;span style="color: #606060"&gt;  31:&lt;/span&gt;      System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step = {System.Web.HttpApplication.MapHandlerExecutionStep}, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; completedSynchronously = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;) + 0xb9 字节    &lt;span style="color: #606060"&gt;  32:&lt;/span&gt;      System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps(System.Exception error) + 0x13e 字节    &lt;span style="color: #606060"&gt;  33:&lt;/span&gt;      System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext context, System.AsyncCallback cb, &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; extraData) + 0xf8 字节    &lt;span style="color: #606060"&gt;  34:&lt;/span&gt;      System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest wr = {System.Web.Hosting.ISAPIWorkerRequestInProcForIIS7}) + 0x1a2 字节    &lt;span style="color: #606060"&gt;  35:&lt;/span&gt;      System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest wr) + 0x7d 字节    &lt;span style="color: #606060"&gt;  36:&lt;/span&gt;      System.Web.dll!System.Web.Hosting.ISAPIRuntime.ProcessRequest(System.IntPtr ecb, &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; iWRType) + 0xfd 字节    &lt;span style="color: #606060"&gt;  37:&lt;/span&gt;      [应用程序域转换]    &lt;span style="color: #606060"&gt;  38:&lt;/span&gt;      [本机到托管的转换]   &lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2.&amp;nbsp; 尝试过的处理思路&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我Attach到w3wp上去后，首次访问目录下的默认页面，catch到ArgumentException异常，然后根据异常信息，追踪到 TemplateParser.ParseFile -&amp;gt; ParseString -&amp;gt; ParseStringInternal -&amp;gt; ProcessBeginTag；然后我就尝试了下列努力：&lt;p&gt;1. 该目录下默认文件的&amp;lt;%@Page ....%&amp;gt;定义，属性与其他文件并没有任何不同(除了CodeBehind和Inherits的值)，我把页面里面的内容一点儿一点儿删掉，最后是剩下空内容(只剩下head、body、form几个元素)、空后台代码，问题还依旧存在。&lt;p&gt;2. 我在这个目录下面，新建了一个页面(VS2010自动生成的，没有添加任何元素或标签)，然后把这个新文件作为该目录默认页，发现问题还是存在。&lt;p&gt;3. 在第2步的基础上，打开新默认页后，我在URL中直接输入旧的默认页，页面瞬间刷出来了。然后我就纠结了，这延时，与页面内容毛的关系都木有，这异常信息也太坑爹了。&lt;p&gt;4. 出于尝试一下，我试下该目录下的其他文件，首次访问会不会变慢。于是重启IIS，然后发现首次访问该目录中的第一个文件(不论首次访问的是哪个文件)，都会耗时数秒；如果Attach到w3wp，都会Catch到这个异常。&lt;p&gt;5. 项目是从.net framework 1.1(VS2003)上直接升级过来的，以前03的版本，木有这个问题。然后google“asp.net 4.0 slow”，找到下面这个：&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://stackoverflow.com/questions/2382741/slow-performance-asp-net-aspnet-wp-exe-and-csc-exe-running-after-clicking-re"&gt;Slow Performance — ASP .NET ASPNET_WP.EXE and CSC.EXE Running After Clicking Redirect Link&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt; 6. The very first time that the page is load, then the asp.net compile a lot of pages, almost every one found on the same dir, including modules, and dlls found on bin.也就是说，在首次请求一个目录下的某个文件时，会把该目录的所有文件都编译一下。这样做的好处是，把一个目录的编译操作都集中在首次访问上，后续请求会比较快；但这样带来的问题是，如果编译时间比较久，那第一个发起请求的人就杯具了。于是在配置文件中，&lt;code&gt;设置batch="false"&lt;/code&gt;，然后问题消失，首次请求的速度提上来了，attach上去也没有异常了。&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;strong&gt;3.&amp;nbsp; 解决方法&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 部署的时候，记得在配置文件中，把debug和batch关闭，把optimizeCompilations打开：&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;compilation targetFramework="4.0" debug="false" batch="false" optimizeCompilations="true"&amp;gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:6d8201cc-d532-4b0b-a532-922e6bce7ce5" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/asp.net" rel="tag"&gt;asp.net&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Performance" rel="tag"&gt;Performance&lt;/a&gt;,&lt;a href="http://technorati.com/tags/compilation" rel="tag"&gt;compilation&lt;/a&gt;,&lt;a href="http://technorati.com/tags/batch" rel="tag"&gt;batch&lt;/a&gt;,&lt;a href="http://technorati.com/tags/optimizeCompilations" rel="tag"&gt;optimizeCompilations&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2397901.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2012/03/15/2397901.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2012/03/02/2376771.html</id><title type="text">PageCollectionView[Bug]，使用Filter的时候，删除集合中的元素，会导致ArgumentOutOfRangeException</title><summary type="text">PageCollectionView，没有使用Filter的时候一切正常；当使用Filter的时候，删除集合中的元素，会抛出如下异常： 1: 指定的参数已超出有效值的范围。\n参数名: index 2: 位于 System.Windows.Data.PagedCollectionView.GetItemAt(Int32 index)\n 3: 位于 System.Wi...</summary><published>2012-03-02T03:16:00Z</published><updated>2012-03-02T03:16:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2012/03/02/2376771.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2012/03/02/2376771.html"/><content type="html">&lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;PageCollectionView，没有使用Filter的时候一切正常；当使用Filter的时候，删除集合中的元素，会抛出如下异常：&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; 指定的参数已超出有效值的范围。\n参数名: index   &lt;span style="color: #606060"&gt;   2:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.GetItemAt(Int32 index)\n   &lt;span style="color: #606060"&gt;   3:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.get_IsCurrentInSync()\n   &lt;span style="color: #606060"&gt;   4:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.AdjustCurrencyForRemove(Int32 index)\n   &lt;span style="color: #606060"&gt;   5:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.ProcessRemoveEvent(Object removedItem, Boolean isReplace)\n   &lt;span style="color: #606060"&gt;   6:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)\n   &lt;span style="color: #606060"&gt;   7:&lt;/span&gt; 位于 System.Windows.Data.PagedCollectionView.&amp;lt;.ctor&amp;gt;b__0(Object sender, NotifyCollectionChangedEventArgs args)\n   &lt;span style="color: #606060"&gt;   8:&lt;/span&gt; 位于 System.Collections.Specialized.NotifyCollectionChangedEventHandler.Invoke(Object sender, NotifyCollectionChangedEventArgs e)\n   &lt;span style="color: #606060"&gt;   9:&lt;/span&gt; 位于 System.Collections.ObjectModel.ObservableCollection`1.OnCollectionChanged(NotifyCollectionChangedEventArgs e)\n   位于 System.Collections.ObjectModel.ObservableCollection`1.RemoveItem(Int32 index)\n   &lt;span style="color: #606060"&gt;  10:&lt;/span&gt; 位于 System.Collections.ObjectModel.Collection`1.RemoveAt(Int32 index)\n  &lt;span style="color: #606060"&gt;  11:&lt;/span&gt; 位于 System.Collections.ObjectModel.Collection`1.Remove(T item)\n&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;搜了一圈，找到这篇：&lt;a href="https://connect.microsoft.com/VisualStudio/feedback/details/595364/argumentoutofrangeexception-in-pagecollectionview" target="_blank"&gt;ArgumentOutOfRangeException in PageCollectionView&lt;/a&gt;&lt;/p&gt;&lt;p&gt;发现是PagedCollectionView的一个bug；更新到最新的SilverLight5，Bug依旧存在。没办法，只好从&lt;a href="https://silverlight.svn.codeplex.com/svn/Release/Silverlight4/Source/System.Windows.Data/PagedCollectionView/" target="_blank"&gt;这里&lt;/a&gt;把PagedCollectionView的源代码抓下来，调整并重新编译：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; needToRemove = (&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PageSize == 0 &amp;amp;&amp;amp; removeIndex &amp;gt;= 0) || (&lt;font color="#ff0000" size="4"&gt;&lt;strong&gt;internalRemoveIndex &amp;gt;= 0&lt;/strong&gt;&lt;/font&gt; &amp;amp;&amp;amp; internalRemoveIndex &amp;lt; (&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PageIndex + 1) * &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PageSize); &lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;记录一下。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2376771.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2012/03/02/2376771.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/07/17/2108976.html</id><title type="text">C#使用TCP/IP与ModBus进行通讯</title><summary type="text">1. ModBus的 Client/Server模型2. 数据包格式及MBAP header (MODBUS Application Protocol header)3. 大小端转换4. 事务标识和缓...</summary><published>2011-07-17T15:28:00Z</published><updated>2011-07-17T15:28:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/07/17/2108976.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/07/17/2108976.html"/><content type="html">&lt;p&gt;1. ModBus的 Client/Server模型&lt;br&gt;2. 数据包格式及MBAP header (MODBUS Application Protocol header)&lt;br&gt;3. 大小端转换&lt;br&gt;4. 事务标识和缓冲清理&lt;br&gt;5. 示例代码&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;0. MODBUS MESSAGING ON TCP/IP IMPLEMENTATION GUIDE&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 下载地址：&lt;a href="http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf"&gt;http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. ModBus的 Client/Server模型&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327421591.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327428145.png" width="703" height="186"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Client与Server之间有两种通讯方式：一种是TCP/IP，另一种是通过串口(Serial Port)，本文重点介绍第一种通讯方式。第二种方式留了接口，暂时还没有实现。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2. 数据包格式及MBAP header (MODBUS Application Protocol header) &lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.1 数据包格式&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327432539.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/20110717232744521.png" width="573" height="169"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 数据交换过程中，数据包的格式由三部分组成：协议头 + 功能码 + 数据(请求或接受的数据)。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这里主要用到下列两个功能码（十进制）：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3: 读取寄存器中的值（Read Multiple Register）&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 16: 往寄存器中写值（Write Multiple Register）&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp; 2.2 MBAP header&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/20110717232746290.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327483995.png" width="660" height="358"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 协议头具体包括下列4个字段：&lt;br&gt;(1) Transaction Identifier：事务ID标识，Client每发送一个Request数据包的时候，需要带上该标识；当Server响应该请求的时候，会把该标识复制到Response中；这样客户端就可以进行容错判断，防止数据包发串了。&lt;br&gt;(2) Protocal Identifier：协议标识，ModBus协议中，该值为0；&lt;br&gt;(3) Length：整个数据包中，从当个前这个字节之后开始计算，后续数据量的大小(按byte计算)。&lt;br&gt;(4) Unit Identifier：-_-&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3. 大小端转换&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ModBus使用Big-Endian表示地址和数据项。因此在发送或者接受数据的过程中，需要对数据进行转换。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;3.1 判断大小端&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327493307.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Endian" border="0" alt="Endian" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327514778.jpg" width="667" height="312"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 对于整数1，在两种机器上有两种不同的标示方式，如上图所示；因此，我们可以用&amp;amp;操作符来取其地址，再转换成指向byte的指针(byte*)，最后再取该指针的值；若得到的byte值为1，则为Little-Endian，否则为Big-Endian。&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;unsafe&lt;/span&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; tester = 1;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; littleEndian = (*(&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;*)(&amp;amp;tester)) == (&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)1;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3.2 整数/浮点数转换成Byte数组&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .Net提供了现成的API，可以BitConverter.GetBytes(value)和BitConverter.ToXXOO(Byte[] data)来进行转换。下面的代码对该转换进行了封装，加入了Little-Endian转Big-Endian的处理（以int为例）：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; ValueHelper &lt;font color="#ff0000"&gt;//Big-Endian可以直接转换&lt;/font&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;virtual&lt;/span&gt; Byte[] GetBytes(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;)&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; BitConverter.GetBytes(&lt;span style="color: #0000ff"&gt;value&lt;/span&gt;);&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;virtual&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; GetInt(&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] data)&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; BitConverter.ToInt32(data, 0);&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  11:&lt;/span&gt; }&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  13:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;internal&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; LittleEndianValueHelper : ValueHelper &lt;font color="#ff0000"&gt;//Little-Endian，转换时需要做翻转处理。&lt;/font&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt; {&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; Byte[] GetBytes(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;)&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Reverse(BitConverter.GetBytes(&lt;span style="color: #0000ff"&gt;value&lt;/span&gt;));&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;virtual&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; GetInt(&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] data)&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; BitConverter.ToInt32(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Reverse(data), 0);&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; Byte[] Reverse(Byte[] data)&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;             Array.Reverse(data);&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; data;&lt;span style="color: #606060"&gt;  27:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  28:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. 事务标识和缓冲处理&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4.1 Transaction Identifier&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上面2.2节中提到，Client每发送一个Request数据包的时候，需要带上一个标识；当Server响应该请求的时候，会把该标识复制到Response中，返回给Client。这样Client就可以用来判断数据包有没有发串。在程序中，可以可以用一个变量及记录该标识：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt; dataIndex = 0;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;protected&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt; CurrentDataIndex&lt;span style="color: #606060"&gt;   4:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;        get { &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.dataIndex; }&lt;span style="color: #606060"&gt;   6:&lt;/span&gt; }&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   8:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;protected&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt; NextDataIndex()&lt;span style="color: #606060"&gt;   9:&lt;/span&gt; {&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;        &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; ++&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.dataIndex;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 每次Client发送数据的时候，调用NextDataIndex()来取得事务标识；接着当Client读取Server的返回值的时候，需要判断数据包中的数据标识是否与发送时的标志一致；如果一致，则认为数据包有效；否则丢掉无效的数据包。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4.2 缓冲处理&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上节中提到，如果Client接收到的响应数据包中的标识，与发送给Server的数据标识不一致，则认为Server返回的数据包无效，并丢弃该数据包。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果只考虑正常情况，即数据木有差错，Client每次发送请求后，其请求包里面包含需要读取的寄存器数量，能算出从Server返回的数据两大小，这样就能确定读完Server返回的所有缓冲区中的数据；每次交互后，Socket缓冲区中都为空，则整个过程没有问题。但是问题是：如果Server端出错，或者数据串包等异常情况下，Client不能确定Server返回的数据包(占用的缓冲区)有多大；如果缓冲区中的数据没有读完，下次再从缓冲区中接着读的时候，数据包必然是不正确的，而且会错误会一直延续到后续的读取操作中。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 因此，每次读取数据时，要么全部读完缓冲区中的数据，要么读到错误的时候，就必须清楚缓冲区中剩余的数据。网上搜了半天，木有找到Windows下如何清理Socket缓冲区的。有篇文章倒是提到一个狠招，每次读完数据后，直接把Socket给咔嚓掉；然后下次需要读取或发送数据的时候，再重新建立Socket连接。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 回过头再来看，其实，在Client与Server进行交互的过程中，Server每次返回的数据量都不大，也就一个MBAP Header + 几十个寄存器的值。因此，另一个处理方式，就是每次读取尽可能多的数据(多过缓冲区中的数据量)，多读的内容，再忽略掉。暂时这么处理，期待有更好的解决方法。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. 源代码&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5.1 类图结构：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327539497.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="ClassDiagram1" border="0" alt="ClassDiagram1" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172327575546.jpg" width="855" height="679"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5.2 使用示例&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;(1) 写入数据：&lt;/strong&gt;&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Wrapper.Send(Encoding.ASCII.GetBytes(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.tbxSendText.Text.Trim()));&lt;span style="color: #606060"&gt;   2:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; Send(&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] data)&lt;span style="color: #606060"&gt;   4:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[0]:填充0，清掉剩余的寄存器&lt;/span&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (data.Length &amp;lt; 60)&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         var input = data;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;         data = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Byte[60];&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;         Array.Copy(input, data, input.Length);&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Connect();&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;     List&amp;lt;&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;&amp;gt; values = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;&amp;gt;(255);&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  15:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[1].Write Header：MODBUS Application Protocol header&lt;/span&gt;&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;     values.AddRange(ValueHelper.Instance.GetBytes(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.NextDataIndex()));&lt;span style="color: #008000"&gt;//1~2.(Transaction Identifier)&lt;/span&gt;&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;     values.AddRange(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Byte[] { 0, 0 });&lt;span style="color: #008000"&gt;//3~4:Protocol Identifier,0 = MODBUS protocol&lt;/span&gt;&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;     values.AddRange(ValueHelper.Instance.GetBytes((&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)(data.Length + 7)));&lt;span style="color: #008000"&gt;//5~6:后续的Byte数量&lt;/span&gt;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;     values.Add(0);&lt;span style="color: #008000"&gt;//7:Unit Identifier:This field is used for intra-system routing purpose.&lt;/span&gt;&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;     values.Add((&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)FunctionCode.Write);&lt;span style="color: #008000"&gt;//8.Function Code : 16 (Write Multiple Register)&lt;/span&gt;&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;     values.AddRange(ValueHelper.Instance.GetBytes(StartingAddress));&lt;span style="color: #008000"&gt;//9~10.起始地址&lt;/span&gt;&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;     values.AddRange(ValueHelper.Instance.GetBytes((&lt;span style="color: #0000ff"&gt;short&lt;/span&gt;)(data.Length / 2)));&lt;span style="color: #008000"&gt;//11~12.寄存器数量&lt;/span&gt;&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;     values.Add((&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)data.Length);&lt;span style="color: #008000"&gt;//13.数据的Byte数量&lt;/span&gt;&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  25:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[2].增加数据&lt;/span&gt;&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;     values.AddRange(data);&lt;span style="color: #008000"&gt;//14~End:需要发送的数据&lt;/span&gt;&lt;span style="color: #606060"&gt;  27:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  28:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[3].写数据&lt;/span&gt;&lt;span style="color: #606060"&gt;  29:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.socketWrapper.Write(values.ToArray());&lt;span style="color: #606060"&gt;  30:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  31:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[4].防止连续读写引起前台UI线程阻塞&lt;/span&gt;&lt;span style="color: #606060"&gt;  32:&lt;/span&gt;     Application.DoEvents();&lt;span style="color: #606060"&gt;  33:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  34:&lt;/span&gt;     &lt;span style="color: #008000"&gt;//[5].读取Response: 写完后会返回12个byte的结果&lt;/span&gt;&lt;span style="color: #606060"&gt;  35:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] responseHeader = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.socketWrapper.Read(12);&lt;span style="color: #606060"&gt;  36:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;(2) 读取数据：&lt;/strong&gt;&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.tbxReceiveText.Text = Encoding.ASCII.GetString(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Wrapper.Receive());&lt;span style="color: #606060"&gt;   2:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   3:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;override&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] Receive()&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.Connect();&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;             List&amp;lt;&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;&amp;gt; sendData = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; List&amp;lt;&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;&amp;gt;(255);&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   8:&lt;/span&gt;             &lt;span style="color: #008000"&gt;//[1].Send&lt;/span&gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;             sendData.AddRange(ValueHelper.Instance.GetBytes(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.NextDataIndex()));&lt;span style="color: #008000"&gt;//1~2.(Transaction Identifier)&lt;/span&gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;             sendData.AddRange(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Byte[] { 0, 0 });&lt;span style="color: #008000"&gt;//3~4:Protocol Identifier,0 = MODBUS protocol&lt;/span&gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;             sendData.AddRange(ValueHelper.Instance.GetBytes((&lt;span style="color: #0000ff"&gt;short&lt;/span&gt;)6));&lt;span style="color: #008000"&gt;//5~6:后续的Byte数量（针对读请求，后续为6个byte）&lt;/span&gt;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;             sendData.Add(0);&lt;span style="color: #008000"&gt;//7:Unit Identifier:This field is used for intra-system routing purpose.&lt;/span&gt;&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;             sendData.Add((&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)FunctionCode.Read);&lt;span style="color: #008000"&gt;//8.Function Code : 3 (Read Multiple Register)&lt;/span&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;             sendData.AddRange(ValueHelper.Instance.GetBytes(StartingAddress));&lt;span style="color: #008000"&gt;//9~10.起始地址&lt;/span&gt;&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;             sendData.AddRange(ValueHelper.Instance.GetBytes((&lt;span style="color: #0000ff"&gt;short&lt;/span&gt;)30));&lt;span style="color: #008000"&gt;//11~12.需要读取的寄存器数量&lt;/span&gt;&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.socketWrapper.Write(sendData.ToArray()); &lt;span style="color: #008000"&gt;//发送读请求&lt;/span&gt;&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  18:&lt;/span&gt;             &lt;span style="color: #008000"&gt;//[2].防止连续读写引起前台UI线程阻塞&lt;/span&gt;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;             Application.DoEvents();&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  21:&lt;/span&gt;             &lt;span style="color: #008000"&gt;//[3].读取Response Header : 完后会返回8个byte的Response Header&lt;/span&gt;&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] receiveData = &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.socketWrapper.Read(256);&lt;span style="color: #008000"&gt;//缓冲区中的数据总量不超过256byte，一次读256byte，防止残余数据影响下次读取&lt;/span&gt;&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;short&lt;/span&gt; identifier = (&lt;span style="color: #0000ff"&gt;short&lt;/span&gt;)((((&lt;span style="color: #0000ff"&gt;short&lt;/span&gt;)receiveData[0]) &amp;lt;&amp;lt; 8) + receiveData[1]);&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  25:&lt;/span&gt;             &lt;span style="color: #008000"&gt;//[4].读取返回数据：根据ResponseHeader，读取后续的数据&lt;/span&gt;&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (identifier != &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.CurrentDataIndex) &lt;span style="color: #008000"&gt;//请求的数据标识与返回的标识不一致，则丢掉数据包&lt;/span&gt;&lt;span style="color: #606060"&gt;  27:&lt;/span&gt;             {&lt;span style="color: #606060"&gt;  28:&lt;/span&gt;                 &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Byte[0];&lt;span style="color: #606060"&gt;  29:&lt;/span&gt;             }&lt;span style="color: #606060"&gt;  30:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt; length = receiveData[8];&lt;span style="color: #008000"&gt;//最后一个字节，记录寄存器中数据的Byte数&lt;/span&gt;&lt;span style="color: #606060"&gt;  31:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] result = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[length];&lt;span style="color: #606060"&gt;  32:&lt;/span&gt;             Array.Copy(receiveData, 9, result, 0, length);&lt;span style="color: #606060"&gt;  33:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; result;&lt;span style="color: #606060"&gt;  34:&lt;/span&gt;         }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;(3) 测试发送和读取：&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172328003787.jpg"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="DataPackage" border="0" alt="DataPackage" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201107/201107172328067587.jpg" width="1034" height="699"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt; ModBus-TCP Client Tool（可以从网上下载，用来测试）中，可以点击“Edit Values”，修改寄存器中的值；然后再在测试程序中，点击“接收”，可以解析到修改后的值。这里只是测试发送和接收字符串，如果需要处理复杂的数字/字符串组合啥的，就需要自己定义数据格式和解析方式了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5.3 代码下载&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://files.cnblogs.com/happyhippy/CSharpModBusExample.rar" target="_blank"&gt;CSharpModBusExample&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:03ba1a78-a866-44bc-a0ec-3b48572c3d55" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/ModBus" rel="tag"&gt;ModBus&lt;/a&gt;,&lt;a href="http://technorati.com/tags/%e5%a4%a7%e5%b0%8f%e7%ab%af" rel="tag"&gt;大小端&lt;/a&gt;,&lt;a href="http://technorati.com/tags/TCP%2fIP" rel="tag"&gt;TCP/IP&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Socket%e7%bc%93%e5%86%b2%e5%8c%ba" rel="tag"&gt;Socket缓冲区&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2108976.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/07/17/2108976.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/07/02/2096482.html</id><title type="text">ProtocolException : 已超过传入消息(65536)的最大消息大小配额。</title><summary type="text">SilverLight调用WCF，提交的是一个List&lt;Linq2SqlEntity&gt;；当List中数据量不大的时候，不会报错；当数据量稍微大一点儿，就会出现这个错误。发生了 System.ServiceModel.ProtocolException Message=已超过传入消息(65536)的最大消息大小配额。若要增加配额，请使用相应绑定元素上的 MaxReceivedMessageSize ...</summary><published>2011-07-02T13:40:00Z</published><updated>2011-07-02T13:40:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/07/02/2096482.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/07/02/2096482.html"/><content type="html">&lt;p&gt;SilverLight调用WCF，提交的是一个List&amp;lt;Linq2SqlEntity&amp;gt;；当List中数据量不大的时候，不会报错；当数据量稍微大一点儿，就会出现这个错误。发生了 System.ServiceModel.&lt;strong&gt;ProtocolException&lt;/strong&gt;&lt;br&gt;&amp;nbsp; Message=已超过传入消息(65536)的最大消息大小配额。若要增加配额，请使用相应绑定元素上的 MaxReceivedMessageSize 属性。&lt;br&gt;&amp;nbsp; Source=System.ServiceModel&lt;br&gt;&amp;nbsp; StackTrace:&lt;br&gt;&amp;nbsp; 在 System.ServiceModel.Channels.HttpInput.ThrowHttpProtocolException(String message, HttpStatusCode statusCode, String statusDescription) &lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 费了牛劲在网上搜了一通，有的说是要修改behavior的maxItemsInObjectGraph，有的说要修改binding的maxBufferSize/maxReceivedMessageSize，有的说要增加readerQuotas，有的是endpoint没有与自定义binding配置节关联起来，有的说要客户端和服务器端都要改(我的应用中，接受数据的是服务器端，客户端负责提交，所以应该不关客户端的事儿)。。。然后把server端的配置文件调整成下面这样：&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &amp;lt;system.serviceModel&amp;gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt;   &amp;lt;behaviors&amp;gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &amp;lt;serviceBehaviors&amp;gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;       &amp;lt;behavior name=&lt;span style="color: #006080"&gt;"MessageHeaderOperationBehaviourAuthenticationBehavior"&lt;/span&gt;&amp;gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         &amp;lt;dataContractSerializer &lt;font color="#ff0000"&gt;maxItemsInObjectGraph=&lt;span style="color: #006080"&gt;"&lt;strong&gt;2147483647&lt;/strong&gt;"&lt;/span&gt;&lt;/font&gt;/&amp;gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;         &amp;lt;serviceMetadata httpGetEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;         &amp;lt;serviceDebug includeExceptionDetailInFaults=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;       &amp;lt;/behavior&amp;gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;     &amp;lt;/serviceBehaviors&amp;gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;   &amp;lt;/behaviors&amp;gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;   &amp;lt;serviceHostingEnvironment aspNetCompatibilityEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt; multipleSiteBindingsEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;   &amp;lt;services&amp;gt;&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;     &amp;lt;service behaviorConfiguration=&lt;span style="color: #006080"&gt;"MessageHeaderOperationBehaviourAuthenticationBehavior"&lt;/span&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;              name=&lt;span style="color: #006080"&gt;"MessageHeaderOperationBehaviourAuthentication"&lt;/span&gt;&amp;gt;&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;       &amp;lt;endpoint address=&lt;span style="color: #006080"&gt;""&lt;/span&gt; binding=&lt;span style="color: #006080"&gt;"basicHttpBinding"&lt;/span&gt; &lt;font color="#ff0000"&gt;bindingConfiguration&lt;/font&gt;=&lt;span style="color: #006080"&gt;"&lt;strong&gt;&lt;font color="#ff0000"&gt;LargeSize&lt;/font&gt;&lt;/strong&gt;"&lt;/span&gt; contract=&lt;span style="color: #006080"&gt;"DyeService"&lt;/span&gt;&amp;gt;&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;       &amp;lt;/endpoint&amp;gt;&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;     &amp;lt;/service&amp;gt;&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;   &amp;lt;/services&amp;gt;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;   &amp;lt;bindings&amp;gt;&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;     &amp;lt;basicHttpBinding&amp;gt;&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;       &amp;lt;binding name=&lt;span style="color: #006080"&gt;"&lt;font color="#ff0000"&gt;&lt;strong&gt;LargeSize&lt;/strong&gt;&lt;/font&gt;"&lt;/span&gt;  maxBufferSize=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;                maxBufferPoolSize=&lt;span style="color: #006080"&gt;"21474836471"&lt;/span&gt; &lt;font color="#ff0000"&gt;maxReceivedMessageSize=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&amp;gt;&lt;/font&gt;&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;         &amp;lt;&lt;font color="#ff0000"&gt;readerQuotas&lt;/font&gt; maxDepth=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt; maxStringContentLength=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;                       maxArrayLength=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt; maxBytesPerRead=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;                       maxNameTableCharCount=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt; /&amp;gt;&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;         &amp;lt;security mode=&lt;span style="color: #006080"&gt;"None"&lt;/span&gt; /&amp;gt;&lt;span style="color: #606060"&gt;  27:&lt;/span&gt;       &amp;lt;/binding&amp;gt;&lt;span style="color: #606060"&gt;  28:&lt;/span&gt;     &amp;lt;/basicHttpBinding&amp;gt;&lt;span style="color: #606060"&gt;  29:&lt;/span&gt;   &amp;lt;/bindings&amp;gt;&lt;span style="color: #606060"&gt;  30:&lt;/span&gt; &amp;lt;/system.serviceModel&amp;gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是问题依旧。最后在stackoverflow找到了正解：&lt;a href="http://stackoverflow.com/questions/4186938/wcf-error-unexpected-response-400-bad-request"&gt;WCF Error - unexpected response: (400) Bad Request.&lt;/a&gt;，里面链接到了另一篇帖子：&lt;a href="http://forums.silverlight.net/forums/p/191589/442381.aspx" target="_blank"&gt;Cannot get the MaxReceivedMessageSize higher than (65536)&lt;/a&gt;&lt;p&gt;&amp;nbsp; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;&lt;font color="#ff0000" size="2"&gt;解决办法就是：配置文件中，service 的name属性必须与服务的类型全名称保持一致，才能应用上自定义的配置节。&lt;/font&gt;&lt;/strong&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 按着这个方法，调整之后的配置文件如下所示（删掉了其他无关的maxItemsInObjectGraph、readerQuotas等）：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   2:&lt;/span&gt;   &amp;lt;system.serviceModel&amp;gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &amp;lt;behaviors&amp;gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;       &amp;lt;serviceBehaviors&amp;gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         &amp;lt;behavior name=&lt;span style="color: #006080"&gt;"MessageHeaderOperationBehaviourAuthenticationBehavior"&lt;/span&gt;&amp;gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;           &amp;lt;serviceMetadata httpGetEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;           &amp;lt;serviceDebug includeExceptionDetailInFaults=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         &amp;lt;/behavior&amp;gt;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;       &amp;lt;/serviceBehaviors&amp;gt;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;     &amp;lt;/behaviors&amp;gt;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;     &amp;lt;serviceHostingEnvironment aspNetCompatibilityEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt; multipleSiteBindingsEnabled=&lt;span style="color: #006080"&gt;"true"&lt;/span&gt;/&amp;gt;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;     &amp;lt;services&amp;gt;&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;       &amp;lt;service behaviorConfiguration=&lt;span style="color: #006080"&gt;"MessageHeaderOperationBehaviourAuthenticationBehavior"&lt;/span&gt;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;                &lt;font color="#ff0000" size="4"&gt;name=&lt;span style="color: #006080"&gt;"NameSpace.DyeService"&lt;/span&gt;&amp;gt;&lt;/font&gt;&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;         &amp;lt;endpoint address=&lt;span style="color: #006080"&gt;""&lt;/span&gt; binding=&lt;span style="color: #006080"&gt;"basicHttpBinding"&lt;/span&gt; &lt;font color="#ff0000"&gt;&lt;font size="4"&gt;bindingConfiguration=&lt;span style="color: #006080"&gt;"LargeSize"&lt;/span&gt;&lt;/font&gt;&lt;/font&gt; &lt;font size="4"&gt;&lt;font color="#ff0000"&gt;contract=&lt;span style="color: #006080"&gt;"NameSpace.DyeService"&lt;/span&gt;&lt;/font&gt;&lt;/font&gt;&amp;gt;&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;         &amp;lt;/endpoint&amp;gt;&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;       &amp;lt;/service&amp;gt;&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;     &amp;lt;/services&amp;gt;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;     &amp;lt;bindings&amp;gt;&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;       &amp;lt;basicHttpBinding&amp;gt;&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;         &amp;lt;binding &lt;font size="4"&gt;&lt;font color="#ff0000"&gt;name=&lt;span style="color: #006080"&gt;"LargeSize"&lt;/span&gt;&lt;/font&gt;&lt;/font&gt;  maxBufferSize=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;                  maxBufferPoolSize=&lt;span style="color: #006080"&gt;"21474836471"&lt;/span&gt; maxReceivedMessageSize=&lt;span style="color: #006080"&gt;"2147483647"&lt;/span&gt;&amp;gt;&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;         &amp;lt;/binding&amp;gt;&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;       &amp;lt;/basicHttpBinding&amp;gt;&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;     &amp;lt;/bindings&amp;gt;&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;   &amp;lt;/system.serviceModel&amp;gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:0cc19e9f-9a55-48c0-ab7c-0d0cd8211ae8" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/WCF" rel="tag"&gt;WCF&lt;/a&gt;,&lt;a href="http://technorati.com/tags/ProtocalException" rel="tag"&gt;ProtocalException&lt;/a&gt;,&lt;a href="http://technorati.com/tags/65535" rel="tag"&gt;65535&lt;/a&gt;,&lt;a href="http://technorati.com/tags/SilverLight" rel="tag"&gt;SilverLight&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2096482.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/07/02/2096482.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/06/26/2090468.html</id><title type="text">ESC/P打印：程序控制打印机自动进退纸</title><summary type="text">打印连续纸张的时候，经常会遇到需要自动进退纸的功能，譬如下列场景：1. 打印完一个小标；(自动进纸)2. 用户撕断打印出来的小标；3. (自动退纸，纸张归位) 接着打印下一个小标； 下列代码在爱普生LQ 730k打印机上测试通过： 1: //使用示例 2: new PrintDirect("PrinterName").PrintESC(1);//打印后进纸 3: new PrintDirect("...</summary><published>2011-06-25T16:00:00Z</published><updated>2011-06-25T16:00:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/06/26/2090468.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/06/26/2090468.html"/><content type="html">&lt;p&gt;打印连续纸张的时候，经常会遇到需要自动进退纸的功能，譬如下列场景：&lt;br&gt;1. 打印完一个小标；(自动进纸)&lt;br&gt;2. 用户撕断打印出来的小标；&lt;br&gt;3. (自动退纸，纸张归位) 接着打印下一个小标；&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;下列代码在爱普生LQ 730k打印机上测试通过：&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #008000"&gt;//使用示例&lt;/span&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; PrintDirect(&lt;span style="color: #006080"&gt;"PrinterName"&lt;/span&gt;).PrintESC(1);&lt;span style="color: #008000"&gt;//打印后进纸&lt;/span&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; PrintDirect(&lt;span style="color: #006080"&gt;"PrinterName"&lt;/span&gt;).PrintESC(0);//打印前退纸&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;源代码：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   2:&lt;/span&gt;     [StructLayout(LayoutKind.Sequential)]&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;struct&lt;/span&gt; DOCINFO&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         [MarshalAs(UnmanagedType.LPWStr)]&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; pDocName;&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;         [MarshalAs(UnmanagedType.LPWStr)]&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; pOutputFile;&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;         [MarshalAs(UnmanagedType.LPWStr)]&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; pDataType;&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  13:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;class&lt;/span&gt; PrintDirect&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;  15:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; PrintPort { get; set; }&lt;span style="color: #606060"&gt;  16:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; PrintDirect(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; port)&lt;span style="color: #606060"&gt;  17:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  18:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PrintPort = port;&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  21:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; OpenPrinter(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt; pPrinterName, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; IntPtr phPrinter, &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; pDefault);&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  24:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; StartDocPrinter(IntPtr hPrinter, &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; Level, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; DOCINFO pDocInfo);&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  27:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  28:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; StartPagePrinter(IntPtr hPrinter);&lt;span style="color: #606060"&gt;  29:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  30:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Ansi, ExactSpelling = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  31:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; WritePrinter(IntPtr hPrinter, &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; data, &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; buf, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; pcWritten);&lt;span style="color: #606060"&gt;  32:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  33:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  34:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; EndPagePrinter(IntPtr hPrinter);&lt;span style="color: #606060"&gt;  35:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  36:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  37:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; EndDocPrinter(IntPtr hPrinter);&lt;span style="color: #606060"&gt;  38:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  39:&lt;/span&gt;         [DllImport(&lt;span style="color: #006080"&gt;"winspool.drv"&lt;/span&gt;, CharSet = CharSet.Unicode, ExactSpelling = &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;, CallingConvention = CallingConvention.StdCall)]&lt;span style="color: #606060"&gt;  40:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;extern&lt;/span&gt; &lt;span style="color: #0000ff"&gt;long&lt;/span&gt; ClosePrinter(IntPtr hPrinter);&lt;span style="color: #606060"&gt;  41:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  42:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;void&lt;/span&gt; PrintESC(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; type)&lt;span style="color: #606060"&gt;  43:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  44:&lt;/span&gt;             System.IntPtr lhPrinter = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; System.IntPtr();&lt;span style="color: #606060"&gt;  45:&lt;/span&gt;             DOCINFO di = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; DOCINFO();&lt;span style="color: #606060"&gt;  46:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;int&lt;/span&gt; pcWritten = 0;&lt;span style="color: #606060"&gt;  47:&lt;/span&gt;             di.pDocName = &lt;span style="color: #006080"&gt;"进退纸Document"&lt;/span&gt;;&lt;span style="color: #606060"&gt;  48:&lt;/span&gt;             di.pDataType = &lt;span style="color: #006080"&gt;"RAW"&lt;/span&gt;;&lt;span style="color: #606060"&gt;  49:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  50:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;try&lt;/span&gt;&lt;span style="color: #606060"&gt;  51:&lt;/span&gt;             {&lt;span style="color: #606060"&gt;  52:&lt;/span&gt;                 PrintDirect.OpenPrinter(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PrintPort, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; lhPrinter, 0);&lt;span style="color: #606060"&gt;  53:&lt;/span&gt;                 &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (lhPrinter == IntPtr.Zero)&lt;span style="color: #606060"&gt;  54:&lt;/span&gt;                 {&lt;span style="color: #606060"&gt;  55:&lt;/span&gt;                     MessageBox.Show(&lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Format(&lt;span style="color: #006080"&gt;"没有连接打印机或者打印机端口不是{0}！"&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;this&lt;/span&gt;.PrintPort),&lt;span style="color: #606060"&gt;  56:&lt;/span&gt;                         &lt;span style="color: #006080"&gt;"提示"&lt;/span&gt;, MessageBoxButtons.OK, MessageBoxIcon.Error);&lt;span style="color: #606060"&gt;  57:&lt;/span&gt;                     &lt;span style="color: #0000ff"&gt;return&lt;/span&gt;;&lt;span style="color: #606060"&gt;  58:&lt;/span&gt;                 }&lt;span style="color: #606060"&gt;  59:&lt;/span&gt;                 PrintDirect.StartDocPrinter(lhPrinter, 1, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; di);&lt;span style="color: #606060"&gt;  60:&lt;/span&gt;                 PrintDirect.StartPagePrinter(lhPrinter);&lt;span style="color: #606060"&gt;  61:&lt;/span&gt;                 &lt;span style="color: #0000ff"&gt;string&lt;/span&gt; send = &lt;span style="color: #0000ff"&gt;string&lt;/span&gt;.Empty;&lt;span style="color: #606060"&gt;  62:&lt;/span&gt;                 &lt;span style="color: #0000ff"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; j = 0; j &amp;lt; 3; j++)&lt;span style="color: #606060"&gt;  63:&lt;/span&gt;                 {&lt;span style="color: #606060"&gt;  64:&lt;/span&gt;                     &lt;span style="color: #0000ff"&gt;switch&lt;/span&gt; (type)&lt;span style="color: #606060"&gt;  65:&lt;/span&gt;                     {&lt;span style="color: #606060"&gt;  66:&lt;/span&gt;                         &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 0:&lt;span style="color: #606060"&gt;  67:&lt;/span&gt;                             send = &lt;span style="color: #006080"&gt;""&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(64) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + &lt;span style="color: #006080"&gt;'j'&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(180);&lt;span style="color: #008000"&gt;//退纸&lt;/span&gt;&lt;span style="color: #606060"&gt;  68:&lt;/span&gt;                             &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;span style="color: #606060"&gt;  69:&lt;/span&gt;                         &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 1:&lt;span style="color: #606060"&gt;  70:&lt;/span&gt;                             send = &lt;span style="color: #006080"&gt;""&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(64) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + &lt;span style="color: #006080"&gt;'J'&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(180);&lt;span style="color: #008000"&gt;//进纸&lt;/span&gt;&lt;span style="color: #606060"&gt;  71:&lt;/span&gt;                             &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;span style="color: #606060"&gt;  72:&lt;/span&gt;                         &lt;span style="color: #0000ff"&gt;case&lt;/span&gt; 2:&lt;span style="color: #606060"&gt;  73:&lt;/span&gt;                             send = &lt;span style="color: #006080"&gt;""&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(64) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(12);   &lt;span style="color: #008000"&gt;//换行（未测试）&lt;/span&gt;&lt;span style="color: #606060"&gt;  74:&lt;/span&gt;                             &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;span style="color: #606060"&gt;  75:&lt;/span&gt;                         &lt;span style="color: #0000ff"&gt;default&lt;/span&gt;:&lt;span style="color: #606060"&gt;  76:&lt;/span&gt;                             send = &lt;span style="color: #006080"&gt;""&lt;/span&gt; + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(27) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(64) + (&lt;span style="color: #0000ff"&gt;char&lt;/span&gt;)(12);   &lt;span style="color: #008000"&gt;//换行（未测试）&lt;/span&gt;&lt;span style="color: #606060"&gt;  77:&lt;/span&gt;                             &lt;span style="color: #0000ff"&gt;break&lt;/span&gt;;&lt;span style="color: #606060"&gt;  78:&lt;/span&gt;                     }&lt;span style="color: #606060"&gt;  79:&lt;/span&gt;                     &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[] buf = &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;[80];&lt;span style="color: #606060"&gt;  80:&lt;/span&gt;                     &lt;span style="color: #0000ff"&gt;for&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;int&lt;/span&gt; i = 0; i &amp;lt; send.Length; i++)&lt;span style="color: #606060"&gt;  81:&lt;/span&gt;                     {&lt;span style="color: #606060"&gt;  82:&lt;/span&gt;                         buf[i] = (&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;)send[i];&lt;span style="color: #606060"&gt;  83:&lt;/span&gt;                     }&lt;span style="color: #606060"&gt;  84:&lt;/span&gt;                     PrintDirect.WritePrinter(lhPrinter, send, send.Length, &lt;span style="color: #0000ff"&gt;ref&lt;/span&gt; pcWritten);&lt;span style="color: #606060"&gt;  85:&lt;/span&gt;                 }&lt;span style="color: #606060"&gt;  86:&lt;/span&gt;             }&lt;span style="color: #606060"&gt;  87:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;catch&lt;/span&gt; (Exception e)&lt;span style="color: #606060"&gt;  88:&lt;/span&gt;             {&lt;span style="color: #606060"&gt;  89:&lt;/span&gt;                 MessageBox.Show(e.Message);&lt;span style="color: #606060"&gt;  90:&lt;/span&gt;             }&lt;span style="color: #606060"&gt;  91:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;finally&lt;/span&gt;&lt;span style="color: #606060"&gt;  92:&lt;/span&gt;             {&lt;span style="color: #606060"&gt;  93:&lt;/span&gt;                 &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (lhPrinter != IntPtr.Zero)&lt;span style="color: #606060"&gt;  94:&lt;/span&gt;                 {&lt;span style="color: #606060"&gt;  95:&lt;/span&gt;                     PrintDirect.EndPagePrinter(lhPrinter);&lt;span style="color: #606060"&gt;  96:&lt;/span&gt;                     PrintDirect.EndDocPrinter(lhPrinter);&lt;span style="color: #606060"&gt;  97:&lt;/span&gt;                     PrintDirect.ClosePrinter(lhPrinter);&lt;span style="color: #606060"&gt;  98:&lt;/span&gt;                 }&lt;span style="color: #606060"&gt;  99:&lt;/span&gt;             }&lt;span style="color: #606060"&gt; 100:&lt;/span&gt;         }&lt;span style="color: #606060"&gt; 101:&lt;/span&gt;     }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:ff00879f-be1b-42bc-925f-ac260e5090ce" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/ESC%2fP" rel="tag"&gt;ESC/P&lt;/a&gt;,&lt;a href="http://technorati.com/tags/%e6%89%93%e5%8d%b0" rel="tag"&gt;打印&lt;/a&gt;,&lt;a href="http://technorati.com/tags/%e8%87%aa%e5%8a%a8%e8%bf%9b%e7%ba%b8" rel="tag"&gt;自动进纸&lt;/a&gt;,&lt;a href="http://technorati.com/tags/%e8%87%aa%e5%8a%a8%e9%80%80%e7%ba%b8" rel="tag"&gt;自动退纸&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2090468.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/06/26/2090468.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/06/21/2086441.html</id><title type="text">判断泛型的值是否为default(T)</title><summary type="text">1: public static bool IsDefault&lt;T&gt;(this T value) 2: { 3: //... 4: }1. ==的问题 如果用==直接判断(default(T) == value)，编译时会提示错误：Error CS0019: 运算符“==”无法应用于“T”和“T”类型的操作数 (CS0019)。2. object.Equals的问题 object提供了一个静态方...</summary><published>2011-06-21T15:00:00Z</published><updated>2011-06-21T15:00:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/06/21/2086441.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/06/21/2086441.html"/><content type="html">&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; IsDefault&amp;lt;T&amp;gt;(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt; T &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;) &lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;       &lt;span style="color: #008000"&gt;//...&lt;/span&gt;&lt;span style="color: #606060"&gt;   4:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;1. ==的问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果用==直接判断(default(T) == value)，编译时会提示错误：Error CS0019: 运算符“==”无法应用于“T”和“T”类型的操作数 (CS0019)。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. object.Equals的问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; object提供了一个静态方法，可用于比较两个对象是否相等：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; Equals(&lt;span style="color: #0000ff"&gt;object&lt;/span&gt; objA, &lt;span style="color: #0000ff"&gt;object&lt;/span&gt; objB)&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (objA == objB)&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;true&lt;/span&gt;;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (objA == &lt;span style="color: #0000ff"&gt;null&lt;/span&gt; || objB == &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;false&lt;/span&gt;;&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; objA.Equals(objB);&lt;span style="color: #606060"&gt;  12:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 但是该方法接收的是引用类型的实例，如果传入的是值类型(譬如int、enum、struct等)，则会对值类型进行装箱(boxing)。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. EqualityComparer&amp;lt;T&amp;gt;.Default.Equals&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 最给力的方法，还是用EqualityComparer&amp;lt;T&amp;gt;.Default.Equals，&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff"&gt;bool&lt;/span&gt; IsDefault&amp;lt;T&amp;gt;(&lt;span style="color: #0000ff"&gt;this&lt;/span&gt; T &lt;span style="color: #0000ff"&gt;value&lt;/span&gt;) &lt;span style="color: #606060"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;      &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; EqualityComparer&amp;lt;T&amp;gt;.Default.Equals(&lt;span style="color: #0000ff"&gt;value&lt;/span&gt;, &lt;span style="color: #0000ff"&gt;default&lt;/span&gt;(T)); &lt;span style="color: #606060"&gt;   4:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 其实现了一个默认的比较器，可以比较Byte、Nullable&amp;lt;T&amp;gt;、Enum、Object、以及实现了IEquatable&amp;lt;T&amp;gt;接口(譬如int、bool、自定义类型等)的所有类型，可以用ILSpy来查看该类型的具体实现：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; EqualityComparer&amp;lt;T&amp;gt; defaultComparer;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; EqualityComparer&amp;lt;T&amp;gt; Default&lt;span style="color: #606060"&gt;   3:&lt;/span&gt; {&lt;span style="color: #606060"&gt;   4:&lt;/span&gt;     [TargetedPatchingOptOut(&lt;span style="color: #006080"&gt;"Performance critical to inline across NGen image boundaries"&lt;/span&gt;), SecuritySafeCritical]&lt;span style="color: #606060"&gt;   5:&lt;/span&gt;     get&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;   7:&lt;/span&gt;         EqualityComparer&amp;lt;T&amp;gt; equalityComparer = EqualityComparer&amp;lt;T&amp;gt;.defaultComparer;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (equalityComparer == &lt;span style="color: #0000ff"&gt;null&lt;/span&gt;)&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  10:&lt;/span&gt;             equalityComparer = EqualityComparer&amp;lt;T&amp;gt;.CreateComparer();&lt;span style="color: #606060"&gt;  11:&lt;/span&gt;             EqualityComparer&amp;lt;T&amp;gt;.defaultComparer = equalityComparer;&lt;span style="color: #606060"&gt;  12:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  13:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; equalityComparer;&lt;span style="color: #606060"&gt;  14:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  15:&lt;/span&gt; }&lt;span style="color: #606060"&gt;  16:&lt;/span&gt; [SecuritySafeCritical]&lt;span style="color: #606060"&gt;  17:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff"&gt;static&lt;/span&gt; EqualityComparer&amp;lt;T&amp;gt; CreateComparer()&lt;span style="color: #606060"&gt;  18:&lt;/span&gt; {&lt;span style="color: #606060"&gt;  19:&lt;/span&gt;     RuntimeType runtimeType = (RuntimeType)&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(T);&lt;span style="color: #606060"&gt;  20:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (runtimeType == &lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;byte&lt;/span&gt;))//其实byte也实现了IEquatable&amp;lt;Byte&amp;gt;，不知道为神马要把byte单独提出来？？？&lt;span style="color: #606060"&gt;  21:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;  22:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; (EqualityComparer&amp;lt;T&amp;gt;)&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; &lt;font color="#ff0000"&gt;ByteEqualityComparer&lt;/font&gt;();&lt;span style="color: #606060"&gt;  23:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  24:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(IEquatable&amp;lt;T&amp;gt;).IsAssignableFrom(runtimeType))&lt;font color="#ff0000"&gt;//如果类型实现了IEquatable&amp;lt;T&amp;gt;，则在运行时构造GenericEqualityComparer&amp;lt;T&amp;gt;，调用类型自身提供(重写)的equals方法。&lt;/font&gt;&lt;span style="color: #606060"&gt;  25:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;  26:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(&lt;font color="#ff0000"&gt;GenericEqualityComparer&lt;/font&gt;&amp;lt;&lt;span style="color: #0000ff"&gt;int&lt;/span&gt;&amp;gt;), runtimeType);&lt;span style="color: #606060"&gt;  27:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  28:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (runtimeType.IsGenericType &amp;amp;&amp;amp; runtimeType.GetGenericTypeDefinition() == &lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(Nullable))&lt;span style="color: #606060"&gt;  29:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;  30:&lt;/span&gt;         RuntimeType runtimeType2 = (RuntimeType)runtimeType.GetGenericArguments()[0];&lt;span style="color: #606060"&gt;  31:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(IEquatable).MakeGenericType(&lt;span style="color: #0000ff"&gt;new&lt;/span&gt; Type[]&lt;span style="color: #606060"&gt;  32:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  33:&lt;/span&gt;             runtimeType2&lt;span style="color: #606060"&gt;  34:&lt;/span&gt;         }).IsAssignableFrom(runtimeType2))&lt;span style="color: #606060"&gt;  35:&lt;/span&gt;         {&lt;span style="color: #606060"&gt;  36:&lt;/span&gt;             &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(&lt;font color="#ff0000"&gt;NullableEqualityComparer&lt;/font&gt;&amp;lt;&lt;span style="color: #0000ff"&gt;int&lt;/span&gt;&amp;gt;), runtimeType2);&lt;span style="color: #606060"&gt;  37:&lt;/span&gt;         }&lt;span style="color: #606060"&gt;  38:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  39:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;if&lt;/span&gt; (runtimeType.IsEnum &amp;amp;&amp;amp; Enum.GetUnderlyingType(runtimeType) == &lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;int&lt;/span&gt;))&lt;span style="color: #606060"&gt;  40:&lt;/span&gt;     {&lt;span style="color: #606060"&gt;  41:&lt;/span&gt;         &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)&lt;span style="color: #0000ff"&gt;typeof&lt;/span&gt;(&lt;font color="#ff0000"&gt;EnumEqualityComparer&lt;/font&gt;&amp;lt;&lt;span style="color: #0000ff"&gt;int&lt;/span&gt;&amp;gt;), runtimeType);&lt;span style="color: #606060"&gt;  42:&lt;/span&gt;     }&lt;span style="color: #606060"&gt;  43:&lt;/span&gt;     &lt;span style="color: #0000ff"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff"&gt;new&lt;/span&gt; &lt;font color="#ff0000"&gt;ObjectEqualityComparer&lt;/font&gt;&amp;lt;T&amp;gt;();&lt;span style="color: #606060"&gt;  44:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. ILSpy比JustDecompile强大呀&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;4.1. ObjectEqualityComparer&amp;lt;T&amp;gt;.Equals方法的比较&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106212300032929.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106212300052014.png" width="757" height="309"&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106212300112293.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="clip_image001" border="0" alt="clip_image001" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106212300153660.png" width="808" height="559"&gt;&lt;/a&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;4.2. EqualityComparer&amp;lt;T&amp;gt;.CreateComparer()方法：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106212300192661.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/20110621230022450.png" width="1190" height="545"&gt;&lt;/a&gt; &lt;p&gt;&lt;/p&gt;&lt;p&gt;各种杯具。。。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:3080bbfb-e27a-42bd-9260-9d0e764e80e4" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/default(T)" rel="tag"&gt;default(T)&lt;/a&gt;,&lt;a href="http://technorati.com/tags/IEquatable%3cT%3e" rel="tag"&gt;IEquatable&amp;lt;T&amp;gt;&lt;/a&gt;,&lt;a href="http://technorati.com/tags/EqualityCompare" rel="tag"&gt;EqualityCompare&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2086441.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/06/21/2086441.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/06/04/2072452.html</id><title type="text">T-SQL ROUND Function Bug?</title><summary type="text">先看一个例子： 1: DECLARE @Value float 2: SET @Value = 12.1785 3: SELECT '12.1785' as ValueToRound, ROUND(@Value,3) as RoundedValue 4: SET @Value = 12.1745 5: SELECT '12.1745' as ValueToRound,ROUND(@Value,3)...</summary><published>2011-06-04T00:04:00Z</published><updated>2011-06-04T00:04:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/06/04/2072452.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/06/04/2072452.html"/><content type="html">&lt;p&gt;先看一个例子：&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; @&lt;span style="color: #0000ff"&gt;Value&lt;/span&gt; &lt;span style="color: #0000ff"&gt;float&lt;/span&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SET&lt;/span&gt; @&lt;span style="color: #0000ff"&gt;Value&lt;/span&gt; = 12.1785&lt;span style="color: #606060"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; &lt;span style="color: #006080"&gt;'12.1785'&lt;/span&gt; &lt;span style="color: #0000ff"&gt;as&lt;/span&gt; ValueToRound, ROUND(@&lt;span style="color: #0000ff"&gt;Value&lt;/span&gt;,3) &lt;span style="color: #0000ff"&gt;as&lt;/span&gt; RoundedValue&lt;span style="color: #606060"&gt;   4:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SET&lt;/span&gt; @&lt;span style="color: #0000ff"&gt;Value&lt;/span&gt; = 12.1745&lt;span style="color: #606060"&gt;   5:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; &lt;span style="color: #006080"&gt;'12.1745'&lt;/span&gt; &lt;span style="color: #0000ff"&gt;as&lt;/span&gt; ValueToRound,ROUND(@&lt;span style="color: #0000ff"&gt;Value&lt;/span&gt;,3) &lt;span style="color: #0000ff"&gt;as&lt;/span&gt; RoundedValue &lt;/div&gt;&lt;/div&gt;&lt;p&gt;猜猜看，结果是多少？这个例子源自：&lt;a href="http://connect.microsoft.com/wf/feedback/details/301942/t-sql-round-function-bug" target="_blank"&gt;T-SQL ROUND Function Bug&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1. 问题起因&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一个老系统，每个月要对系统发生的每笔业务数据进行汇总，生成应收款；但是每个月累计下来，总会与每天的连续累加汇总差(相差几分钱，或者几毛钱)。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 同样的问题还有：仓库里面一批按重量计数的物资，每次出库的时候销账；但偶尔会出现：需要出库的数量，与库存中现有数量一致，但依然无法出库的情况。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 问题的根源，就在于数据的存储类型。系统中，选择了使用float来存储数据。但计算机中，是使用近似值来表示float/double的，在这种+/-过程中，误差不断地累积，就出现了上述的这些问题。关于浮点数的存储格式，可以参考我之前的文章：&lt;a href="http://www.cnblogs.com/happyhippy/archive/2006/11/18/601223.html"&gt;“精确”判断一个浮点数是否等于0&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. Linq to SQL中的Round&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 知道了问题的原因，我们就可以针对性地进行处理：对数据进行四舍五入后再进行求和。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; C#中，提供了两种控制四舍五入的计算方式：MidpointRounding.ToEven和MidpointRounding.AwayFromZero；默认为MidpointRounding.ToEven，即当一个数字是其他两个数字的中间值时，会将其舍入为最接近的偶数。有关舍入方式，可以参考MSDN：“&lt;a href="http://msdn.microsoft.com/zh-cn/library/system.midpointrounding.aspx" target="_blank"&gt;MidpointRounding 枚举&lt;/a&gt;”。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 值得一提的是，里面对MidpointRounding.AwayFromZero的翻译有误，翻译原文为：“AwayFromZero 当一个数字是其他两个数字的中间值时，会将其舍入为两个值中绝对值较小的值。”但英文原文为：“When a number is halfway between two others, it is rounded toward the nearest number that is away from zero.” 文档描述得相当糟糕，不知道是不是我理解能力太差，反正那一坨东西，绕来绕去，还是看里面举的例子比较靠谱。&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #008000"&gt;// 3.4 = Math.Round( 3.45, 1)&lt;/span&gt;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #008000"&gt;//-3.4 = Math.Round(-3.45, 1)&lt;/span&gt;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   4:&lt;/span&gt; &lt;span style="color: #008000"&gt;// 3.4 = Math.Round( 3.45, 1, MidpointRounding.ToEven)&lt;/span&gt;&lt;span style="color: #606060"&gt;   5:&lt;/span&gt; &lt;span style="color: #008000"&gt;// 3.5 = Math.Round( 3.45, 1, MidpointRounding.AwayFromZero)&lt;/span&gt;&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   7:&lt;/span&gt; &lt;span style="color: #008000"&gt;//-3.4 = Math.Round(-3.45, 1, MidpointRounding.ToEven)&lt;/span&gt;&lt;span style="color: #606060"&gt;   8:&lt;/span&gt; //-3.5 = Math.Round(-3.45, 1, MidpointRounding.AwayFromZero)&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; L2S中，如果直接用Math.Round(T.Amount, 2, MidpointRounding.AwayFromZero)，则解析成T-SQL就是直接调用Round函数；如果用Math.Round(T.Amount, 2, MidpointRounding.ToEven)，则会解析为：&lt;/p&gt;&lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt;&lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; (&lt;span style="color: #0000ff"&gt;CASE&lt;/span&gt; &lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;WHEN&lt;/span&gt; ((([t0].[Amount]) * 2) = round(([t0].[Amount]) * 2, 2)) &lt;span style="color: #0000ff"&gt;AND&lt;/span&gt; (([t0].[Amount]) &amp;lt;&amp;gt; round([t0].[Amount], 2)) &lt;span style="color: #0000ff"&gt;THEN&lt;/span&gt; round(([t0].[Amount]) / 2, 2) * 2&lt;span style="color: #606060"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;ELSE&lt;/span&gt; round([t0].[Amount], 2)&lt;span style="color: #606060"&gt;   4:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;END&lt;/span&gt;)&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 解决方法&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3.1 两次Round&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 使用一次Round可以解决小数点取舍问题，但无法处理浮点数精度带来的误差，下面是一次Round带来的结果：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106040803369572.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106040803373094.png" width="245" height="399"&gt;&lt;/a&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp; 这是因为，譬如浮点数2053.345在计算机内部存储的格式可能为2053.2449999999……，一次Round的结果就直接把后面49999…..的给舍掉了。但我们可以使用两次Round来达到需要的舍入效果：Round(Round(Value, 6), 2)&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106040803375536.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201106/201106040803378011.png" width="248" height="390"&gt;&lt;/a&gt; &lt;p&gt;&lt;strong&gt;3.2 Decimal&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 处理精度问题，将数据类型定义为Decimal才是王道。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Decimal在计算及中的存储格式：Decimal 值长度是128位，由 1 位符号、96 位整数以及比例因子组成，比例因子用作 96 位整数的除数并指定整数的哪一部分为小数。比例因子隐式地定为数字 10 的幂，指数范围从 0 到 28。因此，Decimal 值的二进制表示形式为：((-2^96 到 2^96) / 10^(0 到 28))。比例因子还保留 Decimal 数字中的所有尾数零。&lt;/p&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:51709b94-a258-4102-8c6a-bc661e722b15" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/Round" rel="tag"&gt;Round&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Decimal" rel="tag"&gt;Decimal&lt;/a&gt;,&lt;a href="http://technorati.com/tags/Linq+to+Sql" rel="tag"&gt;Linq to Sql&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2072452.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/06/04/2072452.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/05/28/2060553.html</id><title type="text">T-SQL 根据年月日创建DateTime</title><summary type="text">T-SQL中提供了不少操作DateTime的函数，可惜竟然没有根据年月日创建DateTime类型的函数，例如DateTime(yyyy, MM, dd)这样的“构造函数”，杯具啊~~ 1: DECLARE @Year int, @Month int, @Day int; 2: SELECT @Year=2011, @Month=5, @Day=8; 3: 4: --法1： 5: SELECT C...</summary><published>2011-05-28T02:36:00Z</published><updated>2011-05-28T02:36:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/05/28/2060553.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/05/28/2060553.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; T-SQL中提供了不少操作DateTime的函数，可惜竟然没有根据年月日创建DateTime类型的函数，例如DateTime(yyyy, MM, dd)这样的“构造函数”，杯具啊~~&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;div style="border-bottom: gray 1px solid; border-left: gray 1px solid; padding-bottom: 4px; line-height: 12pt; background-color: #f4f4f4; margin: 20px 0px 10px; padding-left: 4px; width: 97.5%; padding-right: 4px; font-family: consolas, 'Courier New', courier, monospace; max-height: 200px; font-size: 8pt; overflow: auto; border-top: gray 1px solid; cursor: text; border-right: gray 1px solid; padding-top: 4px"&gt; &lt;div style="border-bottom-style: none; padding-bottom: 0px; line-height: 12pt; background-color: #f4f4f4; border-left-style: none; padding-left: 0px; width: 100%; padding-right: 0px; font-family: consolas, 'Courier New', courier, monospace; border-top-style: none; color: black; border-right-style: none; font-size: 8pt; overflow: visible; padding-top: 0px"&gt;&lt;span style="color: #606060"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;DECLARE&lt;/span&gt; @&lt;span style="color: #0000ff"&gt;Year&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt;, @&lt;span style="color: #0000ff"&gt;Month&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt;, @&lt;span style="color: #0000ff"&gt;Day&lt;/span&gt; &lt;span style="color: #0000ff"&gt;int&lt;/span&gt;;&lt;span style="color: #606060"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; @&lt;span style="color: #0000ff"&gt;Year&lt;/span&gt;=2011, @&lt;span style="color: #0000ff"&gt;Month&lt;/span&gt;=5, @&lt;span style="color: #0000ff"&gt;Day&lt;/span&gt;=8;&lt;span style="color: #606060"&gt;   3:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   4:&lt;/span&gt; --法1：&lt;span style="color: #606060"&gt;   5:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; &lt;span style="color: #0000ff"&gt;CAST&lt;/span&gt;(&lt;span style="color: #0000ff"&gt;CAST&lt;/span&gt;(@&lt;span style="color: #0000ff"&gt;Year&lt;/span&gt; &lt;span style="color: #0000ff"&gt;AS&lt;/span&gt; &lt;span style="color: #0000ff"&gt;varchar&lt;/span&gt;(4)) + &lt;span style="color: #006080"&gt;'-'&lt;/span&gt; + &lt;span style="color: #0000ff"&gt;CAST&lt;/span&gt;(@&lt;span style="color: #0000ff"&gt;Month&lt;/span&gt; &lt;span style="color: #0000ff"&gt;AS&lt;/span&gt; nvarchar(2)) + &lt;span style="color: #006080"&gt;'-'&lt;/span&gt; + &lt;span style="color: #0000ff"&gt;CAST&lt;/span&gt;(@&lt;span style="color: #0000ff"&gt;Day&lt;/span&gt; &lt;span style="color: #0000ff"&gt;AS&lt;/span&gt; nvarchar(2)) &lt;span style="color: #0000ff"&gt;AS&lt;/span&gt; DateTime)&lt;span style="color: #606060"&gt;   6:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;   7:&lt;/span&gt; --法2：&lt;span style="color: #606060"&gt;   8:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; DateAdd(dd, @&lt;span style="color: #0000ff"&gt;Day&lt;/span&gt;-1, DateAdd(mm, @&lt;span style="color: #0000ff"&gt;Month&lt;/span&gt; -1, DateAdd(yy, @&lt;span style="color: #0000ff"&gt;Year&lt;/span&gt; - 1900, &lt;span style="color: #006080"&gt;'19000101'&lt;/span&gt;)))&lt;span style="color: #606060"&gt;   9:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060"&gt;  10:&lt;/span&gt; --法3：&lt;span style="color: #606060"&gt;  11:&lt;/span&gt; &lt;span style="color: #0000ff"&gt;SELECT&lt;/span&gt; DateAdd(mm, (@&lt;span style="color: #0000ff"&gt;Year&lt;/span&gt; - 1900) * 12 + @&lt;span style="color: #0000ff"&gt;Month&lt;/span&gt; - 1 , @&lt;span style="color: #0000ff"&gt;Day&lt;/span&gt; - 1)&lt;/div&gt;&lt;/div&gt;&lt;div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:0767317B-992E-4b12-91E0-4F059A8CECA8:75eb5d55-8eee-4051-b072-1265ba74ce0d" class="wlWriterEditableSmartContent"&gt;标签: &lt;a href="http://technorati.com/tags/DateTime" rel="tag"&gt;DateTime&lt;/a&gt;,&lt;a href="http://technorati.com/tags/%e6%9e%84%e9%80%a0%e5%87%bd%e6%95%b0" rel="tag"&gt;构造函数&lt;/a&gt;&lt;/div&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/2060553.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/05/28/2060553.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/01/29/1947443.html</id><title type="text">使用键值表实现通用流水号</title><summary type="text">一般的简单的流水号，由标识+日期+自增序号来组成；但如果考虑通用的话，就稍微复杂点儿的，需要考虑自定义日期格式、自增序号归1、自增序号溢出处理、前缀/中缀/后缀、并发访问、批量获取等，本文抽象出一个通用的生成流水号的方案。1. 查询原始数据表 vs. 键值表2. 键值表、取流水号的T-SQL实现3. 并发处理需要考虑的三个因素4. C#封装取流水号操作5. 不给代码怎马叫给力~</summary><published>2011-01-29T05:13:00Z</published><updated>2011-01-29T05:13:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/01/29/1947443.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/01/29/1947443.html"/><content type="html">&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 很多MIS系统，都需要用到流水号；一般的简单的流水号，由标识+日期+自增序号来组成；但如果考虑通用的话，就稍微复杂点儿的，需要考虑自定义日期格式、自增序号归1、自增序号溢出处理、前缀/中缀/后缀、并发访问、批量获取等，本文抽象出一个通用的生成流水号的方案。&lt;/p&gt; &lt;p&gt;1. 查询原始数据表 vs. 键值表&lt;br /&gt;2. 键值表、取流水号的T-SQL实现&lt;br /&gt;3. 并发处理需要考虑的三个因素&lt;br /&gt;4. C#封装取流水号操作&lt;br /&gt;5. 不给代码怎马叫给力~&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. 查询原始数据表 vs. 键值表&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 流水号的变动部分可分为日期和自增序号两部分，日期就是取当前的日期(yyyy、yyMM、或yyyyMMdd等)，自增序号部分可以有如下两种获取方式：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;strong&gt;1.1. 每次查询原始数据表&lt;/strong&gt;：&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 缺点就是要手工&lt;strong&gt;处理并发，如果并发量大的话，性能堪忧&lt;/strong&gt;；好处就是每次可以取得准确的下一个自增序号，如果最后没有保存或者保存失败，取得的序号&lt;strong&gt;可以被重复读得，不会被浪费&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 下面是一个并发示例：譬如当前表中最大的序号(这里暂时只考虑自增序号部分，忽略日期)为003，这是A/B两个用户同时打开页面并取得流水号，此时数据都没有保存，因此他们会取得相同的流水号004，保存时就会出现重复键值(违反唯一性约束)了。保险一点的做法，就是在保存的时候，去校验一下流水号是否已经被用过了；但是校验的时候，也必须十分小心，下面是一个保存时的未考虑并发的&lt;strong&gt;无效校验&lt;/strong&gt;：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312065370.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312099047.png" border="0" width="800" height="454" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 针对这个问题，一种解决办法就是，&lt;strong&gt;将操作串行化，保存前先获得锁&lt;/strong&gt;：&lt;/p&gt; &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312117883.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312146785.png" border="0" width="801" height="404" /&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以在应用程序中加锁(但如果有集群的话，还要考虑多个服务器的问题)，或者锁数据库表。虽然加锁能解决并发问题，但是却带来更严重的性能问题。&lt;strong&gt;每次获取流水号时都要去查询原始数据表(或索引，如果有索引的话)，且插入前要进行加锁，操作只能被串行化，并发量一大，性能是个大问题&lt;/strong&gt;。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1.2. 使用键值表&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 另一种思路就是使用键值表。可以为每个需要使用流水号的表，在键值表中保存一条记录，该记录保存其对应表中当前的最大流水号值。这样的操作的好处是：每次取流水号的时候，只需要操作该表中对应的一条记录即可，而不用去查询原始表/索引；还可以用于批量操作，一次获取一批流水号(批量录入或导入的时候，经常会用到)。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 键值表还需要处理的一个问题，何时更新键值表中的记录(当前最大值)？有两种处理思路：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (1). &lt;strong&gt;采用写时更新&lt;/strong&gt;：能避免每次读取时查询原始表的问题，但还是会遇到上面1.1节中的并发问题。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2). &lt;strong&gt;每次读取最大值的时候更新&lt;/strong&gt;：&lt;strong&gt;先锁记录再读&lt;/strong&gt;，最后更新为新的最大值。下一个人来读的时候，再取到下一个流水号，这样可以获得最大的并发性，但带来的问题是，如果上一个人取到的业务流水号最后没有保存，则这个流水号就废了(跳过去了)，导致最后的实际的业务流水号不连续。如果业务上允许序号被浪费，建议采用这种方式。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 本文的解决方案，也主要是针对后一种(读取时更新)获取流水号的方式。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;&lt;strong&gt;2. 键值表、取流水号的T-SQL实现&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 还虑通用型，可以对业务流水号进行抽象：&lt;strong&gt;流水号 = 前缀+日期+中缀+流水号+后缀&lt;/strong&gt;。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其中：&lt;br /&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 前缀/中缀/后缀：可以包含0个或多个字符；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 日期：可以包含yyMM、yyyy、yyMMdd、yyyyMMdd等多种格式；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 流水号：从1开始累加，按日期归1，长度可扩展(考虑到溢出)；&lt;/strong&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这些信息都可以放在键值表中统一维护。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 继续考虑通用性，可以封装下取流水号的操作，提供一个&lt;strong&gt;批量获取&lt;/strong&gt;方式，一次取一批序号(Max + N)，避免批量操作时循环去取(Max + 1)；批量录入或导入的时候，经常会用到批量获取的方式。&lt;/p&gt; &lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.1 键值表的设计&lt;/strong&gt;&lt;/p&gt; &lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt; &lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; /*happyhippy.cnblogs.com*/ &lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;IF&lt;/span&gt;(OBJECT_ID(&lt;span style="color: #006080;"&gt;'SequenceNumber'&lt;/span&gt;) &lt;span style="color: #0000ff;"&gt;IS&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;NOT&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;NULL&lt;/span&gt;)&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;DROP&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;TABLE&lt;/span&gt; SequenceNumber;&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;   5:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;Create&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;Table&lt;/span&gt; SequenceNumber&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt; (&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;     ID &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;identity&lt;/span&gt;(1,1),&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;     Code nvarchar(10) &lt;font size="2"&gt;&lt;font color="#ff0000"&gt;&lt;strong&gt;&lt;span style="color: #0000ff;"&gt;primary&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;key&lt;/span&gt;&lt;/strong&gt;&lt;/font&gt;&lt;/font&gt;,    /*&lt;span style="color: #0000ff;"&gt;Key&lt;/span&gt;*/&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt; nvarchar(5),   /*前缀*/&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;     DateType nvarchar(8), /*日期类型，可以为yyyy，yymm, yyyymm，yymmdd，yyyymmdd等等等等。*/&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;     Infix nvarchar(5),    /*中缀*/&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;     IndexLength &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt;,      /*自增流水号长度*/&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;     Suffix nvarchar(5),   /*后缀*/&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;     MaxDate nvarchar(8),  /*当前最大日期值*/&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;     MaxIndex &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;(0),/*当前最大流水号值*/&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;     CurrentMaxValue &lt;span style="color: #0000ff;"&gt;AS&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt; + MaxDate + Infix + Replace(STR(MaxIndex, IndexLength), &lt;span style="color: #006080;"&gt;' '&lt;/span&gt; , &lt;span style="color: #006080;"&gt;'0'&lt;/span&gt;) + Suffix)&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt; )&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (1). 表的主键设置在Code字段上；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2). MaxData、MaxIndex等记录当前最大值，用于直接运算。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2.2 T-SQL获取流水号&lt;/strong&gt;&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; /*happyhippy.cnblogs.com*/&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;go&lt;/span&gt;&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;IF&lt;/span&gt;(OBJECT_ID(&lt;span style="color: #006080;"&gt;'GetSequenceNumber'&lt;/span&gt;) &lt;span style="color: #0000ff;"&gt;IS&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;NOT&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;NULL&lt;/span&gt;)&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;DROP&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;PROCEDURE&lt;/span&gt;  GetSequenceNumber;&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #606060;"&gt;   6:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;go&lt;/span&gt;&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;CREATE&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;PROCEDURE&lt;/span&gt; GetSequenceNumber&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt; (&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;     @Code nvarchar(10),&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;     @&lt;span style="color: #0000ff;"&gt;Count&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; = 1&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt; )&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;AS&lt;/span&gt;&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;BEGIN&lt;/span&gt;&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;DECLARE&lt;/span&gt; @NewValue nvarchar(20), @CurrentDate nvarchar(8);&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;DECLARE&lt;/span&gt; @&lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt; nvarchar(5), @DateType nvarchar(8), @Infix nvarchar(5), @Suffix nvarchar(5);&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;DECLARE&lt;/span&gt; @MaxIndex &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt;, @IndexLength tinyint, @MaxDate nvarchar(8);&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  18:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;BEGIN&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;TRAN&lt;/span&gt;&lt;span style="color: #606060;"&gt;  19:&lt;/span&gt;         --读取配置信息&lt;span style="color: #606060;"&gt;  20:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;SELECT&lt;/span&gt;  @&lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt; = &lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt;, @Infix = Infix, @Suffix = Suffix,&lt;span style="color: #606060;"&gt;  21:&lt;/span&gt;                 @DateType = DateType, @MaxDate=MaxDate,&lt;span style="color: #606060;"&gt;  22:&lt;/span&gt;                 @MaxIndex = MaxIndex, @IndexLength = IndexLength&lt;span style="color: #606060;"&gt;  23:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;FROM&lt;/span&gt; SequenceNumber &lt;span style="color: #0000ff;"&gt;with&lt;/span&gt;&lt;font color="#ff0000" size="4"&gt;&lt;strong&gt;(xlock)&lt;/strong&gt;&lt;/font&gt; &lt;span style="color: #0000ff;"&gt;WHERE&lt;/span&gt; Code=@Code;&lt;span style="color: #606060;"&gt;  24:&lt;/span&gt;         &lt;span style="color: #606060;"&gt;  25:&lt;/span&gt;         --取得日期部分，如果需要其他格式，需要自己再扩展，增加CASE分支。&lt;span style="color: #606060;"&gt;  26:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;SET&lt;/span&gt; @CurrentDate= &lt;span style="color: #0000ff;"&gt;SUBSTRING&lt;/span&gt;(&lt;span style="color: #0000ff;"&gt;Convert&lt;/span&gt;(nvarchar(8), GetDate(), 112),&lt;span style="color: #606060;"&gt;  27:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;CASE&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;SubString&lt;/span&gt;(@DateType, 1, 4)&lt;span style="color: #606060;"&gt;  28:&lt;/span&gt;                 &lt;span style="color: #0000ff;"&gt;WHEN&lt;/span&gt; &lt;span style="color: #006080;"&gt;'yyyy'&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;THEN&lt;/span&gt; 1&lt;span style="color: #606060;"&gt;  29:&lt;/span&gt;                 &lt;span style="color: #0000ff;"&gt;WHEN&lt;/span&gt; &lt;span style="color: #006080;"&gt;'yyy'&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;THEN&lt;/span&gt; 2&lt;span style="color: #606060;"&gt;  30:&lt;/span&gt;                 &lt;span style="color: #0000ff;"&gt;ELSE&lt;/span&gt; 3&lt;span style="color: #606060;"&gt;  31:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;END&lt;/span&gt;, LEN(@DateType));&lt;span style="color: #606060;"&gt;  32:&lt;/span&gt;         &lt;span style="color: #606060;"&gt;  33:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;IF&lt;/span&gt;(@CurrentDate = @MaxDate)            &lt;span style="color: #606060;"&gt;  34:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;SET&lt;/span&gt; @MaxIndex = @MaxIndex + @&lt;span style="color: #0000ff;"&gt;Count&lt;/span&gt;; --累加&lt;span style="color: #606060;"&gt;  35:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;ELSE&lt;/span&gt;&lt;span style="color: #606060;"&gt;  36:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;SET&lt;/span&gt; @MaxIndex = @&lt;span style="color: #0000ff;"&gt;Count&lt;/span&gt;; --归1&lt;span style="color: #606060;"&gt;  37:&lt;/span&gt;         &lt;span style="color: #606060;"&gt;  38:&lt;/span&gt;         &lt;span style="color: #606060;"&gt;  39:&lt;/span&gt;         --超过自增长度限制，自动扩展自增部分的长度&lt;span style="color: #606060;"&gt;  40:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;IF&lt;/span&gt;(@MaxIndex &amp;gt;= POWER(10, @IndexLength)) &lt;span style="color: #606060;"&gt;  41:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;SET&lt;/span&gt; @IndexLength = @IndexLength + 1; &lt;span style="color: #606060;"&gt;  42:&lt;/span&gt;         &lt;span style="color: #606060;"&gt;  43:&lt;/span&gt;         --可以取消下面一行的注释，来测试并发&lt;span style="color: #606060;"&gt;  44:&lt;/span&gt;         --&lt;span style="color: #0000ff;"&gt;Waitfor&lt;/span&gt; delay &lt;span style="color: #006080;"&gt;'00:00:10'&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  45:&lt;/span&gt;             &lt;span style="color: #606060;"&gt;  46:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;Update&lt;/span&gt; SequenceNumber &lt;span style="color: #0000ff;"&gt;SET&lt;/span&gt; MaxDate = @CurrentDate, MaxIndex=@MaxIndex, IndexLength=@IndexLength &lt;span style="color: #0000ff;"&gt;WHERE&lt;/span&gt; Code=@Code;&lt;span style="color: #606060;"&gt;  47:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;COMMIT&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;TRAN&lt;/span&gt;&lt;span style="color: #606060;"&gt;  48:&lt;/span&gt;     &lt;span style="color: #606060;"&gt;  49:&lt;/span&gt;     --取得获取到的最大值，取得@IndexLength和Len(@Suffix)用于解析得到批量获取的序列号&lt;span style="color: #606060;"&gt;  50:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;SELECT&lt;/span&gt; (@&lt;span style="color: #0000ff;"&gt;Prefix&lt;/span&gt; + @CurrentDate + @Infix + Replace(STR(@MaxIndex, @IndexLength), &lt;span style="color: #006080;"&gt;' '&lt;/span&gt; , &lt;span style="color: #006080;"&gt;'0'&lt;/span&gt;) + @Suffix), @IndexLength, Len(@Suffix);&lt;span style="color: #606060;"&gt;  51:&lt;/span&gt; END&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 注意：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (1). 整个读取、更新过程，封装在一次事务操作中；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2) 参数@Count，可以传一个正整数，批量获取多个流水号；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (3). 第19~22行，读取的时候获取排它锁(xlock)，用于处理并发情况；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (4). 第39~41行，如果溢出，则自动扩展自增序号的宽度；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 并发需要考虑的几个因素&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 并发要考虑两种情况：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (1) 并发访问&lt;strong&gt;同一种类&lt;/strong&gt;的序列号(键值表中的一个Key)时，必须&lt;strong&gt;串行访问&lt;/strong&gt;，以防止取得相同的流水号；&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; (2) 并发访问&lt;strong&gt;不同种类&lt;/strong&gt;的序列号(键值表中的不同Key)时，必须允许&lt;strong&gt;并发访问&lt;/strong&gt;，互不干扰才能获得最大的并发度；&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3.1 在应用程序中处理锁，还是在数据库中处理锁？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; .Net中提供了现成lock、Monitor等，我们可以用来处理锁；譬如可以维护一个字典Dictionary&amp;lt;string, Object&amp;gt;，Key中保存键值表中对应的键值，Value保存同步对象，伪代码如下：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; dictionarySyncObj = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;();&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; Dictionary&amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;&amp;gt; syncDictionary = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;&amp;gt;();&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; GetMaxSequenceNumber(&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; key)&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;lock&lt;/span&gt; (dictionarySyncObj)&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!syncDictionary.ContainsKey(key))&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;         {&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;             syncDictionary.Add(key, &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;());&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;         }&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;     Object keySyncObj = syncDictionary[key];&lt;span style="color: #008000;"&gt;//针对不同的Key，使用不同的同步对象&lt;/span&gt;&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;lock&lt;/span&gt; (keySyncObj)&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;         &lt;span style="color: #008000;"&gt;//从数据库读取最大流水号....&lt;/span&gt;&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; ....&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  18:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 程序中所有需要取流水号的地方，都调用该函数来获取，以保证对同一种类序列号的访问被串行化。如果系统只是部署在单台服务器上，这种方法没有问题；但是如果使用了服务器集群，系统在多个系统上部署了多份，则还是无法串行化对同一个Key的所有访问。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 比较理想的做法，是在一个统一的地方处理并发，譬如在数据库中。上面第2节中，给出的键值表实现和获取流水号的存储过程，其实已经实现了并发处理，下面展开进行讨论。讨论之前，先执行下列代码来构造几个测试用例：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #008000;"&gt;/*构造测试用例*/&lt;/span&gt;&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; INSERT INTO SequenceNumber(Code, Prefix, DateType, Infix, IndexLength, Suffix)&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     VALUES(&lt;span style="color: #006080;"&gt;'Test1'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'P'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'yyyy'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;''&lt;/span&gt;, 8, &lt;span style="color: #006080;"&gt;''&lt;/span&gt;),&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;         (&lt;span style="color: #006080;"&gt;'Test2'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;''&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'yymmdd'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'M'&lt;/span&gt;, 6, &lt;span style="color: #006080;"&gt;''&lt;/span&gt;),&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;         (&lt;span style="color: #006080;"&gt;'Test3'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'P'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'yymmdd'&lt;/span&gt;, &lt;span style="color: #006080;"&gt;'M'&lt;/span&gt;, 6, &lt;span style="color: #006080;"&gt;'S'&lt;/span&gt;);&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;   7:&lt;/span&gt; UPDATE SequenceNumber SET MaxDate= SUBSTRING(Convert(nvarchar(8), GetDate(), 112),&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;             CASE SubString(DateType, 1, 4)&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;                 WHEN &lt;span style="color: #006080;"&gt;'yyyy'&lt;/span&gt; THEN 1&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;                 WHEN &lt;span style="color: #006080;"&gt;'yyy'&lt;/span&gt; THEN 2&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;                 ELSE 3&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;             END, LEN(DateType));&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp; 3.2 串行化访问&lt;strong&gt;同一种类&lt;/strong&gt;的序列号&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 默认情况下(Read Committed事务隔离级别)，读取操作会对对应的数据Key(或行)加S锁(不考虑锁升级的情况)，对该行所属的页和表加IS锁；读取完毕后，就释放这些IS锁和S锁。可以加表提示(with (holdlock))，来让会话强制持有锁，直至事务结束(提交或回滚)后才释放锁。但是，如果多个会话并发访问的时候，由于IS锁与IS锁之间是兼容的，在值被更新(持有更新锁ulock)之前，可以并发读得相同的数据，因此这里&lt;strong&gt;读取时，必须要用排它锁(xlock)来独占资源，当一个线程读的时候，不允许其他线程并发读&lt;/strong&gt;。有关并发和锁兼容性的更多介绍，可以参考我之前的文章《&lt;a href="http://www.cnblogs.com/happyhippy/archive/2008/11/14/1333922.html"&gt;SQL Server死锁总结&lt;/a&gt;》。&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以取消2.2节中存储过程&lt;span style="color: #006080;"&gt;GetSequenceNumber&lt;/span&gt;中的第44行(Waitfor delay '00:00:10';)的注释，让T-SQL执行时等待10秒钟，以比较测试结果。&lt;strong&gt;开两个窗口&lt;/strong&gt;分别&lt;strong&gt;同时执行&lt;/strong&gt;下列一段测试代码：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;exec&lt;/span&gt; dbo.GetSequenceNumber &lt;span style="color: #006080;"&gt;'Test2'&lt;/span&gt;, 1;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第一个窗口的执行结果：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312169491.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312171019.png" border="0" width="775" height="197" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二个窗口的执行结果(操作过程中存在延时，所以显示的只有18秒)：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312195677.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312204381.png" border="0" width="786" height="207" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 虽然两个会话&amp;#8220;&lt;strong&gt;同时&lt;/strong&gt;&amp;#8221;执行(第二个会话，我在操作时存在延时，所以显示的只有18秒)，但两个会话没有读得相同的序列号。执行时，第二个会话等待被阻塞等待了；只有等到第一个会话执行完毕后，第二个会话才获得锁资源，并继续执行；因此用了2倍的时间(20秒)。这就达到了多线程访问同一个Key时必须被串行化的效果。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&amp;nbsp;&amp;nbsp; 3.3 并发访问&lt;strong&gt;不同种类&lt;/strong&gt;的序列号&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 多线程并发访问&lt;strong&gt;不同种类&lt;/strong&gt;的序列号(键值表中的不同Key)时，必须允许&lt;strong&gt;并发访问&lt;/strong&gt;，互不干扰才能获得最大的并发度。在2.1节键值表的设计中，我将Code设为主键，这样做的一个好处，就是在读取一条Code记录并获取锁的时候，锁的粒度只会限制在Key锁，而不会升级为页锁或表锁。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 现在开两个查询窗口，分别&lt;strong&gt;同时执行&lt;/strong&gt;下列两段代码（注意：这次，两个窗口访问的是不同的Key）：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;exec&lt;/span&gt; dbo.GetSequenceNumber &lt;span style="color: #006080;"&gt;'Test2'&lt;/span&gt;, 1;&lt;/div&gt;&lt;/div&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;exec&lt;/span&gt; dbo.GetSequenceNumber &lt;span style="color: #006080;"&gt;'Test3'&lt;/span&gt;, 1;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第一个窗口的执行结果：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312226531.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312235234.png" border="0" width="773" height="201" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 第二个窗口的执行结果：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/20110129131225731.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312263023.png" border="0" width="780" height="184" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 虽然两个会话同时执行，但是第二个会话，并没有被第一个会话阻塞，所以第二个会话也只用10秒就执行完毕了。两个会话可以并发执行，这就达到了多线程可以并发访问不同Key的效果。 &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果执行上面的查询时，我们sp_lock来查看锁的情况，也可以看到： &lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312295305.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101291312316899.png" border="0" width="848" height="416" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 参考我之前的文章《&lt;a href="http://www.cnblogs.com/happyhippy/archive/2008/11/14/1333922.html"&gt;SQL Server死锁总结&lt;/a&gt;》，并结合上图，可以看到，两个X锁是应用在不同的Resource上，他们之间不会冲突；IX锁虽然应用在同一个Table上/Page(1:828)上，但IX锁与IX锁之间是兼容的，他们之间也不存在冲突；因此多个线程之间不会相互影响。回过头来考虑3.2节中的测试，两个会话尝试对同一个Key加X锁，但X锁与X锁之间是不兼容的，因此读取操作被串行化了。这里利用SQL Server的锁机制来实现并行化/串行化的目的。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 抛一个问题，如果键值表的主键，不在Code字段上，还能并发访问&lt;strong&gt;不同种类&lt;/strong&gt;的序列号吗？有兴趣的可以试试。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. C#封装取流水号操作&lt;/strong&gt;&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; ReadOnlyCollection&amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt; GetSequenceNumbers(SequenceType type, &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; count = 1)&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; maxSequenceNumber = &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;.Empty;&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt; indexLength = 0;&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;byte&lt;/span&gt; suffixLength = 0;&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;     &lt;span style="color: #008000;"&gt;//以上三个值，调用存储过程读取，省略。。。。&lt;/span&gt;&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;     &lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (count == 1)&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; List&amp;lt;&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt;&amp;gt;() { maxSequenceNumber }).AsReadOnly();&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt;&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; prefix = maxSequenceNumber.Substring(0, maxSequenceNumber.Length - indexLength - suffixLength);&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; index = Convert.ToInt32(maxSequenceNumber.Substring(prefix.Length, indexLength));&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; suffix = maxSequenceNumber.Substring(maxSequenceNumber.Length - suffixLength);&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  18:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; format = &lt;span style="color: #006080;"&gt;"0000000000"&lt;/span&gt;.Substring(0, indexLength);&lt;span style="color: #606060;"&gt;  19:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; Enumerable.Range(index - count + 1, count)&lt;span style="color: #606060;"&gt;  20:&lt;/span&gt;                     .Select(i =&amp;gt; prefix + i.ToString(format) + suffix)&lt;span style="color: #606060;"&gt;  21:&lt;/span&gt;                     .ToList()&lt;span style="color: #606060;"&gt;  22:&lt;/span&gt;                     .AsReadOnly();&lt;span style="color: #606060;"&gt;  23:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  24:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;使用方式：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;foreach&lt;/span&gt; (&lt;span style="color: #0000ff;"&gt;string&lt;/span&gt; item &lt;span style="color: #0000ff;"&gt;in&lt;/span&gt; SequenceNumber.GetSequenceNumbers(SequenceType.Test3, 3))&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;    Response.Write(item + &lt;span style="color: #006080;"&gt;"&amp;lt;br/&amp;gt;"&lt;/span&gt;);&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. 不给代码怎马叫给力~&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;a href="http://files.cnblogs.com/happyhippy/happyhippy.cnblogs.com.SequenceNumber.rar"&gt;happyhippy.cnblogs.com.SequenceNumber.rar&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;参考文献：&lt;br /&gt;《企业应用架构模式》&lt;/p&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/1947443.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/01/29/1947443.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/happyhippy/archive/2011/01/12/1934006.html</id><title type="text">[SilverLight]DataGrid实现批量输入(like Excel)（补充）</title><summary type="text">1. Tab/Enter跳转到下一列；2. 最后一行最后一列，Tab/Enter自动增加新行；3. 增加新行后，自动跳转到新增行的第一列；4. 删除行后，自动选中上一行；5. Up/Down/Left/Right自动编辑；</summary><published>2011-01-12T12:33:00Z</published><updated>2011-01-12T12:33:00Z</updated><author><name>Silent Void</name><uri>http://www.cnblogs.com/happyhippy/</uri></author><link rel="alternate" href="http://www.cnblogs.com/happyhippy/archive/2011/01/12/1934006.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/happyhippy/archive/2011/01/12/1934006.html"/><content type="html">&lt;p&gt;更新内容：&lt;/p&gt; &lt;p&gt;1. Tab/Enter跳转到下一列；&lt;br /&gt;2. 最后一行最后一列，Tab/Enter自动增加新行；&lt;br /&gt;3. 增加新行后，自动跳转到新增行的第一列；&lt;br /&gt;4. 删除行后，自动选中上一行；&lt;br /&gt;5. Up/Down/Left/Right自动编辑；&lt;/p&gt; &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;实现方式：&lt;/p&gt; &lt;p&gt;&lt;strong&gt;1. Tab/Enter跳转到下一列&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 从google搜到的一篇文章《&lt;a href="http://forums.silverlight.net/forums/p/206694/485086.aspx" target="_blank"&gt;Datagrid cell navigation with enter key&lt;/a&gt;》，文中最开始提到在DataGrid的KeyDown事件中进行处理。但是DataGrid里面做了一些封装，我们在自己注册(通过+=操作符来登记、或者XAML中定义)的KeyDown事件中，截获不到Enter/Tab等键。所以最后原作者有提出一个思路：继承DataGrid，重新OnKeyDown方法。&lt;/p&gt; &lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但我之前的思路，是通过扩展方法来扩展DataGrid，再来重写就有点儿郁闷了。于是找出Reflector，翻了下DataGrid的实现(reflector中copy出来的)：&lt;/p&gt; &lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt; &lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #008000;"&gt;// happyhippy.cnblogs.com&lt;/span&gt;&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; &lt;span style="color: #008000;"&gt;// 构造函数&lt;/span&gt;&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; DataGrid()&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #008000;"&gt;//省略&lt;/span&gt;&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.KeyDown += &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; KeyEventHandler(&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.DataGrid_KeyDown);&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.KeyUp += &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; KeyEventHandler(&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.DataGrid_KeyUp);&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;     &lt;span style="color: #008000;"&gt;//省略&lt;/span&gt;&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt; }&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  11:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; DataGrid_KeyDown(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, KeyEventArgs e)&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!e.Handled)&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;         e.Handled = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessDataGridKey(e);&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt; }&lt;span style="color: #606060;"&gt;  18:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  19:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt; ProcessDataGridKey(KeyEventArgs e)&lt;span style="color: #606060;"&gt;  20:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;  21:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt; flag = &lt;span style="color: #0000ff;"&gt;false&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  22:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;switch&lt;/span&gt; (e.Key)&lt;span style="color: #606060;"&gt;  23:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  24:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Tab:&lt;span style="color: #606060;"&gt;  25:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessTabKey(e);&lt;span style="color: #606060;"&gt;  26:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  27:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Enter:&lt;span style="color: #606060;"&gt;  28:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessEnterKey();&lt;span style="color: #606060;"&gt;  29:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  30:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  31:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Escape:&lt;span style="color: #606060;"&gt;  32:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessEscapeKey();&lt;span style="color: #606060;"&gt;  33:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  34:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.PageUp:&lt;span style="color: #606060;"&gt;  35:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessPriorKey();&lt;span style="color: #606060;"&gt;  36:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  37:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  38:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.PageDown:&lt;span style="color: #606060;"&gt;  39:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessNextKey();&lt;span style="color: #606060;"&gt;  40:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  41:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  42:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.End:&lt;span style="color: #606060;"&gt;  43:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessEndKey();&lt;span style="color: #606060;"&gt;  44:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  45:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  46:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Home:&lt;span style="color: #606060;"&gt;  47:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessHomeKey();&lt;span style="color: #606060;"&gt;  48:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  49:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  50:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Left:&lt;span style="color: #606060;"&gt;  51:&lt;/span&gt;             flag = (&lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.FlowDirection == FlowDirection.LeftToRight) ? &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessLeftKey() : &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessRightKey();&lt;span style="color: #606060;"&gt;  52:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  53:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  54:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Up:&lt;span style="color: #606060;"&gt;  55:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessUpKey();&lt;span style="color: #606060;"&gt;  56:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  57:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  58:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Right:&lt;span style="color: #606060;"&gt;  59:&lt;/span&gt;             flag = (&lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.FlowDirection == FlowDirection.LeftToRight) ? &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessRightKey() : &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessLeftKey();&lt;span style="color: #606060;"&gt;  60:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  61:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  62:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Down:&lt;span style="color: #606060;"&gt;  63:&lt;/span&gt;             flag = &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessDownKey();&lt;span style="color: #606060;"&gt;  64:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  65:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  66:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Insert:&lt;span style="color: #606060;"&gt;  67:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessCopyKey();&lt;span style="color: #606060;"&gt;  68:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  69:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.A:&lt;span style="color: #606060;"&gt;  70:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessAKey();&lt;span style="color: #606060;"&gt;  71:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  72:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.C:&lt;span style="color: #606060;"&gt;  73:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessCopyKey();&lt;span style="color: #606060;"&gt;  74:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  75:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.F2:&lt;span style="color: #606060;"&gt;  76:&lt;/span&gt;             &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;.ProcessF2Key(e);&lt;span style="color: #606060;"&gt;  77:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  78:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (flag &amp;amp;&amp;amp; &lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.IsTabStop)&lt;span style="color: #606060;"&gt;  79:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  80:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;base&lt;/span&gt;.Focus();&lt;span style="color: #606060;"&gt;  81:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  82:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt; flag;&lt;span style="color: #606060;"&gt;  83:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; 我们可以发现，KeyDown中已经定义一部分按键的处理，并更新了KeyEventArgs.Handled属性。MSDN对该属性的解释是：&lt;/p&gt;&lt;table width="739" border="0" cellpadding="2" cellspacing="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="737"&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 将事件标记为已处理将会&lt;strong&gt;限制路由事件在事件路由过程中对于侦听器的可见性&lt;/strong&gt;。事件将仍然进行路由过程的其余部分，但&lt;strong&gt;只会在响应中调用在 AddHandler(RoutedEvent, Delegate, Boolean) 方法调用中明确添加的其 HandledEventsToo 为 true 的处理程序&lt;/strong&gt;。 将不会调用实例侦听器上的默认处理程序（比如用可扩展应用程序标记语言 (XAML) 表示的那些处理程序）。处理标记为已处理的事件这种情形并不常见。 &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果您是定义您自己的事件的控件作者，则您在类级别所做的有关事件处理的决策将影响您的控件的用户，以及派生控件的任何用户，并可能会影响您的控件所包含或包含您的控件的其他元素。有关更多信息，请参见 将路由事件标记为&amp;#8220;已处理&amp;#8221;和&amp;#8220;类处理&amp;#8221;。 &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在极少数情况下，适合处理其中 Handled 标记为 true 的事件，并通过将 Handled 更改为 false 来修改事件参数。 在控件输入事件的某些区域（比如 KeyDown 与 TextInput 键处理）中，低级别和高级别的输入事件会争用处理，并且每个事件都会尝试使用不同的路由策略，因此，可能必须要这样做。 &lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 也就是说，在将KeyEventArgs.Handled，我们通过+=来自己注册的处理程序，就无法执行了。一种替代思路就是在不要用+=来注册处理程序，而是调用&lt;strong&gt;AddHandler(RoutedEvent, Delegate, Boolean) &lt;/strong&gt;方法来注册处理程序：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #008000;"&gt;//之前的处理方式，将监听不到已经处理过的Tab/Enter等键&lt;/span&gt;&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; &lt;span style="color: #008000;"&gt;//dataGrid.KeyDown += KeyDownHandler&amp;lt;T&amp;gt;; //注册按键事件&lt;/span&gt;&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; &lt;span style="color: #008000;"&gt;//只能通过下列方式来注册&lt;/span&gt;&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; dataGrid.AddHandler(DataGrid.KeyDownEvent, &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; KeyEventHandler(KeyDownHandler&amp;lt;T&amp;gt;), &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;);&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 但在使用的时候发现，如果当前Cell(TextBlock)处于编辑状态，要按两次Tab/Enter才能跳转到下一列：第一次提交更改，第二次才执行跳转操作。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 回过头来看，上面的DataGrid的部分源码，跟进去看了下KeyUp事件的处理，发现KeyUp并没有去更改KeyEventArgs.Handled属性。因此，我们可以考虑在DataGrid上注册自定义KeyUp事件，来做一些额外处理：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; dataGrid.KeyUp += KeyUpHandler&amp;lt;T&amp;gt;;&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; KeyUpHandler&amp;lt;T&amp;gt;(&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sender, KeyEventArgs e) &lt;span style="color: #0000ff;"&gt;where&lt;/span&gt; T : &lt;span style="color: #0000ff;"&gt;class&lt;/span&gt;, &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;()&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;       DataGrid dataGrid = sender &lt;span style="color: #0000ff;"&gt;as&lt;/span&gt; DataGrid;&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;       &lt;span style="color: #0000ff;"&gt;switch&lt;/span&gt; (e.Key)&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;       {&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;              &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Tab:&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;                     &lt;span style="color: #0000ff;"&gt;goto&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Enter;&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;              &lt;span style="color: #0000ff;"&gt;case&lt;/span&gt; Key.Enter:&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;                     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (dataGrid.CurrentColumn != &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;)&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;                     {&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;                         &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (dataGrid.IsLastRow() &amp;amp; dataGrid.IsLastCell()) &lt;span style="color: #008000;"&gt;// Last Row And Last Cell (End of the grid)&lt;/span&gt;&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;                         {&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;                             dataGrid.AddEntity&amp;lt;T&amp;gt;();&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;                         }&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;                         &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (dataGrid.IsLastCell()) &lt;span style="color: #008000;"&gt;// Last Cell&lt;/span&gt;&lt;span style="color: #606060;"&gt;  18:&lt;/span&gt;                         {&lt;span style="color: #606060;"&gt;  19:&lt;/span&gt;                             dataGrid.SelectedIndex = dataGrid.SelectedIndex + 1;&lt;span style="color: #606060;"&gt;  20:&lt;/span&gt;                             dataGrid.NextCellToEdit();&lt;span style="color: #606060;"&gt;  21:&lt;/span&gt;                         }&lt;span style="color: #606060;"&gt;  22:&lt;/span&gt;                         &lt;span style="color: #0000ff;"&gt;else&lt;/span&gt; &lt;span style="color: #008000;"&gt;// Normal navigation&lt;/span&gt;&lt;span style="color: #606060;"&gt;  23:&lt;/span&gt;                         {&lt;span style="color: #606060;"&gt;  24:&lt;/span&gt;                             dataGrid.NextCellToEdit();&lt;span style="color: #606060;"&gt;  25:&lt;/span&gt;                         }&lt;span style="color: #606060;"&gt;  26:&lt;/span&gt;                     }&lt;span style="color: #606060;"&gt;  27:&lt;/span&gt;                     &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  28:&lt;/span&gt;              &lt;span style="color: #0000ff;"&gt;default&lt;/span&gt;:&lt;span style="color: #606060;"&gt;  29:&lt;/span&gt;                     &lt;span style="color: #0000ff;"&gt;break&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  30:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101122032369588.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="clip_image001[4]" alt="clip_image001[4]" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101122032386042.png" width="612" border="0" height="526" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp; &lt;/p&gt;&lt;p&gt;&lt;strong&gt;2. 最后一行最后一列，Tab/Enter自动增加新行&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 能捕捉到Tab/Enter后，处理就比较简单了；只需判断时候在最后一行、最后一列，如果是，则调用上一篇文章中的增加新行方法：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (dataGrid.IsLastRow() &amp;amp; dataGrid.IsLastCell())&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     dataGrid.AddEntity&amp;lt;T&amp;gt;();&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3. 增加新行后，自动跳转到新增行的第一列&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 可以尝试在后台代码中，将DataGrid聚焦在新增行的第一列上：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; dataGrid.SelectedItem = entity;&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; dataGrid.CurrentColumn = dataGrid.Columns[0];&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt; dataGrid.NextCellToEdit();&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是在实际使用过程中，可能会遇到如下异常：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101122032427631.png"&gt;&lt;img style="border: 0px none ; display: inline;" title="image" alt="image" src="http://images.cnblogs.com/cnblogs_com/happyhippy/201101/201101122032435830.png" width="458" border="0" height="608" /&gt;&lt;/a&gt; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这是因为，新增加的行，&lt;strong&gt;可能(有时能成功，有时可能失败)&lt;/strong&gt;还没有来得及在UI显示出来，如果在这时设置SelectedItem/CurrentColumn，并调用ScrollIntoView，就会出现上面的异常。一种代替思路，就是用一个计时器来进行处理，如果执行失败，则下次接着执行，直到成功为止：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; ScrolToFirstColumn(&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt; DataGrid dataGrid, &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; entity)&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     DispatcherTimer timer = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; DispatcherTimer();&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;     timer.Interval = TimeSpan.FromTicks(100);&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; sucess = &lt;span style="color: #0000ff;"&gt;false&lt;/span&gt;;&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;     timer.Tick += (o, e) =&amp;gt;&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (!(&lt;span style="color: #0000ff;"&gt;bool&lt;/span&gt;)sucess)&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;         {&lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;             dataGrid.SelectedItem = entity;&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;             dataGrid.CurrentColumn = dataGrid.Columns[0];&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;             dataGrid.NextCellToEdit();&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;             sucess = &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;;&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;             timer.Stop(); &lt;span style="color: #008000;"&gt;//执行完毕后，就不再执行。&lt;/span&gt;&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;         }&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;     };&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;     timer.Start();&lt;span style="color: #606060;"&gt;  18:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;4. 删除行后，自动选中上一行&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp; 默认情况下，行被删除行后，DataGrid没有选中任何行。如果想连续删除、或者用Up/Down/Left/Right跳转的话，就需要每次用鼠标点一下，着实不方便。于是提供了此功能：&lt;/p&gt;&lt;div style="border: 1px solid gray; margin: 20px 0px 10px; padding: 4px; overflow: auto; line-height: 12pt; background-color: #f4f4f4; width: 97.5%; font-family: consolas,'Courier New',courier,monospace; max-height: 200px; font-size: 8pt; cursor: text;"&gt;&lt;div style="border-style: none; padding: 0px; overflow: visible; line-height: 12pt; background-color: #f4f4f4; width: 100%; font-family: consolas,'Courier New',courier,monospace; color: black; font-size: 8pt;"&gt;&lt;span style="color: #606060;"&gt;   1:&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;public&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;static&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt; DeleteCurrentRow(&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt; DataGrid dataGrid)&lt;span style="color: #606060;"&gt;   2:&lt;/span&gt; {&lt;span style="color: #606060;"&gt;   3:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt; entity = dataGrid.SelectedItem;&lt;span style="color: #606060;"&gt;   4:&lt;/span&gt;     IList dataSource = dataGrid.ItemsSource &lt;span style="color: #0000ff;"&gt;as&lt;/span&gt; IList;&lt;span style="color: #606060;"&gt;   5:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (entity == &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt; || dataSource == &lt;span style="color: #0000ff;"&gt;null&lt;/span&gt; || dataSource.Count == 0)&lt;span style="color: #606060;"&gt;   6:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;   7:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;return&lt;/span&gt;;&lt;span style="color: #606060;"&gt;   8:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;   9:&lt;/span&gt;&amp;nbsp; &lt;span style="color: #606060;"&gt;  10:&lt;/span&gt;     &lt;strong&gt;&lt;font color="#008000"&gt;&lt;span style="color: #0000ff;"&gt;int&lt;/span&gt; selectedIndex = dataGrid.SelectedIndex; //删除前，记录当前行的位置&lt;/font&gt;&lt;/strong&gt;&lt;span style="color: #606060;"&gt;  11:&lt;/span&gt;     &lt;span style="color: #0000ff;"&gt;if&lt;/span&gt; (ExtenderInfo[dataGrid].Deleteable)&lt;span style="color: #606060;"&gt;  12:&lt;/span&gt;     {&lt;span style="color: #606060;"&gt;  13:&lt;/span&gt;         dataSource.Remove(entity);&lt;span style="color: #606060;"&gt;  14:&lt;/span&gt;         &lt;strong&gt;&lt;font color="#008000"&gt;dataGrid.SelectedIndex = selectedIndex - 1;//删除后，重置当前行&lt;/font&gt;&lt;/strong&gt;&lt;span style="color: #606060;"&gt;  15:&lt;/span&gt;     }&lt;span style="color: #606060;"&gt;  16:&lt;/span&gt;     dataGrid.BeginEdit();&lt;span style="color: #606060;"&gt;  17:&lt;/span&gt;     ExtenderInfo[dataGrid].RaiseRowChangedEvent(dataGrid,&lt;span style="color: #606060;"&gt;  18:&lt;/span&gt;         &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; EntityChangedEventArgs() { ChangedEntities = &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; List&amp;lt;&lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;&amp;gt; { entity }, Type = &lt;span style="color: #006080;"&gt;"Delete"&lt;/span&gt; });&lt;span style="color: #606060;"&gt;  19:&lt;/span&gt; }&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;5. Up/Down/Left/Right自动编辑&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上一篇文章中，通过在DataGrid的KeyDown事件中，见按键记录到DataGrid.Tag中，然后进入编辑状态时再赋值给单元格中的文本框(TextBox)，来实现即时编辑功能。但在使用的时候，发现如果使用中文输入法、或者列使用的是自定义模板列(不是TextBox)，就会遇到问题：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果是中文输入法+默认的TextBox：DataGrid会将第一个字符赋给TextBox，这样就多出一个字符，每次得多按一次BackSpace键。&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果是使用自定义模板列，而不是TextBox，就会丢失第一次字符按键。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一种替代的思路，就是在捕捉Up/Down/Left/Right后，让DataGrid立即进入编辑状态。如上面第1点所述，还是要在KeyUp事件中进行处理。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;6. 不给代码怎马叫給力&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://files.cnblogs.com/happyhippy/happyhippy.cnblogs.BatchInput.rar" target="_blank"&gt;happyhippy.cnblogs.com.BatchInput.rar&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/happyhippy/aggbug/1934006.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/happyhippy/archive/2011/01/12/1934006.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
