tag:blogger.com,1999:blog-40635454361383192052025年04月11日T03:20:41.393-07:00Tom DuPont .NETI'm a tool, so you don't have to be.Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.comBlogger224125tag:blogger.com,1999:blog-4063545436138319205.post-73936792170322706712017年04月27日T08:01:00.000-07:002017年04月27日T08:38:30.816-07:00Creating an IOC Container for ASP.NET Core<img style="float:right;margin-left:20px;" src="http://sites.google.com/site/tdupont750/microsoft_patterns_practices.gif"> <p>I recently added support for ASP.NET Core to my <a href="https://github.com/tdupont750/tact.net/blob/master/framework/src/Tact.AspNetCore/Practices/Implementation/AspNetCoreContainer.cs" target="_blank">Tact.NET IOC Container</a>, and I thought that I would share some of the interesting requirements I discovered while doing so. I had originally expected the default ASP.NET Core container would follow the same rules as the official Microsoft Unity container, but that turned out not to be the case!</p> <h3 style="margin-top:20px; margin-bottom:15px;">1) Register is first in win.</h3> <p>The Unity container was last in win, and it would completely disregard the original registration. With ASP.NET Core you need to preserve the original registration and then treat all subsequent registrations as addition registrations that will only be resolved when ResolveAll is invoked, similar to keyed registrations in Unity. Which brings us to our next difference...</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> RegisterIsFirstInWinTests</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Unity()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> log = <span class="kwrd">new</span> EmptyLog();</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> container = <span class="kwrd">new</span> TactContainer(log))</pre> <pre> {</pre> <pre class="alt"> container.RegisterSingleton&lt;IExample, ExampleA&gt;();</pre> <pre> container.RegisterSingleton&lt;IExample, ExampleB&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> example = container.Resolve&lt;IExample&gt;();</pre> <pre class="alt"> Assert.IsType&lt;ExampleB&gt;(example);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> AspNetCore()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> log = <span class="kwrd">new</span> EmptyLog();</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> container = <span class="kwrd">new</span> AspNetCoreContainer(log))</pre> <pre> {</pre> <pre class="alt"> container.RegisterSingleton&lt;IExample, ExampleA&gt;();</pre> <pre> container.RegisterSingleton&lt;IExample, ExampleB&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> example = container.Resolve&lt;IExample&gt;();</pre> <pre class="alt"> Assert.IsType&lt;ExampleA&gt;(example);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">interface</span> IExample</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">string</span> Name { <span class="kwrd">get</span>; }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">class</span> ExampleA : IExample</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">string</span> Name =&gt; nameof(ExampleA);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">class</span> ExampleB : IExample</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">string</span> Name =&gt; nameof(ExampleB);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">2) ResolveAll includes single registrations.</h3> <p>Unity has the concept of keyed registrations, where you can register the same type multiple times with distinct keys (strings) and then resolve them individually by key or as an IEnumerable with ResolveAll. In ASP.NET Core there is no concept of keyed registration, and ResolveAll will return all registrations made for a given type. This means that registering a single type will cause ResolveAll to return an IEnumerable with one value.</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> ResolveAllIncludesSingleRegistrations</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Unity()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> log = <span class="kwrd">new</span> EmptyLog();</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> container = <span class="kwrd">new</span> TactContainer(log))</pre> <pre> {</pre> <pre class="alt"> container.RegisterSingleton&lt;IExample, ExampleA&gt;();</pre> <pre> container.RegisterSingleton&lt;IExample, ExampleB&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> examples = container.ResolveAll&lt;IExample&gt;();</pre> <pre class="alt"> Assert.Equal(0, examples.Count());</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> AspNetCore()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> log = <span class="kwrd">new</span> EmptyLog();</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> container = <span class="kwrd">new</span> AspNetCoreContainer(log))</pre> <pre> {</pre> <pre class="alt"> container.RegisterSingleton&lt;IExample, ExampleA&gt;();</pre> <pre> container.RegisterSingleton&lt;IExample, ExampleB&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> examples = container.ResolveAll&lt;IExample&gt;();</pre> <pre class="alt"> Assert.Equal(2, examples.Count());</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">interface</span> IExample</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">string</span> Name { <span class="kwrd">get</span>; }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">class</span> ExampleA : IExample</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">string</span> Name =&gt; nameof(ExampleA);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">class</span> ExampleB : IExample</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">string</span> Name =&gt; nameof(ExampleB);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">3) Generic resolution is required.</h3> <p>This was a neat feature that I had not used before! You can register generic types without any generic parameters, and then the container will fill in the generic parameters based on the resolve request. So, for example, I could register IRepository&lt;&gt; to type DataRepository&lt;&gt;, and then when I call container.Resolve&lt;IRepository&lt;User&gt;&gt;, the IOC container will automatically try to construct a DataRepository&lt;User&gt;</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> TactContainerTests</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> GenericResolution()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> log = <span class="kwrd">new</span> EmptyLog();</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> container = <span class="kwrd">new</span> TactContainer(log))</pre> <pre> {</pre> <pre class="alt"> container.RegisterSingleton(<span class="kwrd">typeof</span>(IRepository&lt;&gt;), <span class="kwrd">typeof</span>(Repository&lt;&gt;));</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> userRepository = container.Resolve&lt;IRepository&lt;User&gt;&gt;();</pre> <pre> Assert.IsType&lt;Repository&lt;User&gt;&gt;(userRepository);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">interface</span> IRepository&lt;T&gt;</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">string</span> ClassName { <span class="kwrd">get</span>; }</pre> <pre class="alt"> <span class="kwrd">string</span> GenericName { <span class="kwrd">get</span>; }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">class</span> Repository&lt;T&gt; : IRepository&lt;T&gt;</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">string</span> ClassName =&gt; nameof(Repository&lt;T&gt;);</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">string</span> GenericName =&gt; <span class="kwrd">typeof</span>(T).Name;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">class</span> User</pre> <pre class="alt"> {</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">4) You have to convert IServiceCollection to your container.</h3> <p>I like how the new .NET separates their service registration from the container itself. In your ASP.NET Core wire up you will work with an IServiceCollection, which will then be converted into a container after you are done filling in your registrations. This means that if you want to create your own container, you will need to map the service collection into your container.</p> <ul> <li><a href="https://github.com/tdupont750/tact.net/blob/master/framework/demo/Demo.Tact.AspNetCore/Startup.cs#L33" target="_blank">ASP.NET Core Registration Example</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/framework/src/Tact.AspNetCore/Practices/Implementation/AspNetCoreContainer.cs#L80" target="_blank">Registration Conversion Example</a></li> </ul> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com15tag:blogger.com,1999:blog-4063545436138319205.post-40196528470576020352017年04月16日T09:31:00.000-07:002017年04月16日T09:35:01.436-07:00How to make a dotnet CLI Tool<p>Good news, everyone! It is remarkably easy to make a new <a href="https://docs.microsoft.com/en-us/dotnet/articles/core/tools/" target="_blank">dotnet CLI (Command Line Interface) tool</a>! I recently created a CLI tool for one of my new projects, <a href="http://www.tomdupont.net/2016/10/introducing-tactnet.html" target="_blank">Tact.NET</a> RPC, and in this post I will be referencing that project as my example.</p> <p><h2>The Basics</h2></p> <p><h3>Step 1) Create your CLI Console App</h3></p> <p>All you have to do is...</p> <ol> <li>Create a normal .NET Core Console App. <ul><li><i>NOTE: Currently, the dotnet CLI only supports <b>netcoreapp1.0</b></i></li></ul></li> <li>Rename the assembly to be prefixed with "dotnet-"</li> <li>dotnet pack the project and put the package in your local NuGet package source</li> </ol> <p>...that is it! It is literally that easy to create your CLI tool!</p> <p style="text-align:center"><img src="https://sites.google.com/site/tdupont750/dotnet_cli_new_project.PNG" style="max-width:800px; min-width:400px;" /></p> <p>Recommendation: use <a href="https://www.nuget.org/packages/Microsoft.Extensions.Configuration.CommandLine/" target="_blank">Microsoft.Extensions.Configuration</a> to parse your command line arguments in a standard way.</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">&lt;</span><span class="html">Project</span> <span class="attr">Sdk</span><span class="kwrd">="Microsoft.NET.Sdk"</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">OutputType</span><span class="kwrd">&gt;</span>Exe<span class="kwrd">&lt;/</span><span class="html">OutputType</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span>netcoreapp1.0<span class="kwrd">&lt;/</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">AssemblyName</span><span class="kwrd">&gt;</span>dotnet-tactrpcgen<span class="kwrd">&lt;/</span><span class="html">AssemblyName</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PackageId</span><span class="kwrd">&gt;</span>Tact.Rpc.Generator<span class="kwrd">&lt;/</span><span class="html">PackageId</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">Version</span><span class="kwrd">&gt;</span>1.0.3<span class="kwrd">&lt;/</span><span class="html">Version</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PackageReference</span> <span class="attr">Include</span><span class="kwrd">="Microsoft.Extensions.Configuration.CommandLine"</span> <span class="attr">Version</span><span class="kwrd">="1.1.1"</span> <span class="kwrd">/&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;/</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre><span class="kwrd">&lt;/</span><span class="html">Project</span><span class="kwrd">&gt;</span></pre> </div> <p><h3>Step 2) Consume your CLI Tool NuGet Package</h3></p> <p>Edit your csproj file and add a DotNetCliToolReference element that references your package.</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">&lt;</span><span class="html">Project</span> <span class="attr">Sdk</span><span class="kwrd">="Microsoft.NET.Sdk"</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span>netstandard1.6<span class="kwrd">&lt;/</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">DotNetCliToolReference</span> <span class="attr">Include</span><span class="kwrd">="Tact.Rpc.Generator"</span> <span class="attr">Version</span><span class="kwrd">="1.0.3"</span> <span class="kwrd">/&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;/</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre><span class="kwrd">&lt;/</span><span class="html">Project</span><span class="kwrd">&gt;</span></pre> </div> <p>Now you are able to invoke your console app via the dotnet command line whenever it is executed in the same path as the csproj.</p> <p style="text-align:center"><img src="https://sites.google.com/site/tdupont750/dotnet_cli_tool.PNG" style="max-width:800px; min-width:400px;" /></p> <p>That's it; it really is that easy!</p> <p><h2>Development Tips</h2></p> <p>Here is a simple way to automate the creation and consumption of your CLI tool NuGet package during development.</p> <a name='more'></a> <img src="https://sites.google.com/site/tdupont750/nuget_config.PNG" style="float:right; margin-left:20px;" /> <p><h3>Create a local package directory</h3></p> <p>I recommend that you create a packages folder at the root of your solution along with a NuGet.config file; this will allow you to automatically build and consume NuGet packages from this location.</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">&lt;</span><span class="html">configuration</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">packageSources</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">add</span> <span class="attr">key</span><span class="kwrd">="LocalPackages"</span> <span class="attr">value</span><span class="kwrd">="packages"</span> <span class="kwrd">/&gt;</span></pre> <pre> <span class="kwrd">&lt;/</span><span class="html">packageSources</span><span class="kwrd">&gt;</span></pre> <pre class="alt"><span class="kwrd">&lt;/</span><span class="html">configuration</span><span class="kwrd">&gt;</span></pre> </div> <p><h3>Pack on post build</h3></p> <p>Go back and update your CLI tool's csproj to include a post build step that packs your project and places the output in your package folder. (There is a csproj GeneratePackageOnBuild setting, however at this time I do not believe that you can customize the output directory, so instead I recommend using a post build step.)</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">&lt;</span><span class="html">Project</span> <span class="attr">Sdk</span><span class="kwrd">="Microsoft.NET.Sdk"</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">OutputType</span><span class="kwrd">&gt;</span>Exe<span class="kwrd">&lt;/</span><span class="html">OutputType</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span>netcoreapp1.0<span class="kwrd">&lt;/</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">AssemblyName</span><span class="kwrd">&gt;</span>dotnet-tactrpcgen<span class="kwrd">&lt;/</span><span class="html">AssemblyName</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PackageId</span><span class="kwrd">&gt;</span>Tact.Rpc.Generator<span class="kwrd">&lt;/</span><span class="html">PackageId</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">RunPostBuildEvent</span><span class="kwrd">&gt;</span>OnBuildSuccess<span class="kwrd">&lt;/</span><span class="html">RunPostBuildEvent</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PostBuildEvent</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> dotnet pack $(SolutionDir)rpc\src\Tact.Rpc.Generator -o $(SolutionDir)packages --no-build</pre> <pre> <span class="kwrd">&lt;/</span><span class="html">PostBuildEvent</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">Version</span><span class="kwrd">&gt;</span>1.0.3<span class="kwrd">&lt;/</span><span class="html">Version</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PackageReference</span> <span class="attr">Include</span><span class="kwrd">="Microsoft.Extensions.Configuration.CommandLine"</span> <span class="attr">Version</span><span class="kwrd">="1.1.1"</span> <span class="kwrd">/&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;/</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre><span class="kwrd">&lt;/</span><span class="html">Project</span><span class="kwrd">&gt;</span></pre> </div> <p><h3>Execute on pre build</h3></p> <p>Obviously this is a completely optional step that should only be used when necessary; in the case of Tact.NET RPC, the active code generation is powered by the CLI tool, and thus it needs to run every build.</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">&lt;</span><span class="html">Project</span> <span class="attr">Sdk</span><span class="kwrd">="Microsoft.NET.Sdk"</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span>netstandard1.6<span class="kwrd">&lt;/</span><span class="html">TargetFramework</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">PreBuildEvent</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> cd $(SolutionDir)rpc\demo\Demo.Rpc</pre> <pre> dotnet tactrpcgen</pre> <pre class="alt"> <span class="kwrd">&lt;/</span><span class="html">PreBuildEvent</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;/</span><span class="html">PropertyGroup</span><span class="kwrd">&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre> <span class="kwrd">&lt;</span><span class="html">DotNetCliToolReference</span> <span class="attr">Include</span><span class="kwrd">="Tact.Rpc.Generator"</span> <span class="attr">Version</span><span class="kwrd">="1.0.3"</span> <span class="kwrd">/&gt;</span></pre> <pre class="alt"> <span class="kwrd">&lt;/</span><span class="html">ItemGroup</span><span class="kwrd">&gt;</span></pre> <pre><span class="kwrd">&lt;/</span><span class="html">Project</span><span class="kwrd">&gt;</span></pre> </div> <p><h3>Increment version to update</h3></p> <p>NOTE: Despite updating the your local package directory each build, the package will actually be cached in your global NuGet cache, and thus updates will not be picked up unless you increment the version number.</p> <p>Enjoy,<br> Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com14tag:blogger.com,1999:blog-4063545436138319205.post-25626269176816235722017年03月30日T08:00:00.000-07:002017年04月02日T17:28:03.169-07:00C# String Interpolation Performance<p>Time for a follow up to my <a href="http://www.tomdupont.net/2014/03/string-concat-vs-string-format.html" target="_blank">String.Concat vs String.Format Performance</a> post from back in 2014!</p> <p>I recently found out that string interpolation is not nearly as efficient as I would have thought. I also suspected that it was just doing a string concatenation, but it is actually doing a string format. This leads to a pretty significant performance degradation; the following test runs one million iterations of each.</p> <table border="1" style="margin: 0 auto; text-align:center"> <tbody><tr> <th width="80px">Number<br>of Args</th> <th width="100px">Interpolation<br>Milliseconds</th> <th width="100px">String.Format<br>Milliseconds</th> <th width="100px">String.Concat<br>Milliseconds</th> <th width="100px">String Add<br>Milliseconds</th> <th width="100px">StringBuilder<br>Milliseconds</th> </tr> <tr> <td>2</td> <td>262</td> <td>260</td> <td>19</td> <td>18</td> <td>34</td> </tr> <tr> <td>3</td> <td>367</td> <td>367</td> <td>25</td> <td>24</td> <td>35</td> </tr> <tr> <td>4</td> <td>500</td> <td>513</td> <td>31</td> <td>32</td> <td>41</td> </tr> <tr> <td>5</td> <td>646</td> <td>635</td> <td>67</td> <td>66</td> <td>44</td> </tr> <tr> <td>6</td> <td>740</td> <td>723</td> <td>79</td> <td>76</td> <td>49</td> </tr> <tr> <td>7</td> <td>802</td> <td>819</td> <td>86</td> <td>85</td> <td>52</td> </tr> <tr> <td>8</td> <td>938</td> <td>936</td> <td>97</td> <td>98</td> <td>58</td> </tr> </tbody></table> <p>So, what's the lesson? Don't use string interpolation in high performance areas (<a href="https://github.com/tdupont750/tact.net/blame/master/src/Tact/Diagnostics/Log.generated.tt#L86" target="_blank">such as your logger</a>)!</p> <p>Enjoy,<br>Tom</p> Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com11tag:blogger.com,1999:blog-4063545436138319205.post-45539836928557938142017年02月28日T08:00:00.000-08:002017年03月01日T19:43:19.367-08:00WebSocket Support for .NET Core<img src="https://sites.google.com/site/tdupont750/websockets.png" style="float:right; margin-left:20px; margin-bottom:20px; width:250px;" /> <p>Full WebSocket support is coming with .NET Standard 2.0, which has now been <a href="https://github.com/dotnet/core/commit/388215e8715a4ccfac9b22ccf7094a7138a6759b" target="_blank">delayed until Q3</a>. In the meantime, there are still a few options to work with...</p> <ul> <li>Third Party <ul> <li><a href="https://github.com/kerryjiang/WebSocket4Net" target="_blank">Client - WebSocket4Net - Beta</a></li> <li><a href="https://github.com/statianzo/Fleck/pull/175" target="_blank">Server - Fleck - Pending PR</a></li> </ul> </li> <li>Microsoft <ul> <li><a href="https://www.nuget.org/packages/System.Net.WebSockets.Client/" target="_blank">Client - System.Net.WebSockets.Client - v4.3.0</a></li> <li><a href="https://www.nuget.org/packages/Microsoft.AspNetCore.WebSockets.Server/" target="_blank">Server - Microsoft.AspNetCore.WebSockets.Server - v0.1.0</a></li> </ul> </li> </ul> <p>If you want to use Microsoft.AspNetCore.WebSockets.Server, I have added a middle ware wrapper that feel a lot more like Fleck:<p> <ul><li><a href="https://github.com/tdupont750/tact.net/tree/master/src/Tact.AspNetCore.WebSockets.Server" target="_blank">Tact.AspNetCore.WebSockets.Server</a></li></ul> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode" style="margin-top:20px"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">void</span> Configure(IApplicationBuilder app)</pre> <pre>{</pre> <pre class="alt"> app.UseWebSockets();</pre> <pre> app.UseWebSocketHandler(<span class="str">"test"</span>, connection =&gt;</pre> <pre class="alt"> {</pre> <pre> <span class="rem">// Register your listeners here</span></pre> <pre class="alt"> connection.OnMessage = m =&gt;</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (m == <span class="str">"hi"</span>)</pre> <pre> connection.SendAsync(<span class="str">"bye"</span>);</pre> <pre class="alt"> };</pre> <pre> });</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com5tag:blogger.com,1999:blog-4063545436138319205.post-90548499000911478902017年02月26日T08:11:00.001-08:002017年02月26日T08:11:35.255-08:00Run .NET Core xUnit tests from ReSharper in VS2015<p>Visual Studio 2017 is literally only a few days away from release; so it might be a little late, but I finally figured out how to run .NET Core xUnit tests from ReSharper in VS2015! Good News: If you can't upgrade to VS2017 right away, then at least you can still run your unit tests!</p> <img style="float:right; max-width:450px;" src="https://sites.google.com/site/tdupont750/xUnit_DotNet_ReSharper.png"/> <p><b>Just make sure that the following is included in your project.json file (with the appropriate runtime):</b></p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt">{</pre> <pre class="alt"> <span class="str">"testRunner"</span>: "xunit",</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="str">"dependencies"</span>: {</pre> <pre> <span class="str">"dotnet-test-xunit"</span>: "2.2.0-preview2-build1029",</pre> <pre class="alt"> <span class="str">"xunit"</span>: "2.2.0"</pre> <pre class="alt"> },</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="str">"frameworks"</span>: {</pre> <pre> <span class="str">"netcoreapp1.0"</span>: {</pre> <pre class="alt"> <span class="str">"imports"</span>: "dnxcore50"</pre> <pre> }</pre> <pre class="alt"> },</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="str">"runtimes"</span>: {</pre> <pre> <span class="str">"win10-x64"</span>: {}</pre> <pre class="alt"> }</pre> <pre>}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com7tag:blogger.com,1999:blog-4063545436138319205.post-7316774271345529332017年01月31日T08:48:00.000-08:002017年02月16日T08:27:16.688-08:00.NET Standard Adoption as of January 2017<p><i>Updated 2/16 to include Elasticsearch</i></p> <p>As should be obviously from my recently blog posts, I have really been enjoying working with .NET Core. Clearly I am not alone, as a significant number of libraries have been porting over to the .NET Standard.</p> <p>Below is a list libraries that have added support for the .NET Standard, meaning that they should be able to run cross platform on both Windows and Linux.</p> <p>While I have not yet had the opportunity to try all of the libraries listed below, I have had great luck with the ones that I have tested, and I am simply ecstatic to see this list growing as fast as it is.</p> <table> <tr> <th>Technology</th> <th>NuGet Package</th> <th>.NET Standard Support</th> </tr> <tr> <td>Autofac</td> <td><a href="https://www.nuget.org/packages/Autofac/" target="_blank">Autofac</a></td> <td>Released for 1.1</td> </tr> <tr> <td>Cassandra</td> <td><a href="https://www.nuget.org/packages/CassandraCSharpDriver/" target="_blank">DataStax C# Driver for Apache Cassandra</a></td> <td>Released for 1.5</td> </tr> <tr> <td>Couchbase</td> <td><a href="https://www.nuget.org/packages/CouchbaseNetClient/" target="_blank">Couchbase SDK 2.0</a></td> <td>Beta for 1.5</td> </tr> <tr> <td>Elasticsearch</td> <td><a href="https://www.nuget.org/packages/Elasticsearch.Net/" target="_blank">Elasticsearch.Net</a></td> <td>Released for 1.3</td> </tr> <tr> <td>Kafka</td> <td><a href="https://www.nuget.org/packages/Confluent.Kafka/0.9.2-preview" target="_blank">Confluent.Kafka</a></td> <td>Preview for 1.3</td> </tr> <tr> <td>log4net</td> <td><a href="https://www.nuget.org/packages/log4net/" target="_blank">Apache log4net</a></td> <td>Released for 1.3</td> </tr> <tr> <td>MongoDB</td> <td><a href="https://www.nuget.org/packages/MongoDB.Driver/" target="_blank">MongoDB.Driver</a></td> <td>Released for 1.4</td> </tr> <tr> <td>NLog</td> <td><a href="https://www.nuget.org/packages/NLog/5.0.0-beta05" target="_blank">NLog</a></td> <td>Beta for 1.3</td> </tr> <tr> <td>RabbitMQ</td> <td><a href="https://www.nuget.org/packages/RabbitMQ.Client/5.0.0-pre2" target="_blank">RabbitMQ.Client</a></td> <td>Released for 1.5</td> </tr> <tr> <td>RavenDB</td> <td><a href="https://www.nuget.org/packages/RavenDB.Client/3.5.3-patch-35187" target="_blank">RavenDB Client</a></td> <td>Released for 1.3</td> </tr> <tr> <td>Redis</td> <td><a href="https://www.nuget.org/packages/StackExchange.Redis/" target="_blank">StackExchange.Redis</a></td> <td>Released for 1.5</td> </tr> <tr> <td>Sqlite</td> <td><a href="https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/" target="_blank">Microsoft.EntityFrameworkCore.Sqlite</a></td> <td>Released for 1.3</td> </tr> <tr> <td>WebSocket Client</td> <td><a href="https://www.nuget.org/packages/WebSocket4Net/" target="_blank">WebSocket4Net</a></td> <td>Released for 1.3</td> </tr> </table> <p>How have these libraries been working out for you? Is there a better option than what I have listed? Please leave a comment and let me know!</p> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com2tag:blogger.com,1999:blog-4063545436138319205.post-48014264750544161282017年01月29日T16:06:00.000-08:002017年01月29日T16:06:37.202-08:00.NET JsonContent for HttpClient<img src="https://sites.google.com/site/tdupont750/json.png" style="float:right; margin-left: 20px; margin-bottom: 20px; width: 125px;"/> <p>.NET already comes with a nice collection of <a href="https://github.com/dotnet/corefx/tree/master/src/System.Net.Http/src/System/Net/Http" target="_blank">HttpContent serializers</a>, but it lacks a JsonContent type. A <a href="http://stackoverflow.com/questions/23585919/send-json-via-post-in-c-sharp-and-receive-the-json-returned" target="_blank">common solution</a> is to just serialize their payload to a JSON string and that insert that into an instance of StringContent. However, this means that you need to remember to set your headers, and it is a little bit inefficient because of how it creates multiple strings and buffers for each payload.</p> <p>I have create a simple implementation of JsonContent that uses <a href="http://www.newtonsoft.com/json" target="_blank">Json.NET</a> and <a href="http://www.tomdupont.net/2016/12/object-pooling-and-memory-streams.html" target="_blank">pooled memory streams</a>. The result is between 2% and 10% faster, and causes ~50% fewer garbage collections.</p> <p>Check out the implementation in <a href="http://www.tomdupont.net/2016/10/introducing-tactnet.html" target="_blank">Tact.NET</a>:</p> <ul> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Json/Net/Http/JsonContent.cs" target="_blank">Source - JsonContent.cs</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/tests/Tact.Tests/Net/Http/JsonContentTest.cs" target="_blank">Tests - JsonContentTests.cs</a></li> </ul> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com11tag:blogger.com,1999:blog-4063545436138319205.post-7601325523435976482016年12月31日T14:17:00.000-08:002017年01月01日T14:20:11.476-08:002016 Retrospective<img style="float:right; margin-left:15px; margin-bottom:15px; width:100px;" src="https://sites.google.com/site/tdupont750/DotNetFoundation.png"> <h3>.NET</h3> <p>It has been a great year for .NET development! A Visual Studio Community is fully featured, .NET Core has arrived, and everything is open source. Regarding .NET Core, I am really enjoying working with it, and I simply cannot wait to get deeper into the Linux world.</p> <h3>Blog</h3> <p>I finally had to downgrade from three posts per month to only two posts per month. Unfortunately writing quality blog posts tasks time, and that was not something that I had in great abundance this year. Fortunately, I do think that the majority of posts this year were very high quality, especially when you look at the most recent ones. I have been working a lot with performance optimization, and have really been enjoying profiling and digging deep into code to see exactly what it is doing and why.</p> <img style="float:right; margin-left:15px; margin-bottom:15px; width:100px;" src="https://sites.google.com/site/tdupont750/Tact_NET_M.png"> <h3>Tact.NET</h3> <p>I am very happy to have launched <a href="https://github.com/tdupont750/tact.net" target="_blank">Tact.NET</a> this year! I have always really enjoyed creating frameworks, so rather than continue to write one off posts on this blog I decide to put all of my extracurricular worth together under one repository. I am really enjoying making Tact, and I have every intention of continuing to grow it.</p> <h3>QQ Cast</h3> <p>Wow, the <a href="http://www.qq-cast.com/" target="_blank">QQ Cast</a> is back! We took a hiatus for the second half of 2015, but in 2016 we recorded 43 podcasts. Next week is actually going to be our 100th episode, be sure to check it out! <p>Happy new year,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com7tag:blogger.com,1999:blog-4063545436138319205.post-50753506391474118132016年12月30日T13:58:00.000-08:002017年01月04日T18:05:17.051-08:00Object Pooling and Memory Streams<p>The theme of this year, which I will talk about in my 2016 retrospective, has been optimization. It's been a fun journey, and I have really enjoyed getting down and dirty with profiling garbage collection, using spin waits, and aggressive inlining.</p> <p><b>I want to end this year on a fun note: object pooling.</b></p> <img style="float:right; margin-left: 15px; margin-bottom: 15px;" src="https://sites.google.com/site/tdupont750/ObjectPool.png" /> <p>A great use case for this would be making HTTP requests with serialized objects. When you serialize an object, and then place it in a <a href="https://github.com/dotnet/corefx/blob/master/src/System.Net.Http/src/System/Net/Http/HttpContent.cs" target="_blank">HttpContent</a> object, you are probable creating several buffers (byte arrays) each time. For example, if you are using Newtonsoft to serialize an object and then adding that to a string content object for your request, then you are probably using more memory than you need. But that is getting ahead of ourselves...</p> <p><i>Come back next week for a blog post about efficient JSON Content serialization!</i></p> <p>For now, let's focus on building an <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact/Collections/ObjectPool.cs" target="_blank">object pool</a>. Really all that we need is a preallocated array to store unused objects in, and then a super efficient thread safe data structure to pool (get and set) those objects.</p> <p><b>How does pooling memory streams help us?</b></p> When you create a <a href="https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Extensions/src/System/IO/MemoryStream.cs" target="_blank">MemoryStream</a>, it creates a <a href="https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Extensions/src/System/IO/MemoryStream.cs#L52" target="_blank">byte array</a>. As that byte array grows, the <a href="https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Extensions/src/System/IO/MemoryStream.cs#L281" target="_blank">memory stream resizes</a> it by allocating a new larger array and then copying your bytes into it. This is inefficient not only because it creates new objects and throws the old ones away, but also because it has to do the leg work of copying the content each time it resizes.</p> <p><b>How can we reuse memory streams? Just set the length to zero!</b></p> <p>Internally this will just <a href="https://github.com/dotnet/corefx/blob/master/src/System.Runtime.Extensions/src/System/IO/MemoryStream.cs#L604" target="_blank">set an index and empty the array</a>, but the internal data structures will be preserved for future use. Thus, by putting memory streams into an object pool, we can drastically increase our efficiency.</p> <p>Here is a demo of using the <a href="http://www.tomdupont.net/2016/10/introducing-tactnet.html" target="_blank">Tact.NET</a> <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact/Collections/ObjectPool.cs" target="_blank">ObjectPool</a> to pool MemoryStreams...</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt">[Fact]</pre> <pre><span class="kwrd">public</span> <span class="kwrd">void</span> MemoryStreamPoolDemo()</pre> <pre class="alt">{</pre> <pre> <span class="kwrd">using</span> (<span class="kwrd">var</span> pool = <span class="kwrd">new</span> ObjectPool&lt;MemoryStream&gt;(100, () =&gt; <span class="kwrd">new</span> MemoryStream()))</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> memoryStream1 = pool.Acquire();</pre> <pre class="alt">&nbsp;</pre> <pre> memoryStream1.SetLength(0);</pre> <pre class="alt"> Assert.Equal(0, memoryStream1.Capacity);</pre> <pre>&nbsp;</pre> <pre class="alt"> memoryStream1.Write(<span class="kwrd">new</span> <span class="kwrd">byte</span>[] {1, 0, 1, 0, 1}, 0, 5);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> array1 = memoryStream1.ToArray();</pre> <pre> Assert.Equal(5, array1.Length);</pre> <pre class="alt"> Assert.Equal(1, array1.First());</pre> <pre> Assert.Equal(1, array1.First());</pre> <pre class="alt">&nbsp;</pre> <pre> pool.Release(memoryStream1);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> memoryStream2 = pool.Acquire();</pre> <pre class="alt"> Assert.Same(memoryStream1, memoryStream2);</pre> <pre>&nbsp;</pre> <pre class="alt"> memoryStream2.SetLength(0);</pre> <pre> Assert.Equal(256, memoryStream2.Capacity);</pre> <pre class="alt">&nbsp;</pre> <pre> memoryStream2.Write(<span class="kwrd">new</span> <span class="kwrd">byte</span>[] { 0, 1, 0 }, 0, 3);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> array2 = memoryStream2.ToArray();</pre> <pre class="alt"> Assert.Equal(3, array2.Length);</pre> <pre> Assert.Equal(0, array2.First());</pre> <pre class="alt"> Assert.Equal(0, array2.First());</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-43415877837911084272016年11月27日T12:27:00.000-08:002016年11月27日T12:34:25.691-08:00The Performance Cost of Boxing in .NET<p>I recently had to do some performance optimizations against a sorted dictionary that yielded some interesting results...</p> <p>Background: I am used to using Tuples a lot, simply because they are easy to use and normally quite efficient. Please remember that Tuples were changed from structs to classes back in .NET 4.0.</p> <img style="float:right; margin-left:20px; margin-bottom:20px;" src="https://sites.google.com/site/tdupont750/Struct_Optimization.PNG"> <h3 style="margin-top:20px; margin-bottom:15px;">Problem: A struct decreased performance!</h3> <p>I had a SortedDictionary that was using a Tuple as a key, so I thought "hey, I'll just change that tuple to a struct and reduce the memory usage." ...bad news, that made performance WORSE!</p> <p>Why would using a struct make performance worse? It's actually quite simple and obvious when you think about it: it was causing comparisons to repeatedly box the primitive data structure, thus allocating more memory on the heap and triggering more garbage collections.</p> <h3 style="margin-top:20px; margin-bottom:15px;">Solution: Use a struct with an IComparer.</h3> <p>I then created a custom struct and used that; it was must faster, but it was still causing boxing because of the non-generic IComparable interface. So finally I added a generic IComparer and passed that into my dictionary constructor; my dictionary then ran fast and efficient, causing a total of ZERO garbage collections!</p> <p>See for yourself:</p> <p style="text-align:center;"><img src="https://sites.google.com/site/tdupont750/Cost_of_Boxing.PNG"></p> <h3 style="margin-top:20px; margin-bottom:15px;">The Moral of the Story</h3> <p>Try to be aware of what default implementations are doing, and always remember that boxing to object can add up fast. Also, pay attention to the Visual Studio Diagnostics Tools window; it can be very informative!</p> <p>Here is how many lines of code it took to achieve a <b>5x performance increase</b>:</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">private</span> <span class="kwrd">struct</span> MyStruct</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> MyStruct(<span class="kwrd">int</span> i, <span class="kwrd">string</span> s) { I = i; S = s; }</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> I;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> S;</pre> <pre>}</pre> <pre class="alt">&nbsp;</pre> <pre><span class="kwrd">private</span> <span class="kwrd">class</span> MyStructComparer : IComparer&lt;MyStruct&gt;</pre> <pre class="alt">{</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">int</span> Compare(MyStruct x, MyStruct y)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> c = x.I.CompareTo(y.I);</pre> <pre class="alt"> <span class="kwrd">return</span> c != 0 ? c : StringComparer.Ordinal.Compare(x.S, y.S);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">Test Program</h3> <p>I have written some detailed comments in the Main function about what each test is doing and how it will affect performance. Let's take a look...</p> <a name='more'></a> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> Program</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Main(<span class="kwrd">string</span>[] args)</pre> <pre> {</pre> <pre class="alt"> <span class="rem">// This is going to use the class implementation</span></pre> <pre> <span class="rem">// of Tuple that was changed in .NET 4.0. It is</span></pre> <pre class="alt"> <span class="rem">// be inefficient for three reasons:</span></pre> <pre> <span class="rem">// 1) It is allocating a new class on the heap </span></pre> <pre class="alt"> <span class="rem">// for each and every key.</span></pre> <pre> <span class="rem">// 2) It is using the default object comparer</span></pre> <pre class="alt"> <span class="rem">// to check each item, causing inefficient </span></pre> <pre> <span class="rem">// comparisons and...</span></pre> <pre class="alt"> <span class="rem">// 3) Causing boxing of primitive values.</span></pre> <pre> ClassTupleAsKey();</pre> <pre class="alt"> Reset();</pre> <pre> <span class="rem">// DURATION: 8,479 miliseconds</span></pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">// This is going to use the struct implementation</span></pre> <pre class="alt"> <span class="rem">// of Tuple that was available from .NET 2.0</span></pre> <pre> <span class="rem">// until 4.0. It is even less efficiente than the</span></pre> <pre class="alt"> <span class="rem">// class tuple for both reasons listed above, and </span></pre> <pre> <span class="rem">// one new reason:</span></pre> <pre class="alt"> <span class="rem">// 4) The StructTuple itself will be boxed when</span></pre> <pre> <span class="rem">// being passed between comparers, causing</span></pre> <pre class="alt"> <span class="rem">// even more garbage collections to occur!</span></pre> <pre> StructTupleAsKey();</pre> <pre class="alt"> Reset();</pre> <pre> <span class="rem">// DURATION: 8,880 miliseconds</span></pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">// Now we move to a custom struct implementation</span></pre> <pre class="alt"> <span class="rem">// for the dictionary key, but because we are using</span></pre> <pre> <span class="rem">// a SortedDictionary we still need to implement</span></pre> <pre class="alt"> <span class="rem">// IComparable, which is not generic, and thus will</span></pre> <pre> <span class="rem">// be slow for one reason:</span></pre> <pre class="alt"> <span class="rem">// 1) The struct itself will be boxed when being</span></pre> <pre> <span class="rem">// passed between comparers.</span></pre> <pre class="alt"> MyStructAsKey();</pre> <pre> Reset();</pre> <pre class="alt"> <span class="rem">// DURATION: 2,896 miliseconds</span></pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// This is pretty much as fast as we can get; no</span></pre> <pre> <span class="rem">// heap allocations, no boxing, fast comparisons.</span></pre> <pre class="alt"> MyStructAsKeyWithCompare();</pre> <pre> <span class="rem">// DURATION: 1,442 miliseconds</span></pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> Reset()</pre> <pre> {</pre> <pre class="alt"> GC.Collect();</pre> <pre> Thread.Sleep(500);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> ClassTupleAsKey()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> dictionary = <span class="kwrd">new</span> SortedDictionary&lt;ClassTuple&lt;<span class="kwrd">int</span>, <span class="kwrd">string</span>&gt;, <span class="kwrd">int</span>&gt;();</pre> <pre> <span class="kwrd">var</span> sw = Stopwatch.StartNew();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; 1000000; i++)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> key = <span class="kwrd">new</span> ClassTuple&lt;<span class="kwrd">int</span>, <span class="kwrd">string</span>&gt;(i % 1000, i.ToString());</pre> <pre class="alt"> dictionary.Add(key, i);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> sw.Stop();</pre> <pre class="alt"> Console.WriteLine(<span class="str">"ClassTupleAsKey: "</span> + sw.ElapsedMilliseconds);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> StructTupleAsKey()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> dictionary = <span class="kwrd">new</span> SortedDictionary&lt;StructTuple&lt;<span class="kwrd">int</span>, <span class="kwrd">string</span>&gt;, <span class="kwrd">int</span>&gt;();</pre> <pre class="alt"> <span class="kwrd">var</span> sw = Stopwatch.StartNew();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; 1000000; i++)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> key = <span class="kwrd">new</span> StructTuple&lt;<span class="kwrd">int</span>, <span class="kwrd">string</span>&gt;(i % 1000, i.ToString());</pre> <pre> dictionary.Add(key, i);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> sw.Stop();</pre> <pre> Console.WriteLine(<span class="str">"StructTupleAsKey: "</span> + sw.ElapsedMilliseconds);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> MyStructAsKey()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> dictionary = <span class="kwrd">new</span> SortedDictionary&lt;MyStructA, <span class="kwrd">int</span>&gt;();</pre> <pre> <span class="kwrd">var</span> sw = Stopwatch.StartNew();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; 1000000; i++)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> key = <span class="kwrd">new</span> MyStructA(i % 1000, i.ToString());</pre> <pre class="alt"> dictionary.Add(key, i);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> sw.Stop();</pre> <pre class="alt"> Console.WriteLine(<span class="str">"StructAsKey: "</span> + sw.ElapsedMilliseconds);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">void</span> MyStructAsKeyWithCompare()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> dictionary = <span class="kwrd">new</span> SortedDictionary&lt;MyStructB, <span class="kwrd">int</span>&gt;(MyStructBComparer.Instance);</pre> <pre class="alt"> <span class="kwrd">var</span> sw = Stopwatch.StartNew();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; 1000000; i++)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> key = <span class="kwrd">new</span> MyStructB(i % 1000, i.ToString());</pre> <pre> dictionary.Add(key, i);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> sw.Stop();</pre> <pre> Console.WriteLine(<span class="str">"StructAsKeyWithCompare: "</span> + sw.ElapsedMilliseconds);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">class</span> ClassTuple&lt;T1, T2&gt; : IStructuralEquatable, IStructuralComparable, IComparable</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> T1 _item1;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> T2 _item2;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> T1 Item1 =&gt; _item1;</pre> <pre class="alt"> <span class="kwrd">public</span> T2 Item2 =&gt; _item2;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> ClassTuple(T1 item1, T2 item2)</pre> <pre> {</pre> <pre class="alt"> _item1 = item1;</pre> <pre> _item2 = item2;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Equals(<span class="kwrd">object</span> obj)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> ((IStructuralEquatable) <span class="kwrd">this</span>).Equals(obj, EqualityComparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">bool</span> IStructuralEquatable.Equals(<span class="kwrd">object</span> other, IEqualityComparer comparer)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> objTuple = other <span class="kwrd">as</span> ClassTuple&lt;T1, T2&gt;;</pre> <pre class="alt"> <span class="kwrd">if</span> (objTuple == <span class="kwrd">null</span>) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <pre> <span class="kwrd">return</span> comparer.Equals(_item1, objTuple._item1) &amp;&amp; comparer.Equals(_item2, objTuple._item2);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">int</span> IComparable.CompareTo(<span class="kwrd">object</span> obj)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> ((IStructuralComparable) <span class="kwrd">this</span>).CompareTo(obj, Comparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">int</span> IStructuralComparable.CompareTo(<span class="kwrd">object</span> other, IComparer comparer)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (other == <span class="kwrd">null</span>) <span class="kwrd">return</span> 1;</pre> <pre class="alt"> <span class="kwrd">var</span> objTuple = other <span class="kwrd">as</span> ClassTuple&lt;T1, T2&gt;;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (objTuple == <span class="kwrd">null</span>)</pre> <pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(<span class="str">"ArgumentException_TupleIncorrectType"</span>, nameof(other));</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> c = comparer.Compare(_item1, objTuple._item1);</pre> <pre class="alt"> <span class="kwrd">return</span> c != 0 ? c : comparer.Compare(_item2, objTuple._item2);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> GetHashCode()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((IStructuralEquatable) <span class="kwrd">this</span>).GetHashCode(EqualityComparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">int</span> IStructuralEquatable.GetHashCode(IEqualityComparer comparer)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> CombineHashCodes(comparer.GetHashCode(_item1), comparer.GetHashCode(_item2));</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">int</span> CombineHashCodes(<span class="kwrd">int</span> h1, <span class="kwrd">int</span> h2)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((h1 &lt;&lt; 5) + h1) ^ h2;</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">struct</span> StructTuple&lt;T1, T2&gt; : IStructuralEquatable, IStructuralComparable, IComparable</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> T1 _item1;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> T2 _item2;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> T1 Item1 =&gt; _item1;</pre> <pre> <span class="kwrd">public</span> T2 Item2 =&gt; _item2;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> StructTuple(T1 item1, T2 item2)</pre> <pre class="alt"> {</pre> <pre> _item1 = item1;</pre> <pre class="alt"> _item2 = item2;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">bool</span> Equals(<span class="kwrd">object</span> obj)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((IStructuralEquatable) <span class="kwrd">this</span>).Equals(obj, EqualityComparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">bool</span> IStructuralEquatable.Equals(<span class="kwrd">object</span> other, IEqualityComparer comparer)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (!(other <span class="kwrd">is</span> StructTuple&lt;T1, T2&gt;)) <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <pre> <span class="kwrd">var</span> objTuple = (StructTuple&lt;T1, T2&gt;) other;</pre> <pre class="alt"> <span class="kwrd">return</span> comparer.Equals(_item1, objTuple._item1) &amp;&amp; comparer.Equals(_item2, objTuple._item2);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">int</span> IComparable.CompareTo(<span class="kwrd">object</span> obj)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((IStructuralComparable) <span class="kwrd">this</span>).CompareTo(obj, Comparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">int</span> IStructuralComparable.CompareTo(<span class="kwrd">object</span> other, IComparer comparer)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> objTuple = (StructTuple&lt;T1, T2&gt;) other;</pre> <pre> <span class="kwrd">var</span> c = comparer.Compare(_item1, objTuple._item1);</pre> <pre class="alt"> <span class="kwrd">return</span> c != 0 ? c : comparer.Compare(_item2, objTuple._item2);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">override</span> <span class="kwrd">int</span> GetHashCode()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((IStructuralEquatable) <span class="kwrd">this</span>).GetHashCode(EqualityComparer&lt;<span class="kwrd">object</span>&gt;.Default);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">int</span> IStructuralEquatable.GetHashCode(IEqualityComparer comparer)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> CombineHashCodes(comparer.GetHashCode(_item1), comparer.GetHashCode(_item2));</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">int</span> CombineHashCodes(<span class="kwrd">int</span> h1, <span class="kwrd">int</span> h2)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> ((h1 &lt;&lt; 5) + h1) ^ h2;</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">struct</span> MyStructA : IComparable</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> MyStructA(<span class="kwrd">int</span> i, <span class="kwrd">string</span> s)</pre> <pre class="alt"> {</pre> <pre> I = i;</pre> <pre class="alt"> S = s;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> I;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> S;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">int</span> CompareTo(<span class="kwrd">object</span> obj)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> y = (MyStructA) obj;</pre> <pre> <span class="kwrd">var</span> c = I.CompareTo(y.I);</pre> <pre class="alt"> <span class="kwrd">return</span> c != 0 ? c : StringComparer.Ordinal.Compare(S, y.S);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">struct</span> MyStructB</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> MyStructB(<span class="kwrd">int</span> i, <span class="kwrd">string</span> s)</pre> <pre> {</pre> <pre class="alt"> I = i;</pre> <pre> S = s;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> I;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">string</span> S;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">class</span> MyStructBComparer : IComparer&lt;MyStructB&gt;</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> MyStructBComparer Instance = <span class="kwrd">new</span> MyStructBComparer();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">int</span> Compare(MyStructB x, MyStructB y)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> c = x.I.CompareTo(y.I);</pre> <pre> <span class="kwrd">return</span> c != 0 ? c : StringComparer.Ordinal.Compare(x.S, y.S);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com86tag:blogger.com,1999:blog-4063545436138319205.post-36127282311958693942016年11月26日T18:40:00.001-08:002016年11月26日T18:40:34.783-08:0010x faster than Delegate.DynamicInvoke<p>This is a follow up to my previous blog posts, <a href="http://www.tomdupont.net/2016/08/optimizing-dynamic-method-invokes-in-net.html" target="_blank">Optimizing Dynamic Method Invokes in .NET</a>, and <a href="http://www.tomdupont.net/2016/08/dynamically-invoke-methods-quickly-with.html" target="_blank">Dynamically Invoke Methods Quickly, with InvokeHelpers.EfficientInvoke</a>. Basically, I have re-implemented this for <a href="http://www.tomdupont.net/2016/10/introducing-tactnet.html" target="_blank">Tact.NET</a> in a way that makes it smaller, faster, and compatible with the .NET Standard.</p> <p style="text-align:center"><img style="float:right; width:450px;" src="https://sites.google.com/site/tdupont750/Efficient_Invoke_Performance.PNG"></p> <p>So, how much faster is this new way of doing things? EfficientInvoker.Invoke is over <b>10x faster</b> than Delegate.DynamicInvoke, and <b>10x faster</b> than MethodInfo.Invoke. <p>Check out the source on GitHub:</p> <ul> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact/Reflection/EfficientInvoker.cs" target="_blank">EfficientInvoker.cs</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/tests/Tact.Tests/Reflection/EfficientInvokerTests.cs" target="_blank">EfficientInvokerTests.cs</a></li> </ul> <h3 style="margin-top:20px; margin-bottom:15px;">Simple Explanation</h3> <p>Here is an example of a method and a class that we might want to invoke dynamically...</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> Tester</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">bool</span> AreEqual(<span class="kwrd">int</span> a, <span class="kwrd">int</span> b)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> a == b;</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>...and then here is the code that the EfficientInvoker will generate at runtime to call that method:</p> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">object</span> GeneratedFunction(<span class="kwrd">object</span> target, <span class="kwrd">object</span>[] args)</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">return</span> (<span class="kwrd">object</span>)((Tester)target).AreEqual((<span class="kwrd">int</span>)args[0], (<span class="kwrd">int</span>)args[1]);</pre> <pre>}</pre> </div> <p>See, it's simple!</p> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">Sample Tests</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> EfficientInvokerTest</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> DelegateInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> del = (Delegate) <span class="kwrd">new</span> Func&lt;<span class="kwrd">int</span>, <span class="kwrd">int</span>, <span class="kwrd">bool</span>&gt;((a, b) =&gt; a == b);</pre> <pre> </pre> <pre class="alt"> <span class="rem">// Standard slow way</span></pre> <pre> <span class="kwrd">var</span> result1 = (<span class="kwrd">bool</span>)del.DynamicInvoke(1, 2);</pre> <pre class="alt"> Assert.False(result1);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// New 10x faster way</span></pre> <pre> <span class="kwrd">var</span> invoker = del.GetInvoker();</pre> <pre class="alt"> <span class="kwrd">var</span> result2 = (<span class="kwrd">bool</span>)invoker.Invoke(del, 1, 2);</pre> <pre> Assert.False(result2);</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> MethodInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> tester = <span class="kwrd">new</span> Tester();</pre> <pre> <span class="kwrd">var</span> type = tester.GetType();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">// Standard slow way</span></pre> <pre class="alt"> <span class="kwrd">var</span> methodInfo = type.GetTypeInfo().GetMethod(<span class="str">"AreEqual"</span>);</pre> <pre> <span class="kwrd">var</span> result1 = (<span class="kwrd">bool</span>) methodInfo.Invoke(tester, <span class="kwrd">new</span> <span class="kwrd">object</span>[] {1, 2});</pre> <pre class="alt"> Assert.False(result1);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// New 10x faster way</span></pre> <pre> <span class="kwrd">var</span> invoker = type.GetMethodInvoker(<span class="str">"AreEqual"</span>);</pre> <pre class="alt"> <span class="kwrd">var</span> result2 = (<span class="kwrd">bool</span>) invoker.Invoke(tester, 1, 2);</pre> <pre> Assert.False(result2);</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">class</span> Tester</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">bool</span> AreEqual(<span class="kwrd">int</span> a, <span class="kwrd">int</span> b)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> a == b;</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com11tag:blogger.com,1999:blog-4063545436138319205.post-13785915452062402882016年10月31日T18:00:00.000-07:002016年10月31日T22:39:34.848-07:00IOC Container for Tact.NET<img style="float:right;margin-left:20px;margin-bottom:20px;" src="http://sites.google.com/site/tdupont750/microsoft_patterns_practices.gif"> <p><a href="http://docs.autofac.org/en/latest/integration/aspnetcore.html" target="_blank">Autofac</a> now supports .NET Core, but other IOC frameworks such as <a href="http://www.ninject.org/" target="_blank">Ninject</a> and <a href="https://msdn.microsoft.com/library/ff647202.aspx" taret="_blank">Unity</a> have yet to port over. While I was looking into IOC frameworks for .NET Core, I had a bad idea...I, for fun, wrote my own <a href="https://github.com/tdupont750/tact.net/tree/master/src/Tact.Core/Practices" target="_blank">Container</a> in <a href="https://github.com/tdupont750/tact.net/" target="_blank">Tact.NET</a>!</p> <p>So, why did I do this? Honestly, it was just a fun academic exercise! I do think that this is a pretty good container, and I intend to use it in some of my personal projects. Would I recommend that YOU use this? Probably not yet, but I would invite you take a look and offer feedback!</p> <h3>Container Design</h3> <p>I have broken the container into two interfaces: <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/IContainer.cs" target="_blank">IContainer</a> for registration, and <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/IResolver.cs" target="_blank">IResolver</a> for consumption. There is a abstract class, <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Base/ContainerBase.cs" target="_blank">ContainerBase</a>, that can be inherited to easily create a container that matches other frameworks; for example, I intend to create a <a href="https://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection" target="_blank">IDependencyResolver for ASP.NET</a>.</p> <p>You may notice that the IContainer does not have any <a href="http://www.tomdupont.net/2013/12/undestanding-unity-lifetime-managers.html" target="_blank">lifetime management</a> methods, that is because ALL of them are implemented as <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Extensions/ContainerExtensions.cs" target="_blank">extension methods</a>...</p> <h3>Registrations</h3> <p>To create a new lifetime manager you have to implement the <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/IRegistration.cs" target="_blank">IRegistration</a> interface. There are already implementations for quite a few:</p> <ul> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/Implementation/InstanceRegistration.cs" target="_blank">InstanceRegistration</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/Implementation/PerResolveRegistration.cs" target="_blank">PerResolveRegistration</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/Implementation/PerScopeRegistration.cs" target="_blank">PerScopeRegistration</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/Implementation/SingletonRegistration.cs" target="_blank">SingletonRegistration</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/Registration/Implementation/TransientRegistration.cs" target="_blank">TransientRegistration</a></li> </ul> <h3>Resolution Handlers</h3> <p>The last part of the container is the <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/IResolutionHandler.cs" target="_blank">IResolutionHandler</a> interface. These resolution handlers are used when an exact registration match is not found during dependency resolution. For example, the <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/EnumerableResolutionHandler.cs" target="_blank">EnumerableResolutionHandler</a> will use ResolveAll to get a collection of whatever type is being requested. Another very different example, the <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/ThrowOnFailResolutionHandler.cs" target="_blank">ThrowOnFailResolutionHandler</a> will cause an exception to be thrown when no match can be found.</p> <ul> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/EnumerableResolutionHandler.cs" target="_blank">EnumerableResolutionHandler</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/FuncResolutionHandler.cs" target="_blank">FuncResolutionHandler</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/LazyResolutionHandler.cs" target="_blank">LazyResolutionHandler</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/ThrowOnFailResolutionHandler.cs" target="_blank">ThrowOnFailResolutionHandler</a></li> <li><a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Practices/ResolutionHandlers/Implementation/UnregisteredResolutionHandler.cs" target="_blank">UnregisteredResolutionHandler</a></li> </ul> <p>I think that is a pretty good start, but I am hoping that this will continue to grow with time.</p> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-16448518591176552012016年10月31日T08:30:00.000-07:002016年10月31日T08:30:31.854-07:00Introducing Tact.NET<img style="float:right; margin-left:20px; margin-bottom:20px;" src="https://sites.google.com/site/tdupont750/DotNetFoundation.png" /> <p>I have decided to create a project to consolidate all of the utilities that I have authored over the years on this blog...</p> <p style="text-align: center;"><a href="https://github.com/tdupont750/tact.net" target="_blank"><b>Tact.NET</b> - A tactful collection of utilities for .NET development.</a></p> <p>I believe that this will give me the opportunity to update old code more often, as well as develop new utilities faster. For example, the there is already an <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Diagnostics/ILog.cs" target="_blank">ILog</a> interface that <a href="https://github.com/tdupont750/tact.net/blob/master/src/Tact.Core/Diagnostics/Log.generated.tt#L62" target="_blank">optimizes</a> (thank you, Jeremy) the <a href="http://www.tomdupont.net/2016/05/common-logging-extensions-with-caller.html" target="_blank">Caller Information Extensions</a> I wrote about about back in May.</p> <p>Everything that I add to this project will be build for the .NET Standard Library, and should support .NET Core on all platforms. Expect to see NuGet packages soon, and hopefully many more blog posts to come.</p> <p>Enjoy,<br>Tom</p> Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com2tag:blogger.com,1999:blog-4063545436138319205.post-19334758268150894422016年09月30日T09:06:00.000-07:002016年10月02日T09:14:09.417-07:00Host HTTP and WebSockets on the Same Port in ASP.NET <p>How do you support WebSocket connections on the same port as your HTTP server in .NET? It turns out that this is not that hard, and you even have several choices to do so...</p> <p style="text-align:center;"><img src="https://sites.google.com/site/tdupont750/html5-websockets.jpg"></p> <h3>Option 1: TCP Proxy</h3> <p>You could use a TCP proxy port to divide traffic between the two targets. For example, all traffic would come in to port 80, but then HTTP traffic would be routed internally to 8001 and WS traffic to 8002. This is useful because it would allow you to use multiple technologies (in the example on their site, both NancyFX and Fleck) to host your web servers.</p> <ul><li><a href="https://github.com/lifeemotions/websocketproxy" target="_blank">github.com/lifeemotions/websocketproxy</a></li></ul> <h3>Option 2: SignalR</h3> <p>SignalR is great because of all the backwards compatibility that it supports for both the server and client. However, it is not the most lightweight framework. If you choose to use it, then it will absolutely support both HTTP and WS. <ul><li><a href="https://www.asp.net/signalr" target="_blank">asp.net/signalr</a></li></ul> <h3>Option 3: Owin.WebSocket <i>*My Favorite*</i></h3> <p><a href="http://owin.org/spec/extensions/owin-WebSocket-Extension-v0.4.0.htm" target="_blank">Owin supports WebSockets</a>, and that is exactly what SignalR uses to host its WS connections. The awesome <a href="https://github.com/bryceg" target="_blank">Mr. Bryce Godfrey</a> extracted the Owin code from SignalR into a much smaller library called Owin.WebSocket. <p>The ONLY thing that I did not like about this implementation was that is uses inheritance to define endpoints, whereas I much prefer the ability to use delegates and lambdas. Because of this, I created <a href="https://github.com/tdupont750/Owin.WebSocket/tree/master/src/Owin.WebSocket.Fleck" target="_blank">Owin.WebSocket.Fleck</a>, which allows you to use the Fleck API to map your WebSockets to an Owin context. A <a href="https://github.com/bryceg/Owin.WebSocket/pull/20" target="_blank">pull request</a> is open to merge this into the main repository.</p> <ul><li><a href="https://github.com/bryceg/Owin.WebSocket" target="_blank">github.com/bryceg/Owin.WebSocket</a></li></ul> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com2tag:blogger.com,1999:blog-4063545436138319205.post-45824952644376420762016年09月25日T11:05:00.001-07:002016年09月25日T22:04:40.376-07:00.NET Asynchronous Parallel Batch Processor<p>Last year, <a href="http://www.tomdupont.net/2015/06/net-asynchronous-batch-processor.html" target="_blank">I wrote about</a> how to handle dynamically sized batches of data in an asynchronous manner. That original implementation used an abstract base class, and only supported a single background processing thread. I recently updated that implementation to support lambdas rather than requiring inheritance, and support a dynamic number of background threads.</p> <p><i>...basically, this is a ConcurrentQueue that supports taking a lambda and thread count to asynchronously process enqueued items.</i></p> <h3 style="margin-top:20px; margin-bottom:15px;">Unit Tests</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> ParallelProcessorTests</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> async Task NoDisposeTimeout()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> results = <span class="kwrd">new</span> ConcurrentQueue&lt;<span class="kwrd">int</span>&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">using</span> (<span class="kwrd">var</span> processor = <span class="kwrd">new</span> ParallelProcessor&lt;<span class="kwrd">int</span>&gt;(2, async (i, token) =&gt;</pre> <pre class="alt"> {</pre> <pre> await Task.Delay(200, token).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> results.Enqueue(i);</pre> <pre> }, disposeTimeoutMs: 0))</pre> <pre class="alt"> {</pre> <pre> processor.Enqueue(1);</pre> <pre class="alt"> processor.Enqueue(2);</pre> <pre> processor.Enqueue(3);</pre> <pre class="alt"> processor.Enqueue(4);</pre> <pre> processor.Enqueue(5);</pre> <pre class="alt">&nbsp;</pre> <pre> await Task.Delay(300).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> Assert.Equal(2, results.Count);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> MaxParallelizationLimit()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">const</span> <span class="kwrd">int</span> parallelism = 3;</pre> <pre> <span class="kwrd">var</span> results = <span class="kwrd">new</span> ConcurrentQueue&lt;Tuple&lt;<span class="kwrd">int</span>, <span class="kwrd">int</span>&gt;&gt;();</pre> <pre class="alt"> <span class="kwrd">var</span> active = 0;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">using</span> (<span class="kwrd">var</span> processor = <span class="kwrd">new</span> ParallelProcessor&lt;<span class="kwrd">int</span>&gt;(parallelism, async (i, token) =&gt;</pre> <pre> {</pre> <pre class="alt"> Interlocked.Increment(<span class="kwrd">ref</span> active);</pre> <pre> await Task.Delay(200, token).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> <span class="kwrd">var</span> currentActive = Interlocked.Decrement(<span class="kwrd">ref</span> active) + 1;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> tuple = Tuple.Create(currentActive, i);</pre> <pre> results.Enqueue(tuple);</pre> <pre class="alt"> }))</pre> <pre> {</pre> <pre class="alt"> processor.Enqueue(1);</pre> <pre> processor.Enqueue(2);</pre> <pre class="alt"> processor.Enqueue(3);</pre> <pre> processor.Enqueue(4);</pre> <pre class="alt"> processor.Enqueue(5);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> Assert.Equal(5, results.Count);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> maxParallelism = results.Max(t =&gt; t.Item1);</pre> <pre class="alt"> Assert.Equal(parallelism, maxParallelism);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> BatchProcessor()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> results = <span class="kwrd">new</span> List&lt;Tuple&lt;<span class="kwrd">long</span>, List&lt;<span class="kwrd">int</span>&gt;&gt;&gt;();</pre> <pre> <span class="kwrd">var</span> sw = Stopwatch.StartNew();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">using</span> (<span class="kwrd">var</span> processor = <span class="kwrd">new</span> BatchParallelProcessor&lt;<span class="kwrd">int</span>&gt;(1, 2, async (ints, token) =&gt;</pre> <pre class="alt"> {</pre> <pre> await Task.Delay(100, token).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> <span class="kwrd">var</span> tuple = Tuple.Create(sw.ElapsedMilliseconds, ints);</pre> <pre> results.Add(tuple);</pre> <pre class="alt"> }))</pre> <pre> {</pre> <pre class="alt"> processor.Enqueue(1);</pre> <pre> processor.Enqueue(2);</pre> <pre class="alt"> processor.Enqueue(3);</pre> <pre> processor.Enqueue(4);</pre> <pre class="alt"> processor.Enqueue(5);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> Assert.Equal(3, results.Count);</pre> <pre class="alt">&nbsp;</pre> <pre> Assert.Equal(2, results[0].Item2.Count);</pre> <pre class="alt"> Assert.Equal(1, results[0].Item2[0]);</pre> <pre> Assert.Equal(2, results[0].Item2[1]);</pre> <pre class="alt">&nbsp;</pre> <pre> Assert.True(results[0].Item1 &lt; results[1].Item1);</pre> <pre class="alt"> Assert.Equal(2, results[1].Item2.Count);</pre> <pre> Assert.Equal(3, results[1].Item2[0]);</pre> <pre class="alt"> Assert.Equal(4, results[1].Item2[1]);</pre> <pre>&nbsp;</pre> <pre class="alt"> Assert.True(results[1].Item1 &lt; results[2].Item1);</pre> <pre> Assert.Equal(1, results[2].Item2.Count);</pre> <pre class="alt"> Assert.Equal(5, results[2].Item2[0]);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">ParallelProcessor</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">sealed</span> <span class="kwrd">class</span> ParallelProcessor&lt;T&gt; : ProcessorBase&lt;T&gt;</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Func&lt;T, CancellationToken, Task&gt; _processHandler;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Action&lt;T, Exception&gt; _exceptionHandler;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">public</span> ParallelProcessor(</pre> <pre class="alt"> <span class="kwrd">int</span> maxParallelization, </pre> <pre> Func&lt;T, CancellationToken, Task&gt; processHandler, </pre> <pre class="alt"> Action&lt;T, Exception&gt; exceptionHandler = <span class="kwrd">null</span>,</pre> <pre> <span class="kwrd">int</span> disposeTimeoutMs = 30000,</pre> <pre class="alt"> <span class="kwrd">int</span>? maxQueueSize = <span class="kwrd">null</span>)</pre> <pre> : <span class="kwrd">base</span>(maxParallelization, disposeTimeoutMs, maxQueueSize)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (maxParallelization &lt; 1)</pre> <pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(</pre> <pre> $<span class="str">"{nameof(maxParallelization)} is required"</span>,</pre> <pre class="alt"> nameof(maxParallelization));</pre> <pre>&nbsp;</pre> <pre class="alt"> _processHandler = processHandler;</pre> <pre> _exceptionHandler = exceptionHandler;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">protected</span> <span class="kwrd">override</span> async Task ProcessLoopAsync()</pre> <pre> {</pre> <pre class="alt"> T item;</pre> <pre> while (!CancelSource.IsCancellationRequested &amp;&amp; Queue.TryDequeue(<span class="kwrd">out</span> item))</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">try</span></pre> <pre class="alt"> {</pre> <pre> await _processHandler(item, CancelSource.Token).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> }</pre> <pre> <span class="kwrd">catch</span> (TaskCanceledException) when (CancelSource.IsCancellationRequested)</pre> <pre class="alt"> {</pre> <pre> <span class="rem">// Cancellation was requested, ignore and exit.</span></pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre> }</pre> <pre class="alt"> <span class="kwrd">catch</span> (Exception ex)</pre> <pre> {</pre> <pre class="alt"> _exceptionHandler?.Invoke(item, ex);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">BatchParallelProcessor</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">sealed</span> <span class="kwrd">class</span> BatchParallelProcessor&lt;T&gt; : ProcessorBase&lt;T&gt;</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> _batchSize;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Func&lt;List&lt;T&gt;, CancellationToken, Task&gt; _processHandler;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Action&lt;List&lt;T&gt;, Exception&gt; _exceptionHandler;</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">public</span> BatchParallelProcessor(</pre> <pre> <span class="kwrd">int</span> maxParallelization,</pre> <pre class="alt"> <span class="kwrd">int</span> batchSize,</pre> <pre> Func&lt;List&lt;T&gt;, CancellationToken, Task&gt; processHandler,</pre> <pre class="alt"> Action&lt;List&lt;T&gt;, Exception&gt; exceptionHandler = <span class="kwrd">null</span>,</pre> <pre> <span class="kwrd">int</span> disposeTimeoutMs = 30000,</pre> <pre class="alt"> <span class="kwrd">int</span>? maxQueueSize = <span class="kwrd">null</span>)</pre> <pre> : <span class="kwrd">base</span>(maxParallelization, disposeTimeoutMs, maxQueueSize)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (batchSize &lt; 1)</pre> <pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(</pre> <pre> $<span class="str">"{nameof(batchSize)} is required"</span>,</pre> <pre class="alt"> nameof(batchSize));</pre> <pre>&nbsp;</pre> <pre class="alt"> _batchSize = batchSize;</pre> <pre> _processHandler = processHandler;</pre> <pre class="alt"> _exceptionHandler = exceptionHandler;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">protected</span> <span class="kwrd">override</span> async Task ProcessLoopAsync()</pre> <pre class="alt"> {</pre> <pre> while (!CancelSource.IsCancellationRequested)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> count = Math.Min(_batchSize, Queue.Count + 1);</pre> <pre class="alt"> <span class="kwrd">var</span> list = <span class="kwrd">new</span> List&lt;T&gt;(count);</pre> <pre>&nbsp;</pre> <pre class="alt"> T item;</pre> <pre> while (list.Count &lt; _batchSize &amp;&amp; Queue.TryDequeue(<span class="kwrd">out</span> item))</pre> <pre class="alt"> list.Add(item);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (list.Count == 0)</pre> <pre> <span class="kwrd">return</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">try</span></pre> <pre class="alt"> {</pre> <pre> await _processHandler(list, CancelSource.Token).ConfigureAwait(<span class="kwrd">false</span>);</pre> <pre class="alt"> }</pre> <pre> <span class="kwrd">catch</span> (TaskCanceledException) when (CancelSource.IsCancellationRequested)</pre> <pre class="alt"> {</pre> <pre> <span class="rem">// Cancellation was requested, ignore and exit.</span></pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre> }</pre> <pre class="alt"> <span class="kwrd">catch</span> (Exception ex)</pre> <pre> {</pre> <pre class="alt"> _exceptionHandler?.Invoke(list, ex);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">ProcessorBase</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">class</span> ProcessorBase&lt;T&gt; : IDisposable</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">protected</span> <span class="kwrd">readonly</span> ConcurrentQueue&lt;T&gt; Queue = <span class="kwrd">new</span> ConcurrentQueue&lt;T&gt;();</pre> <pre> <span class="kwrd">protected</span> <span class="kwrd">readonly</span> CancellationTokenSource CancelSource = <span class="kwrd">new</span> CancellationTokenSource();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">object</span> _lock = <span class="kwrd">new</span> <span class="kwrd">object</span>();</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Task[] _tasks;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> _disposeTimeoutMs;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span>? _maxQueueSize;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">bool</span> _isDisposed;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">protected</span> ProcessorBase(<span class="kwrd">int</span> maxParallelization, <span class="kwrd">int</span> disposeTimeoutMs, <span class="kwrd">int</span>? maxQueueSize)</pre> <pre> {</pre> <pre class="alt"> _tasks = <span class="kwrd">new</span> Task[maxParallelization];</pre> <pre> _disposeTimeoutMs = disposeTimeoutMs;</pre> <pre class="alt"> _maxQueueSize = maxQueueSize;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Dispose()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (_isDisposed)</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> _isDisposed = <span class="kwrd">true</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (_disposeTimeoutMs &gt; 0)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> tasks = _tasks.Where(t =&gt; t != <span class="kwrd">null</span>).ToArray();</pre> <pre> <span class="kwrd">var</span> allTask = Task.WhenAll(tasks);</pre> <pre class="alt"> <span class="kwrd">var</span> delayTask = Task.Delay(_disposeTimeoutMs);</pre> <pre> Task.WaitAny(allTask, delayTask);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> CancelSource.Cancel();</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Enqueue(T item)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (_isDisposed)</pre> <pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Cancellation has been requested"</span>);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (_maxQueueSize.HasValue &amp;&amp; Queue.Count &gt;= _maxQueueSize)</pre> <pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> InvalidOperationException(<span class="str">"Queue is full"</span>);</pre> <pre class="alt">&nbsp;</pre> <pre> Queue.Enqueue(item);</pre> <pre class="alt"> TryStartProcessLoop();</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">bool</span> TryEnqueue(T item)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (_isDisposed)</pre> <pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (_maxQueueSize.HasValue &amp;&amp; Queue.Count &gt;= _maxQueueSize)</pre> <pre> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> Queue.Enqueue(item);</pre> <pre class="alt"> TryStartProcessLoop();</pre> <pre> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">protected</span> <span class="kwrd">abstract</span> Task ProcessLoopAsync();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">void</span> TryStartProcessLoop()</pre> <pre> {</pre> <pre class="alt"> <span class="rem">// Another thread is in the lock, bail out.</span></pre> <pre> <span class="kwrd">if</span> (!Monitor.TryEnter(_lock))</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// Create task outside of lock to ensure that we attach the</span></pre> <pre> <span class="rem">// continue without while another thread can be in the block.</span></pre> <pre class="alt"> Task task;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">try</span></pre> <pre> {</pre> <pre class="alt"> <span class="rem">// If cancellation has been requested, do not start.</span></pre> <pre> <span class="kwrd">if</span> (CancelSource.IsCancellationRequested)</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// If the queue is empty, do not start.</span></pre> <pre> <span class="kwrd">if</span> (Queue.Count == 0)</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> freeIndex = 0;</pre> <pre> <span class="kwrd">var</span> activeCount = 0;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">// Find last free index</span></pre> <pre class="alt"> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; _tasks.Length; i++)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (_tasks[i] == <span class="kwrd">null</span> || _tasks[i].IsCompleted)</pre> <pre> freeIndex = i;</pre> <pre class="alt"> <span class="kwrd">else</span></pre> <pre> activeCount++;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// All tasks are active, do not start.</span></pre> <pre> <span class="kwrd">if</span> (activeCount == _tasks.Length)</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// Only one in queue, at least one thread is active, do not start additional thread.</span></pre> <pre> <span class="kwrd">if</span> (activeCount &gt; 0 &amp;&amp; Queue.Count &lt;= 1)</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// Start a new task to process the queue.</span></pre> <pre> task = _tasks[freeIndex] = Task.Run(ProcessLoopAsync, CancelSource.Token);</pre> <pre class="alt"> }</pre> <pre> <span class="kwrd">finally</span> </pre> <pre class="alt"> {</pre> <pre> Monitor.Exit(_lock);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// When the process queue task completes, check to see if</span></pre> <pre> <span class="rem">// the queue has been populated again and needs to restart.</span></pre> <pre class="alt"> task.ContinueWith(t =&gt; TryStartProcessLoop());</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p> Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com2tag:blogger.com,1999:blog-4063545436138319205.post-61128423094613452542016年08月21日T12:28:00.000-07:002016年08月21日T12:28:57.160-07:00Dynamically Invoke Methods Quickly, with InvokeHelpers.EfficientInvoke<img src="http://devstickers.com/assets/img/pro/2p4i.png" style="float:right; width: 200px; margin-left: 20px; margin-bottom: 20px;"> <p>In my previous blog post, I talked about <a href="http://www.tomdupont.net/2016/08/optimizing-dynamic-method-invokes-in-net.html">Optimizing Dynamic Method Invokes in .NET</a>. In this post, we will use that information to create a static helper method that is <i>twice as fast as MethodInfo.Invoke</i>. <p>Basically, we create and cache a delegate in a concurrent dictionary, and then cast both it and it's arguments to dynamics and invoke them directly. The concurrent dictionary introduces overhead, but it still more than twice as fast as calling MethodInfo.Invoke. Please note that this method is highly optimized to reduce the use of hash code look ups, property getters, closure allocations, and if checks.</p> <p>let's take a look at the code...</p> <h3 style="margin-top:15px; margin-bottom:10px;">InvokeHelpers.EfficientInvoke</h3> <style>#efficientinvoke > pre { white-space: pre-wrap; }</style> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode" id="efficientinvoke"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> InvokeHelpers</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">const</span> <span class="kwrd">string</span> TooManyArgsMessage = <span class="str">"Invokes for more than 10 args are not yet implemented"</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> Type VoidType = <span class="kwrd">typeof</span>(<span class="kwrd">void</span>);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> ConcurrentDictionary&lt;Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt;, DelegatePair&gt; DelegateMap </pre> <pre> = <span class="kwrd">new</span> ConcurrentDictionary&lt;Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt;, DelegatePair&gt;();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">object</span> EfficientInvoke(<span class="kwrd">object</span> obj, <span class="kwrd">string</span> methodName, <span class="kwrd">params</span> <span class="kwrd">object</span>[] args)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> key = Tuple.Create(methodName, obj);</pre> <pre class="alt"> <span class="kwrd">var</span> delPair = DelegateMap.GetOrAdd(key, CreateDelegate);</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">if</span> (delPair.HasReturnValue)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">switch</span> (delPair.ArgumentCount)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">case</span> 0: <span class="kwrd">return</span> delPair.Delegate();</pre> <pre> <span class="kwrd">case</span> 1: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0]);</pre> <pre class="alt"> <span class="kwrd">case</span> 2: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1]);</pre> <pre> <span class="kwrd">case</span> 3: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2]);</pre> <pre class="alt"> <span class="kwrd">case</span> 4: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3]);</pre> <pre> <span class="kwrd">case</span> 5: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4]);</pre> <pre class="alt"> <span class="kwrd">case</span> 6: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5]);</pre> <pre> <span class="kwrd">case</span> 7: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6]);</pre> <pre class="alt"> <span class="kwrd">case</span> 8: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7]);</pre> <pre> <span class="kwrd">case</span> 9: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7], (dynamic)args[8]);</pre> <pre class="alt"> <span class="kwrd">case</span> 10: <span class="kwrd">return</span> delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7], (dynamic)args[8], (dynamic)args[9]);</pre> <pre> <span class="kwrd">default</span>: <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException(TooManyArgsMessage);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">switch</span> (delPair.ArgumentCount)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">case</span> 0: delPair.Delegate(); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">case</span> 1: delPair.Delegate((dynamic)args[0]); <span class="kwrd">break</span>;</pre> <pre> <span class="kwrd">case</span> 2: delPair.Delegate((dynamic)args[0], (dynamic)args[1]); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">case</span> 3: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2]); <span class="kwrd">break</span>;</pre> <pre> <span class="kwrd">case</span> 4: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3]); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">case</span> 5: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4]); <span class="kwrd">break</span>;</pre> <pre> <span class="kwrd">case</span> 6: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5]); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">case</span> 7: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6]); <span class="kwrd">break</span>;</pre> <pre> <span class="kwrd">case</span> 8: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7]); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">case</span> 9: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7], (dynamic)args[8]); <span class="kwrd">break</span>;</pre> <pre> <span class="kwrd">case</span> 10: delPair.Delegate((dynamic)args[0], (dynamic)args[1], (dynamic)args[2], (dynamic)args[3], (dynamic)args[4], (dynamic)args[5], (dynamic)args[6], (dynamic)args[7], (dynamic)args[8], (dynamic)args[9]); <span class="kwrd">break</span>;</pre> <pre class="alt"> <span class="kwrd">default</span>: <span class="kwrd">throw</span> <span class="kwrd">new</span> NotImplementedException(TooManyArgsMessage);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> DelegatePair CreateDelegate(Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt; key)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> method = key.Item2</pre> <pre> .GetType()</pre> <pre class="alt"> .GetMethod(key.Item1);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> argTypes = method</pre> <pre> .GetParameters()</pre> <pre class="alt"> .Select(p =&gt; p.ParameterType)</pre> <pre> .Concat(<span class="kwrd">new</span>[] { method.ReturnType })</pre> <pre class="alt"> .ToArray();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> newDelType = Expression.GetDelegateType(argTypes);</pre> <pre> <span class="kwrd">var</span> newDel = Delegate.CreateDelegate(newDelType, key.Item2, method);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">return</span> <span class="kwrd">new</span> DelegatePair(newDel, argTypes.Length - 1, method.ReturnType != VoidType);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">class</span> DelegatePair</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> DelegatePair(dynamic del, <span class="kwrd">int</span> argumentCount, <span class="kwrd">bool</span> hasReturnValue)</pre> <pre> {</pre> <pre class="alt"> Delegate = del;</pre> <pre> ArgumentCount = argumentCount;</pre> <pre class="alt"> HasReturnValue = hasReturnValue;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">readonly</span> dynamic Delegate;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">int</span> ArgumentCount;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">readonly</span> <span class="kwrd">bool</span> HasReturnValue;</pre> <pre class="alt"> }</pre> <pre>}</pre> </div> <p>Now let's take a look at some performance tests...</p> <a name='more'></a> <h3 style="margin-top:15px; margin-bottom:10px;">Unit Tests</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> InvokeHelpersTests</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">const</span> <span class="kwrd">int</span> Iterations = 1000000;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> TestClass Obj = <span class="kwrd">new</span> TestClass();</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> <span class="kwrd">object</span>[] Args = { 1, <span class="kwrd">true</span> };</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> ITestOutputHelper _output;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> InvokeHelpersTests(ITestOutputHelper output)</pre> <pre class="alt"> {</pre> <pre> _output = output;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">/// &lt;summary&gt;</span></pre> <pre> <span class="rem">/// This is not realistic, because our million invokes do not simulate</span></pre> <pre class="alt"> <span class="rem">/// the cost of looking up the MethodInfo. However, for the sake of</span></pre> <pre> <span class="rem">/// argument, this takes 930,136 ticks for one million iterations.</span></pre> <pre class="alt"> <span class="rem">/// &lt;/summary&gt;</span></pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> MethodInfoInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> methodInfo = Obj.GetType().GetMethod(<span class="str">"TestMethod"</span>);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> methodInfo.Invoke(Obj, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> methodInfo.Invoke(Obj, Args);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">/// &lt;summary&gt;</span></pre> <pre class="alt"> <span class="rem">/// This use case is more realistic, where we look up the MethodInfo</span></pre> <pre> <span class="rem">/// each iteration. This takes 1,370,052 ticks for one million iterations.</span></pre> <pre class="alt"> <span class="rem">/// &lt;/summary&gt;</span></pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> GetMethodInfoInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> Obj.GetType().GetMethod(<span class="str">"TestMethod"</span>).Invoke(Obj, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> Obj.GetType().GetMethod(<span class="str">"TestMethod"</span>).Invoke(Obj, Args);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">/// &lt;summary&gt;</span></pre> <pre class="alt"> <span class="rem">/// This is an apples to apples comparision of using a ConcurrentDictionary to cache</span></pre> <pre> <span class="rem">/// the MethodInfo lookup. This takes 1,300,751 ticks for one million iterations.</span></pre> <pre class="alt"> <span class="rem">/// &lt;/summary&gt;</span></pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> MappedMethodInfoInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> MappedMethodInvokeHelpers.EfficientInvoke(Obj, <span class="str">"TestMethod"</span>, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> MappedMethodInvokeHelpers.EfficientInvoke(Obj, <span class="str">"TestMethod"</span>, Args);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">/// &lt;summary&gt;</span></pre> <pre class="alt"> <span class="rem">/// Here is our custom optimized solution, which beats ALL of the previous iterations by</span></pre> <pre> <span class="rem">/// more than a factor of 2! This only takes 467,158 ticks for one million iterations.</span></pre> <pre class="alt"> <span class="rem">/// &lt;/summary&gt;</span></pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> EfficientInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> InvokeHelpers.EfficientInvoke(Obj, <span class="str">"TestMethod"</span>, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> InvokeHelpers.EfficientInvoke(Obj, <span class="str">"TestMethod"</span>, Args);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">class</span> TestClass</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">int</span> TestMethod(<span class="kwrd">int</span> i, <span class="kwrd">bool</span> b)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> i + (b ? 1 : 2);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> MappedMethodInvokeHelpers</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> ConcurrentDictionary&lt;Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt;, MethodInfo&gt; DelegateMap</pre> <pre class="alt"> = <span class="kwrd">new</span> ConcurrentDictionary&lt;Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt;, MethodInfo&gt;();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">object</span> EfficientInvoke(<span class="kwrd">object</span> obj, <span class="kwrd">string</span> methodName, <span class="kwrd">params</span> <span class="kwrd">object</span>[] args)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> key = Tuple.Create(methodName, obj);</pre> <pre> <span class="kwrd">var</span> methodInfo = DelegateMap.GetOrAdd(key, GetMethodInfo);</pre> <pre class="alt"> <span class="kwrd">return</span> methodInfo.Invoke(obj, args);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">static</span> MethodInfo GetMethodInfo(Tuple&lt;<span class="kwrd">string</span>, <span class="kwrd">object</span>&gt; key)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">return</span> key.Item2.GetType().GetMethod(key.Item1);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com1tag:blogger.com,1999:blog-4063545436138319205.post-73396317743558326452016年08月21日T12:05:00.002-07:002016年08月21日T12:15:10.120-07:00Optimizing Dynamic Method Invokes in .NET<p>I recently had a lot of fun helping to optimize some RPC code that was using reflection to dynamically invoke methods in a C# application. Below are a list of implementations that we experimented with, and their performance.</p> <ol> <li>Directly Invoking the Method</li> <li>Using MethodInfo.Invoke</li> <li>Using Delegate.DynamicInvoke</li> <li>Casting to a Func</li> <li>Casting a Delegate to Dynamic</li> </ol> <p><b>Spoilers:</b> Here are the results. (The tests for this can be see below.)</p> <table border="1" cellpadding="5" style="margin: auto; border-spacing: 0px; text-align: center; width: 600px;"> <tr> <th>Name</th> <th>First Call (Ticks)</th> <th>Next Million Calls</th> <th>Invoke Comparison</th> </tr> <tr> <td>Invoke</td> <td>1</td> <td>39795</td> <td>-</td> </tr> <tr> <td>MethodInfo.Invoke</td> <td>12</td> <td>967523</td> <td>x24</td> </tr> <tr> <td>Delegate.DynamicInvoke</td> <td>32</td> <td>1580086</td> <td>x39</td> </tr> <tr> <td>Func Invoke</td> <td>731</td> <td>41331</td> <td>x1</td> </tr> <tr> <td>Dynamic Cast</td> <td>1126896</td> <td>85495</td> <td>x2</td> </tr> </table> <p><b>Conclusion:</b> Invoking a method or delegate directly is always fastest, but when you need to execute code dynamically, then (after the first invoke) the dynamic invoke of a delegate is significantly faster than using reflection.</p> <p style="text-align:center"><img src="https://sites.google.com/site/tdupont750/MethodInvokePerformance.png" /></p> <p>Let's take a look at the test code...</p> <a name='more'></a> <h3 style="margin-top:15px; margin-bottom:10px;">Unit Tests</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> InvokePerformance</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">const</span> <span class="kwrd">int</span> Iterations = 1000000;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> <span class="kwrd">object</span>[] Args = {1, <span class="kwrd">true</span>};</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> TestClass Obj = <span class="kwrd">new</span> TestClass();</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> Type Type = <span class="kwrd">typeof</span>(TestClass);</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> MethodInfo Method = Type.GetMethod(<span class="str">"TestMethod"</span>);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> ITestOutputHelper _output;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> InvokePerformance(ITestOutputHelper output)</pre> <pre class="alt"> {</pre> <pre> _output = output;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Invoke()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> arg0 = (<span class="kwrd">int</span>) Args[0];</pre> <pre class="alt"> <span class="kwrd">var</span> arg1 = (<span class="kwrd">bool</span>) Args[1];</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> Method.Invoke(Obj, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> Obj.TestMethod(arg0, arg1);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> MethodInfoInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> Method.Invoke(Obj, Args);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> Method.Invoke(Obj, Args);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> DelegateDynamicInvoke()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> delType = Expression.GetDelegateType(<span class="kwrd">typeof</span>(<span class="kwrd">int</span>), <span class="kwrd">typeof</span>(<span class="kwrd">bool</span>), <span class="kwrd">typeof</span>(<span class="kwrd">int</span>));</pre> <pre> <span class="kwrd">var</span> del = Delegate.CreateDelegate(delType, Obj, Method);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre class="alt"> del.DynamicInvoke(Args);</pre> <pre> sw0.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre class="alt"> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre> {</pre> <pre class="alt"> del.DynamicInvoke(Args);</pre> <pre> }</pre> <pre class="alt"> sw1.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> FuncInvoke()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> delType = Expression.GetDelegateType(<span class="kwrd">typeof</span>(<span class="kwrd">int</span>), <span class="kwrd">typeof</span>(<span class="kwrd">bool</span>), <span class="kwrd">typeof</span>(<span class="kwrd">int</span>));</pre> <pre class="alt"> <span class="kwrd">var</span> del = (Func&lt;<span class="kwrd">int</span>, <span class="kwrd">bool</span>, <span class="kwrd">int</span>&gt;) Delegate.CreateDelegate(delType, Obj, Method);</pre> <pre> <span class="kwrd">var</span> arg0 = (<span class="kwrd">int</span>) Args[0];</pre> <pre class="alt"> <span class="kwrd">var</span> arg1 = (<span class="kwrd">bool</span>) Args[1];</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre> del(arg0, arg1);</pre> <pre class="alt"> sw0.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre class="alt"> {</pre> <pre> del(arg0, arg1);</pre> <pre class="alt"> }</pre> <pre> sw1.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre class="alt"> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> DynamicCast()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> delType = Expression.GetDelegateType(<span class="kwrd">typeof</span>(<span class="kwrd">int</span>), <span class="kwrd">typeof</span>(<span class="kwrd">bool</span>), <span class="kwrd">typeof</span>(<span class="kwrd">int</span>));</pre> <pre> dynamic del = Delegate.CreateDelegate(delType, Obj, Method);</pre> <pre class="alt"> dynamic arg0 = Args[0];</pre> <pre> dynamic arg1 = Args[1];</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> sw0 = Stopwatch.StartNew();</pre> <pre class="alt"> del(arg0, arg1);</pre> <pre> sw0.Stop();</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">var</span> sw1 = Stopwatch.StartNew();</pre> <pre class="alt"> <span class="kwrd">for</span> (<span class="kwrd">var</span> i = 0; i &lt; Iterations; i++)</pre> <pre> {</pre> <pre class="alt"> del(arg0, arg1);</pre> <pre> }</pre> <pre class="alt"> sw1.Stop();</pre> <pre>&nbsp;</pre> <pre class="alt"> _output.WriteLine(sw0.ElapsedTicks.ToString());</pre> <pre> _output.WriteLine(sw1.ElapsedTicks.ToString());</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">class</span> TestClass</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">int</span> TestMethod(<span class="kwrd">int</span> i, <span class="kwrd">bool</span> b)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> i + (b ? 1 : 2);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-12680918632455527752016年07月31日T12:00:00.000-07:002016年08月02日T23:40:06.753-07:00Can I port my application to .NET Core?<p>I am very excited about .NET Core, and I am looking forward to porting my projects to it. Generally porting your own code is a straight forward task, but updating all of your dependencies can be quite daunting. Good news: the guys over at Octopus have built an awesome little site that helps you determine the state of your dependencies!</p> <h3 style="text-align:center;"><a href="https://icanhasdot.net/" target="_blank">I Can Has .NET Core</a></h3> <p style="text-align:center;"><img src="https://sites.google.com/site/tdupont750/icanhasdotnet.png" /></p> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com4tag:blogger.com,1999:blog-4063545436138319205.post-59598740052624774102016年07月24日T12:34:00.000-07:002016年07月24日T12:34:41.582-07:00Make your NuGet Server use NLog<img style="float:right; width:120px; margin-left:20px;" src="https://sites.google.com/site/tdupont750/nuget.png"> <p>The latest version of <a href="https://www.nuget.org/packages/NuGet.Server/" target="_blank">NuGet.Server</a> is fast, stable, and super simple to setup. As for most .NET tools, <a href="http://www.hanselman.com/blog/HowToHostYourOwnNuGetServerAndPackageFeed.aspx" target="_blank">Scott Hanselman</a> already create a great write up about how to use it.</p> <p><b>However, I was very disappointed at how unintuitive it was to get wire up a custom logger!</b></p> <p>You need to take several steps to make the official NuGet server write to something like NLog:</p> <ol> <li>Create a log wrapper.</li> <li>Implement NuGet.Server.Logging.ILogger.</li> <li>Implement NuGet.ILogger...</li> <li>...which also makes you implement NuGet.IFileConflictResolver!</li> <li>Implement your own NuGet.Server.IServiceResolver</li> <li>When instantiating ServerPackageRepository...</li> <li>...pass in the ILogger...</li> <li>...AND set the Logger property!</li> </ol> <p>Still confused? <a href="https://github.com/tdupont750/NuGet.Server.NLog" target="_blank">Pull the code here</a>, or take a look below!</p> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">NLogLogger</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> NLogLogger : Logging.ILogger, ILogger</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> global::NLog.ILogger _logger;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> NLogLogger(global::NLog.ILogger logger)</pre> <pre> {</pre> <pre class="alt"> _logger = logger;</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> Log(LogLevel level, <span class="kwrd">string</span> message, <span class="kwrd">params</span> <span class="kwrd">object</span>[] args)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">switch</span> (level)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">case</span> LogLevel.Verbose:</pre> <pre class="alt"> _logger.Trace(message, args);</pre> <pre> <span class="kwrd">break</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">case</span> LogLevel.Info:</pre> <pre class="alt"> _logger.Info(message, args);</pre> <pre> <span class="kwrd">break</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">case</span> LogLevel.Warning:</pre> <pre class="alt"> _logger.Warn(message, args);</pre> <pre> <span class="kwrd">break</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">case</span> LogLevel.Error:</pre> <pre class="alt"> _logger.Error(message, args);</pre> <pre> <span class="kwrd">break</span>;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">default</span>:</pre> <pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentOutOfRangeException(nameof(level), level, <span class="kwrd">null</span>);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> FileConflictResolution ResolveFileConflict(<span class="kwrd">string</span> message)</pre> <pre> {</pre> <pre class="alt"> _logger.Warn(message);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="rem">// This is what the default NullLogger returns.</span></pre> <pre> <span class="kwrd">return</span> FileConflictResolution.Ignore;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> Log(MessageLevel level, <span class="kwrd">string</span> message, <span class="kwrd">params</span> <span class="kwrd">object</span>[] args)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">switch</span> (level)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">case</span> MessageLevel.Info:</pre> <pre> _logger.Info(message, args);</pre> <pre class="alt"> <span class="kwrd">break</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">case</span> MessageLevel.Warning:</pre> <pre> _logger.Warn(message, args);</pre> <pre class="alt"> <span class="kwrd">break</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">case</span> MessageLevel.Debug:</pre> <pre> _logger.Debug(message, args);</pre> <pre class="alt"> <span class="kwrd">break</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">case</span> MessageLevel.Error:</pre> <pre> _logger.Error(message, args);</pre> <pre class="alt"> <span class="kwrd">break</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">default</span>:</pre> <pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentOutOfRangeException(nameof(level), level, <span class="kwrd">null</span>);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">NLogServiceResolver</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> NLogServiceResolver : IServiceResolver</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IHashProvider _hashProvider;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IServerPackageRepository _packageRepository;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IPackageAuthenticationService _packageAuthenticationService;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> IPackageService _packageService;</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">readonly</span> ILogger _logger;</pre> <pre> <span class="kwrd">private</span> <span class="kwrd">readonly</span> Logging.ILogger _loggingLogger;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> NLogServiceResolver()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> logger = LogManager.GetCurrentClassLogger();</pre> <pre class="alt"> logger.Info(<span class="str">"Initializing NLogServiceResolver"</span>);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">var</span> nlogLogger = <span class="kwrd">new</span> NLogLogger(logger);</pre> <pre> _logger = nlogLogger;</pre> <pre class="alt"> _loggingLogger = nlogLogger;</pre> <pre>&nbsp;</pre> <pre class="alt"> _hashProvider = <span class="kwrd">new</span> CryptoHashProvider(<span class="str">"SHA512"</span>);</pre> <pre> _packageRepository = <span class="kwrd">new</span> ServerPackageRepository(</pre> <pre class="alt"> PackageUtility.PackagePhysicalPath,</pre> <pre> _hashProvider,</pre> <pre class="alt"> _loggingLogger)</pre> <pre> {</pre> <pre class="alt"> <span class="rem">// The base class has a separate logger that you have to set.</span></pre> <pre> Logger = _logger</pre> <pre class="alt"> };</pre> <pre> </pre> <pre class="alt"> _packageAuthenticationService = <span class="kwrd">new</span> PackageAuthenticationService();</pre> <pre> _packageService = <span class="kwrd">new</span> PackageService(_packageRepository, _packageAuthenticationService);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">object</span> Resolve(Type type)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(IHashProvider))</pre> <pre> <span class="kwrd">return</span> _hashProvider;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(IServerPackageRepository))</pre> <pre class="alt"> <span class="kwrd">return</span> _packageRepository;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(IPackageAuthenticationService))</pre> <pre> <span class="kwrd">return</span> _packageAuthenticationService;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(IPackageService))</pre> <pre class="alt"> <span class="kwrd">return</span> _packageService;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(ILogger))</pre> <pre> <span class="kwrd">return</span> _logger;</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">if</span> (type == <span class="kwrd">typeof</span>(Logging.ILogger))</pre> <pre class="alt"> <span class="kwrd">return</span> _loggingLogger;</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p> Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-18335725819537167282016年07月17日T22:02:00.001-07:002016年07月17日T22:02:50.663-07:00Should you open source your software?<p style="text-align:center;"><img src="http://static1.squarespace.com/static/5681f92f69492e9cac51bb8b/t/577db62dbebafbf9bab92d3c/1467856445474/?format=500w"></p> <p>Every Tuesday I help host the <a href="http://www.qq-cast.com/" target="_blank">QQ Cast</a>, where we fabricate answers to geek culture's most superfluous questions. For our 50th Quest, my good friend <a href="http://www.robokindrobots.com/robots4autism-home/our-team/" target="_blank">Matt Stevenson</a> joined us to talk about open source software.</p> <p style="text-align:center;"><b><a href="http://www.qq-cast.com/podcast/2016/7/6/quest-50-should-you-open-source-your-software" target="_blank">QQ Cast - Quest 50 - Should you open source your software?</a></b></p> <ul> <li>00:00 - Mic Check and Introductions</li> <li>05:30 - Is "Expand Enhance Extinguish" still alive and well?</li> <li>19:40 - Can we build a sustainable infrastructure without open source software?</li> <li>33:00 - How much structure do we like to see in our frameworks?</li> <li>40:50 - What are some great open source projects?</li> <li>47:15 - Was heartbleed a good thing or a bad thing?</li> <li>54:30 - Does contributing to open source projects help your resume?</li> <li>60:00 - Should you open source your software?</li> <li>70:00 - Wrap up!</li> </ul> <p>Please always remember that all views and opinions expressed on the podcast are representative solely of the person expressing them; not of their friends and family, not of their coworkers, and certainly not of their employers, past, present, or future.</p> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-294517972418513522016年06月30日T11:26:00.000-07:002016年07月02日T11:27:33.594-07:00Updating Default Parameters in .NET Assemblies<img style="float:right; width:225px; margin-left:20px; margin-bottom:20px;" src="https://sites.google.com/site/tdupont750/DefaultParameters.jpg" /> <p>One of the things that I love about C# is how so many of it's features are just very conveniently designed compiler tricks. This means that, just like any other magic trick, once you know how the trick is performed you immediately realize that there really is nothing magical about it.</p> <p>So, let's talk about default parameters. They are actually just constant values that get compiled into your code when you go to use a method that has them. Let's look at an example...</p> <h3 style="margin-top:20px; margin-bottom:15px;">The Code</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">enum</span> Country</pre> <pre>{</pre> <pre class="alt"> US,</pre> <pre> CA</pre> <pre class="alt">}</pre> <pre> </pre> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> SharedUtility</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">bool</span> CanDrink(</pre> <pre> <span class="kwrd">int</span> age, </pre> <pre class="alt"> Country country = Country.CA)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">switch</span> (country)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">case</span> Country.US:</pre> <pre> <span class="kwrd">return</span> age &gt;= 21;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">case</span> Country.CA:</pre> <pre class="alt"> <span class="kwrd">return</span> age &gt;= 18;</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">default</span>:</pre> <pre> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(</pre> <pre class="alt"> <span class="str">"Invalid Country"</span>,</pre> <pre> nameof(country));</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> <pre> </pre> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> SharedUtilityTests</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> CanDrink()</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">var</span> result = SharedUtility.CanDrink(20);</pre> <pre class="alt"> </pre> <pre> <span class="rem">// The line above will compile into the following:</span></pre> <pre class="alt"> <span class="rem">// var result = SharedUtility.CanDrink(20, Country.CA);</span></pre> <pre> <span class="rem">// Thus, the Assert below will succeed!</span></pre> <pre class="alt"> </pre> <pre> Assert.True(result, <span class="str">"The default was not US!"</span>);</pre> <pre class="alt"> }</pre> <pre>}</pre> </div> <p>So, now that you know how the trick is performed, how could this cause a problem for you? Specifically, what happens if you update a library that exposes methods with default parameters?</p> <p><i>Nothing will change, until you recompile against the library!</i></p> <p>If another library changes their default parameters, but you do not recompile your code against it, then your code will be using the old default values! Let's look back at the previous example and see why this could cause confusion...</p> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">The Change</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">enum</span> Country</pre> <pre>{</pre> <pre class="alt"> US,</pre> <pre> CA</pre> <pre class="alt">}</pre> <pre> </pre> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> SharedUtility</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">bool</span> CanDrink(</pre> <pre> <span class="kwrd">int</span> age, </pre> <pre class="alt"> <span class="rem">// NOTE: We have changed the default value!</span></pre> <pre> Country country = Country.US)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">switch</span> (country)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">case</span> Country.US:</pre> <pre class="alt"> <span class="kwrd">return</span> age &gt;= 21;</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">case</span> Country.CA:</pre> <pre> <span class="kwrd">return</span> age &gt;= 18;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">default</span>:</pre> <pre class="alt"> <span class="kwrd">throw</span> <span class="kwrd">new</span> ArgumentException(</pre> <pre> <span class="str">"Invalid Country"</span>,</pre> <pre class="alt"> nameof(country));</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre>}</pre> <pre class="alt"> </pre> <pre><span class="kwrd">public</span> <span class="kwrd">class</span> SharedUtilityTests</pre> <pre class="alt">{</pre> <pre> [Fact]</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">void</span> CanDrink()</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> result = SharedUtility.CanDrink(20);</pre> <pre> </pre> <pre class="alt"> <span class="rem">// If you do NOT recompile and just replace the DLL,</span></pre> <pre> <span class="rem">// then this test will still pass. However, as soon</span></pre> <pre class="alt"> <span class="rem">// as you recompile this test will start failing!</span></pre> <pre> </pre> <pre class="alt"> Assert.True(result, <span class="str">"The default was not US!"</span>);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Don't take my word for it, here are screenshots of that code in action...</p> <p style="text-align:center;"> <img src="https://sites.google.com/site/tdupont750/DefaultParams.png" /> </p> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-37647259694562097142016年06月26日T18:57:00.000-07:002016年06月26日T19:27:20.953-07:00MP3 Playlist for Chromecast<p>Come to find out, I am a very retro guy when it comes to music. I have these old things that I like to use to play music, you may not have heard of them, they are called MP3s. They are kind of like 8-track tapes, but digital, not in the cloud, and not old enough to be cool yet.</p> <h3>How do you play MP3s via Chromecast?</h3> <p>The <a href="https://support.google.com/chromecast/answer/6279417?hl=en" target="_blank">official answer</a> is to use Google Play, but that implies that you want to both pay for that service and upload your files to the cloud. The <a href="http://www.groovypost.com/howto/play-local-media-files-google-chromecast/" target="_blank">unofficial answer</a> is to drag and drop your MP3s into a Chrome tab and cast that tab, however this does not allow you to create a playlist.</p> <p style="text-align:center;"><img src="https://sites.google.com/site/tdupont750/ChromecastAndMP3s.png"></p> <h2>Introducing Playlist for Chromecast</h2> <p>I have created a simple single page HTML 5 application that will act as a playlist for MP3s on your computer. Just <a href="https://github.com/tdupont750/PlaylistForChromecast/archive/master.zip" target="_blank">download the project</a> and open up release/playlist.html in Chrome, then drag and drop MP3s on to the page.</p> <p style="text-align:center;"><img src="https://sites.google.com/site/tdupont750/Playlist.PNG"></p> <h3>Development</h3> <p>I had a lot of fun making this, and I'm not done. I intend to use this project as a case study to talk about VSCode, TypeScript, SASS, HTML5 Audio, NPM, and unit testing. For now, I just wanted to start by getting this initial post up, but expect more to follow.</p> <ul> <li><a href="https://github.com/tdupont750/PlaylistForChromecast" target="_blank">Source on GitHub</a></li> </ul> <h3>What's next?</h3> <ul> <li>Update project documentation.</li> <li>Create unit tests.</li> <li>Add theme support.</li> <li>Write blog posts about development.</li> <li>Maybe host it on a domain.</li> <li>Maybe submit it as a Chrome application.</li> </ul> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com40tag:blogger.com,1999:blog-4063545436138319205.post-35698502157214626842016年06月18日T11:07:00.000-07:002016年06月26日T09:22:42.912-07:00Client Side Caching for jQuery<img style="float: right; width: 150px; margin-left: 20px;" src="https://sites.google.com/site/tdupont750/jquery-icon.png" /> <p><b>Updates: <i>6/26/16</i></b></p> <ul> <li>Fixed bug where triple equals null check would miss.</li> <li>Added support for data driven cache key.</li> <li>Removed let and const statements (some minifiers were having a hard time with them)</li> </ul> <p><b>Original:</b></p> <p>There is great question on Stack Overflow about <a href="http://stackoverflow.com/questions/17104265/caching-a-jquery-ajax-response-in-javascript-browser" target="_blank">caching a jquery ajax response in javascript/browser</a>. Unfortunately, even thought it was a good solution, it did not do quite what I needed it to.</p> <p>The application I was trying to optimize sometimes made redundant parallel requests, and I needed my caching solution to include a queuing system to prevent duplicate fetches.</p> <p>Below is a simple solution that uses <a href="https://api.jquery.com/jQuery.ajaxPrefilter/" target="_blank">jQuery.ajaxPrefilter</a> to check a local cache prior to making GET requests. Additionally, it will queue the callback if the request is already in flight. The cache stores the queue in both memory and local storage, ensuring that the cache will persist across page loads.</p> <h3 style="margin-top: 20px; margin-bottom: 15px;">Implementation</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt">(function ($) {</pre> <pre> <span class="str">"use strict"</span>;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">var</span> timeout = 60000;</pre> <pre class="alt"> <span class="kwrd">var</span> cache = {};</pre> <pre> </pre> <pre class="alt"> $.ajaxPrefilter(onPrefilter);</pre> <pre> </pre> <pre class="alt"> function onPrefilter(options, originalOptions) {</pre> <pre> <span class="kwrd">if</span> (options.cache !== <span class="kwrd">true</span>) {</pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">var</span> callback = originalOptions.complete || $.noop;</pre> <pre class="alt"> <span class="kwrd">var</span> cacheKey = getCacheKey(originalOptions);</pre> <pre> </pre> <pre class="alt"> options.cache = <span class="kwrd">false</span>;</pre> <pre> options.beforeSend = onBeforeSend;</pre> <pre class="alt"> options.complete = onComplete;</pre> <pre> </pre> <pre class="alt"> function onBeforeSend() {</pre> <pre> <span class="kwrd">var</span> cachedItem = tryGet(cacheKey);</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">if</span> (!!cachedItem) {</pre> <pre class="alt"> <span class="kwrd">if</span> (cachedItem.data === <span class="kwrd">null</span>) {</pre> <pre> cachedItem.queue.push(callback);</pre> <pre class="alt"> } <span class="kwrd">else</span> {</pre> <pre> setTimeout(onCacheHit, 0);</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">false</span>;</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> cachedItem = createCachedItem();</pre> <pre class="alt"> cachedItem.queue.push(callback);</pre> <pre> setCache(cacheKey, cachedItem);</pre> <pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">true</span>;</pre> <pre>&nbsp;</pre> <pre class="alt"> function onCacheHit() {</pre> <pre> invoke(callback, cachedItem);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> function onComplete(data, textStatus) {</pre> <pre class="alt"> <span class="kwrd">var</span> cachedItem = tryGet(cacheKey);</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">if</span> (!!cachedItem) {</pre> <pre> cachedItem.data = data;</pre> <pre class="alt"> cachedItem.status = textStatus;</pre> <pre> setCache(cacheKey, cachedItem);</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">var</span> queuedCallback;</pre> <pre class="alt"> while (!!(queuedCallback = cachedItem.queue.pop())) {</pre> <pre> invoke(queuedCallback, cachedItem);</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">return</span>;</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> cachedItem = createCachedItem(data, textStatus);</pre> <pre class="alt"> setCache(cacheKey, cachedItem);</pre> <pre> invoke(callback, cachedItem);</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> function tryGet(cacheKey) {</pre> <pre class="alt"> <span class="kwrd">var</span> cachedItem = cache[cacheKey];</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">if</span> (!!cachedItem) {</pre> <pre> <span class="kwrd">var</span> diff = <span class="kwrd">new</span> Date().getTime() - cachedItem.created;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">if</span> (diff &lt; timeout) {</pre> <pre class="alt"> <span class="kwrd">return</span> cachedItem;</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">var</span> item = localStorage.getItem(cacheKey);</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">if</span> (!!item) {</pre> <pre> cachedItem = JSON.parse(item);</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">var</span> diff = <span class="kwrd">new</span> Date().getTime() - cachedItem.created;</pre> <pre class="alt"> </pre> <pre> <span class="kwrd">if</span> (diff &lt; timeout) {</pre> <pre class="alt"> <span class="kwrd">return</span> cachedItem;</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> localStorage.removeItem(cacheKey);</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">return</span> <span class="kwrd">null</span>;</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> function setCache(cacheKey, cachedItem) {</pre> <pre class="alt"> cache[cacheKey] = cachedItem;</pre> <pre> </pre> <pre class="alt"> <span class="kwrd">var</span> clone = createCachedItem(cachedItem.data, cachedItem.status, cachedItem.created);</pre> <pre> <span class="kwrd">var</span> json = JSON.stringify(clone);</pre> <pre class="alt"> localStorage.setItem(cacheKey, json);</pre> <pre> }</pre> <pre class="alt"> </pre> <pre> function createCachedItem(data, status, created) {</pre> <pre class="alt"> <span class="kwrd">return</span> {</pre> <pre> data: data || <span class="kwrd">null</span>,</pre> <pre class="alt"> status: status,</pre> <pre> created: created || <span class="kwrd">new</span> Date().getTime(),</pre> <pre class="alt"> queue: []</pre> <pre> };</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> function invoke(callback, cachedItem) {</pre> <pre> <span class="kwrd">if</span> ($.isFunction(callback)) {</pre> <pre class="alt"> callback(cachedItem.data, cachedItem.status);</pre> <pre> }</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt"> function getCacheKey(originalOptions) {</pre> <pre> <span class="kwrd">if</span> (!!originalOptions.data) {</pre> <pre class="alt"> <span class="kwrd">return</span> originalOptions.url + <span class="str">"?"</span> + JSON.stringify(originalOptions.data);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">return</span> originalOptions.url;</pre> <pre class="alt"> }</pre> <pre> </pre> <pre class="alt">})(jQuery);</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0tag:blogger.com,1999:blog-4063545436138319205.post-76708671383799298302016年05月27日T07:11:00.000-07:002016年05月27日T07:11:04.269-07:00Word Boundaries Regex<div style="float:right; font-size:5em; margin:20px; margin-top:0px; font-weight:bold;">\b</div> <p>This is the second time this week where I have had to ask myself "how did I not know about this?"</p> <p>There is a regex character to identify <a href="http://www.regular-expressions.info/wordboundaries.html" target="_blank">word boundaries: <b>\b</b></a> This is a zero length match, similar to the caret and dollar sign. It finds the boundaries between words, allowing you to search for a whole word match.</p> <p>Below is a sample extension method that uses this to replace words in a string.</p> <h3 style="margin-top:20px; margin-bottom:15px;">Implementation</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> StringExtensions</pre> <pre>{</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">readonly</span> Regex WordRegex = <span class="kwrd">new</span> Regex(<span class="str">@"\b\w+\b"</span>, RegexOptions.Compiled);</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">string</span> ReplaceWords(</pre> <pre> <span class="kwrd">this</span> <span class="kwrd">string</span> input,</pre> <pre class="alt"> <span class="kwrd">string</span> find,</pre> <pre> <span class="kwrd">string</span> replace,</pre> <pre class="alt"> StringComparison comparison = StringComparison.InvariantCulture)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">return</span> WordRegex.Replace(input, m =&gt; m.Value.Equals(find, comparison)</pre> <pre> ? replace</pre> <pre class="alt"> : m.Value);</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">Unit Test</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">public</span> <span class="kwrd">class</span> StringExtensionsTests</pre> <pre>{</pre> <pre class="alt"> [Fact]</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">void</span> ReplaceWords()</pre> <pre class="alt"> {</pre> <pre> Assert.Equal(</pre> <pre class="alt"> <span class="str">"This island can has beautiful"</span>,</pre> <pre> <span class="str">"This island is beautiful"</span>.ReplaceWords(<span class="str">"is"</span>, <span class="str">"can has"</span>));</pre> <pre class="alt">&nbsp;</pre> <pre> Assert.Equal(</pre> <pre class="alt"> <span class="str">"This island are beautiful"</span>,</pre> <pre> <span class="str">"This island is beautiful"</span>.ReplaceWords(</pre> <pre class="alt"> <span class="str">"IS"</span>, </pre> <pre> <span class="str">"are"</span>, </pre> <pre class="alt"> StringComparison.InvariantCultureIgnoreCase));</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com2tag:blogger.com,1999:blog-4063545436138319205.post-90352017346534491172016年05月22日T11:26:00.000-07:002016年05月22日T11:58:48.637-07:00Common Logging Extensions with Caller Information<img src="http://devstickers.com/assets/img/pro/2p4i.png" style="float:right; width: 200px; margin-left: 20px; margin-bottom: 20px;" /> <p><i>Update: Added the BreakParameter.</i></p> <p>I have made it an (arguably bad) habit of manually adding the class and method name as a prefix to all my log lines. It is not that I enjoy typing out the same strings over and over, it's that I do not always trust things like the <a href="https://github.com/NLog/NLog/wiki/Callsite-layout-renderer" target="_blank">NLog callsite</a>. Using the stack frame to identify a calling method always requires a bit of cleverness on the part of the author, as you never can be totally sure when you are dealing with a wrapper class or an async call stack.</p> <p>I was recently introduced to the <a href="https://msdn.microsoft.com/en-us/library/mt653988.aspx" target="_blank">Caller Information attributes in C# 5</a>, and now I think I am in love.</p> <p>Disclaimer: I have not used this very much yet, but I intend to start! I think that these attributes are absolutely brilliant in their simplicity: a compiler trick to insert debug information directly into your code. That is freak'n sweet, and <a href="http://geekswithblogs.net/BlackRabbitCoder/archive/2013/07/25/c.net-little-wonders-getting-caller-information.aspx" target="_blank">it's performant</a>! I am not sure how this flew under my radar, but now that I know about it....</p> <p>Below is a little T4 template that I wrote up to generate overloads for Common Logging that will include caller information. To customize this for your needs, just update the the GetFormat method at the bottom of the template.</p> <h3 style="margin-top:20px; margin-bottom:15px;">Unit Tests</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="lnum"> 1: </span><span class="kwrd">using</span> System;</pre> <pre><span class="lnum"> 2: </span><span class="kwrd">using</span> System.Collections.Generic;</pre> <pre class="alt"><span class="lnum"> 3: </span><span class="kwrd">using</span> Common.Logging.Simple;</pre> <pre><span class="lnum"> 4: </span><span class="kwrd">using</span> Xunit;</pre> <pre class="alt"><span class="lnum"> 5: </span>&nbsp;</pre> <pre><span class="lnum"> 6: </span><span class="kwrd">namespace</span> Common.Logging.Tests</pre> <pre class="alt"><span class="lnum"> 7: </span>{</pre> <pre><span class="lnum"> 8: </span> <span class="rem">/// &lt;summary&gt;</span></pre> <pre class="alt"><span class="lnum"> 9: </span> <span class="rem">/// Tests for Common Logging extensions that use Caller Information</span></pre> <pre><span class="lnum"> 10: </span> <span class="rem">/// https://msdn.microsoft.com/en-us/library/mt653988.aspx</span></pre> <pre class="alt"><span class="lnum"> 11: </span> <span class="rem">/// &lt;/summary&gt;</span></pre> <pre><span class="lnum"> 12: </span> <span class="kwrd">public</span> <span class="kwrd">class</span> LogCallTests</pre> <pre class="alt"><span class="lnum"> 13: </span> {</pre> <pre><span class="lnum"> 14: </span> [Fact]</pre> <pre class="alt"><span class="lnum"> 15: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> LogFromMethod()</pre> <pre><span class="lnum"> 16: </span> {</pre> <pre class="alt"><span class="lnum"> 17: </span> <span class="kwrd">var</span> log = <span class="kwrd">new</span> QueueSimpleLogger();</pre> <pre><span class="lnum"> 18: </span> <span class="kwrd">var</span> ex = <span class="kwrd">new</span> Exception(<span class="str">"Boom"</span>);</pre> <pre class="alt"><span class="lnum"> 19: </span>&nbsp;</pre> <pre><span class="lnum"> 20: </span> log.Debug(<span class="str">"Hello"</span>);</pre> <pre class="alt"><span class="lnum"> 21: </span> log.Debug(<span class="str">"World"</span>, ex);</pre> <pre><span class="lnum"> 22: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 23: </span> log.DebugCall(<span class="str">"Hello"</span>);</pre> <pre><span class="lnum"> 24: </span> log.DebugCall(<span class="str">"World"</span>, ex);</pre> <pre class="alt"><span class="lnum"> 25: </span>&nbsp;</pre> <pre><span class="lnum"> 26: </span> log.WarnFormat(<span class="str">"Hello - {0}"</span>, <span class="str">"Zero"</span>);</pre> <pre class="alt"><span class="lnum"> 27: </span> log.WarnFormat(<span class="str">"World - {0}"</span>, ex, <span class="str">"Zero"</span>);</pre> <pre><span class="lnum"> 28: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 29: </span> log.WarnFormatCall(<span class="str">"Hello - {0}"</span>, <span class="str">"Zero"</span>);</pre> <pre><span class="lnum"> 30: </span> log.WarnFormatCall(<span class="str">"World - {0}"</span>, ex, <span class="str">"Zero"</span>);</pre> <pre class="alt"><span class="lnum"> 31: </span>&nbsp;</pre> <pre><span class="lnum"> 32: </span> Assert.Equal(8, log.Queue.Count);</pre> <pre class="alt"><span class="lnum"> 33: </span>&nbsp;</pre> <pre><span class="lnum"> 34: </span> Assert.Equal(<span class="str">"Hello"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 35: </span> Assert.Equal(<span class="str">"World - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 36: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 37: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromMethod(23) - Hello"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 38: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromMethod(24) - World - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 39: </span>&nbsp;</pre> <pre><span class="lnum"> 40: </span> Assert.Equal(<span class="str">"Hello - Zero"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 41: </span> Assert.Equal(<span class="str">"World - Zero - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 42: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 43: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromMethod(29) - Hello - Zero"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 44: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromMethod(30) - World - Zero - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 45: </span> }</pre> <pre><span class="lnum"> 46: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 47: </span> [Fact]</pre> <pre><span class="lnum"> 48: </span> <span class="kwrd">public</span> <span class="kwrd">void</span> LogFromAction()</pre> <pre class="alt"><span class="lnum"> 49: </span> {</pre> <pre><span class="lnum"> 50: </span> <span class="kwrd">var</span> log = <span class="kwrd">new</span> QueueSimpleLogger();</pre> <pre class="alt"><span class="lnum"> 51: </span> <span class="kwrd">var</span> ex = <span class="kwrd">new</span> Exception(<span class="str">"Boom"</span>);</pre> <pre><span class="lnum"> 52: </span> Action action = () =&gt;</pre> <pre class="alt"><span class="lnum"> 53: </span> {</pre> <pre><span class="lnum"> 54: </span> log.Debug(<span class="str">"Hello"</span>);</pre> <pre class="alt"><span class="lnum"> 55: </span> log.Debug(<span class="str">"World"</span>, ex);</pre> <pre><span class="lnum"> 56: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 57: </span> log.DebugCall(<span class="str">"Hello"</span>);</pre> <pre><span class="lnum"> 58: </span> log.DebugCall(<span class="str">"World"</span>, ex);</pre> <pre class="alt"><span class="lnum"> 59: </span>&nbsp;</pre> <pre><span class="lnum"> 60: </span> log.WarnFormat(<span class="str">"Hello - {0}"</span>, <span class="str">"Zero"</span>);</pre> <pre class="alt"><span class="lnum"> 61: </span> log.WarnFormat(<span class="str">"World - {0}"</span>, ex, <span class="str">"Zero"</span>);</pre> <pre><span class="lnum"> 62: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 63: </span> log.WarnFormatCall(<span class="str">"Hello - {0}"</span>, <span class="str">"Zero"</span>);</pre> <pre><span class="lnum"> 64: </span> log.WarnFormatCall(<span class="str">"World - {0}"</span>, ex, <span class="str">"Zero"</span>);</pre> <pre class="alt"><span class="lnum"> 65: </span> };</pre> <pre><span class="lnum"> 66: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 67: </span> action();</pre> <pre><span class="lnum"> 68: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 69: </span> Assert.Equal(8, log.Queue.Count);</pre> <pre><span class="lnum"> 70: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 71: </span> Assert.Equal(<span class="str">"Hello"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 72: </span> Assert.Equal(<span class="str">"World - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 73: </span>&nbsp;</pre> <pre><span class="lnum"> 74: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromAction(57) - Hello"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 75: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromAction(58) - World - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 76: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 77: </span> Assert.Equal(<span class="str">"Hello - Zero"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 78: </span> Assert.Equal(<span class="str">"World - Zero - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 79: </span>&nbsp;</pre> <pre><span class="lnum"> 80: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromAction(63) - Hello - Zero"</span>, log.Queue.Dequeue());</pre> <pre class="alt"><span class="lnum"> 81: </span> Assert.Equal(<span class="str">"LogCallTests.LogFromAction(64) - World - Zero - Ex: Boom"</span>, log.Queue.Dequeue());</pre> <pre><span class="lnum"> 82: </span> }</pre> <pre class="alt"><span class="lnum"> 83: </span>&nbsp;</pre> <pre><span class="lnum"> 84: </span> <span class="kwrd">private</span> <span class="kwrd">class</span> QueueSimpleLogger : AbstractSimpleLogger</pre> <pre class="alt"><span class="lnum"> 85: </span> {</pre> <pre><span class="lnum"> 86: </span> <span class="kwrd">public</span> <span class="kwrd">readonly</span> Queue&lt;<span class="kwrd">string</span>&gt; Queue = <span class="kwrd">new</span> Queue&lt;<span class="kwrd">string</span>&gt;(); </pre> <pre class="alt"><span class="lnum"> 87: </span>&nbsp;</pre> <pre><span class="lnum"> 88: </span> <span class="kwrd">public</span> QueueSimpleLogger()</pre> <pre class="alt"><span class="lnum"> 89: </span> : <span class="kwrd">base</span>(<span class="kwrd">string</span>.Empty, LogLevel.All, <span class="kwrd">true</span>, <span class="kwrd">true</span>, <span class="kwrd">true</span>, <span class="kwrd">string</span>.Empty)</pre> <pre><span class="lnum"> 90: </span> {</pre> <pre class="alt"><span class="lnum"> 91: </span> }</pre> <pre><span class="lnum"> 92: </span>&nbsp;</pre> <pre class="alt"><span class="lnum"> 93: </span> <span class="kwrd">protected</span> <span class="kwrd">override</span> <span class="kwrd">void</span> WriteInternal(LogLevel level, <span class="kwrd">object</span> message, Exception exception)</pre> <pre><span class="lnum"> 94: </span> {</pre> <pre class="alt"><span class="lnum"> 95: </span> <span class="kwrd">var</span> s = message.ToString();</pre> <pre><span class="lnum"> 96: </span> <span class="kwrd">if</span> (exception != <span class="kwrd">null</span>) s += <span class="str">" - Ex: "</span> + exception.Message;</pre> <pre class="alt"><span class="lnum"> 97: </span> Queue.Enqueue(s);</pre> <pre><span class="lnum"> 98: </span> }</pre> <pre class="alt"><span class="lnum"> 99: </span> }</pre> <pre><span class="lnum"> 100: </span> }</pre> <pre class="alt"><span class="lnum"> 101: </span>}</pre> </div> <a name='more'></a> <h3 style="margin-top:20px; margin-bottom:15px;">LogExtension.cs (Part of a Generated File)</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt"><span class="kwrd">using</span> System;</pre> <pre><span class="kwrd">using</span> System.Runtime.CompilerServices;</pre> <pre class="alt"><span class="kwrd">using</span> System.Text.RegularExpressions;</pre> <pre>&nbsp;</pre> <pre class="alt"><span class="rem">// ReSharper disable once CheckNamespace</span></pre> <pre><span class="kwrd">namespace</span> Common.Logging</pre> <pre class="alt">{</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> LogExtensions</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> Regex ClassNameRegex = <span class="kwrd">new</span> Regex(<span class="str">@"([\w]+)\.[\w]+$"</span>, RegexOptions.Compiled);</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">/* ... */</span></pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> DebugCall(</pre> <pre class="alt"> <span class="kwrd">this</span> ILog log,</pre> <pre> <span class="kwrd">object</span> message,</pre> <pre class="alt"> BreakParameter breakParameter = <span class="kwrd">default</span>(BreakParameter),</pre> <pre> [CallerMemberName] <span class="kwrd">string</span> memberName = <span class="str">""</span>,</pre> <pre class="alt"> [CallerFilePath] <span class="kwrd">string</span> sourceFilePath = <span class="str">""</span>,</pre> <pre> [CallerLineNumber] <span class="kwrd">int</span> sourceLineNumber = 0)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (!log.IsDebugEnabled) <span class="kwrd">return</span>;</pre> <pre class="alt"> <span class="kwrd">var</span> newFormat = GetFormat(message.ToString(), memberName, sourceFilePath, sourceLineNumber);</pre> <pre> log.Debug(</pre> <pre class="alt"> newFormat);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> DebugCall(</pre> <pre class="alt"> <span class="kwrd">this</span> ILog log,</pre> <pre> <span class="kwrd">object</span> message,</pre> <pre class="alt"> Exception ex,</pre> <pre> BreakParameter breakParameter = <span class="kwrd">default</span>(BreakParameter),</pre> <pre class="alt"> [CallerMemberName] <span class="kwrd">string</span> memberName = <span class="str">""</span>,</pre> <pre> [CallerFilePath] <span class="kwrd">string</span> sourceFilePath = <span class="str">""</span>,</pre> <pre class="alt"> [CallerLineNumber] <span class="kwrd">int</span> sourceLineNumber = 0)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (!log.IsDebugEnabled) <span class="kwrd">return</span>;</pre> <pre> <span class="kwrd">var</span> newFormat = GetFormat(message.ToString(), memberName, sourceFilePath, sourceLineNumber);</pre> <pre class="alt"> log.Debug(</pre> <pre> newFormat,</pre> <pre class="alt"> ex);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> DebugFormatCall(</pre> <pre class="alt"> <span class="kwrd">this</span> ILog log,</pre> <pre> <span class="kwrd">string</span> format,</pre> <pre class="alt"> <span class="kwrd">object</span> arg0,</pre> <pre> BreakParameter breakParameter = <span class="kwrd">default</span>(BreakParameter),</pre> <pre class="alt"> [CallerMemberName] <span class="kwrd">string</span> memberName = <span class="str">""</span>,</pre> <pre> [CallerFilePath] <span class="kwrd">string</span> sourceFilePath = <span class="str">""</span>,</pre> <pre class="alt"> [CallerLineNumber] <span class="kwrd">int</span> sourceLineNumber = 0)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (!log.IsDebugEnabled) <span class="kwrd">return</span>;</pre> <pre> <span class="kwrd">var</span> newFormat = GetFormat(format, memberName, sourceFilePath, sourceLineNumber);</pre> <pre class="alt"> log.DebugFormat(</pre> <pre> newFormat,</pre> <pre class="alt"> arg0);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> DebugFormatCall(</pre> <pre class="alt"> <span class="kwrd">this</span> ILog log,</pre> <pre> <span class="kwrd">string</span> format,</pre> <pre class="alt"> Exception ex,</pre> <pre> <span class="kwrd">object</span> arg0,</pre> <pre class="alt"> BreakParameter breakParameter = <span class="kwrd">default</span>(BreakParameter),</pre> <pre> [CallerMemberName] <span class="kwrd">string</span> memberName = <span class="str">""</span>,</pre> <pre class="alt"> [CallerFilePath] <span class="kwrd">string</span> sourceFilePath = <span class="str">""</span>,</pre> <pre> [CallerLineNumber] <span class="kwrd">int</span> sourceLineNumber = 0)</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">if</span> (!log.IsDebugEnabled) <span class="kwrd">return</span>;</pre> <pre class="alt"> <span class="kwrd">var</span> newFormat = GetFormat(format, memberName, sourceFilePath, sourceLineNumber);</pre> <pre> log.DebugFormat(</pre> <pre class="alt"> newFormat,</pre> <pre> ex,</pre> <pre class="alt"> arg0);</pre> <pre> }</pre> <pre class="alt">&nbsp;</pre> <pre> <span class="rem">/* ... */</span></pre> <pre class="alt">&nbsp;</pre> <pre> [MethodImpl(MethodImplOptions.AggressiveInlining)]</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">string</span> GetFormat(<span class="kwrd">string</span> format, <span class="kwrd">string</span> memberName, <span class="kwrd">string</span> sourceFilePath, <span class="kwrd">int</span> sourceLineNumber)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> m = ClassNameRegex.Match(sourceFilePath);</pre> <pre> <span class="kwrd">return</span> m.Success</pre> <pre class="alt"> ? $<span class="str">"{m.Groups[1].Value}.{memberName}({sourceLineNumber}) - {format}"</span></pre> <pre> : format;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">struct</span> BreakParameter</pre> <pre> {</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <h3 style="margin-top:20px; margin-bottom:15px;">LogExtension.tt (T4 Template)</h3> <!-- code formatted by http://manoli.net/csharpformat/ --> <div class="csharpcode"> <pre class="alt">&lt;#@ template debug=<span class="str">"false"</span> hostspecific=<span class="str">"false"</span> language=<span class="str">"C#"</span> #&gt;</pre> <pre>&lt;#@ assembly name=<span class="str">"System.Core"</span> #&gt;</pre> <pre class="alt">&lt;#@ import <span class="kwrd">namespace</span>=<span class="str">"System.Linq"</span> #&gt;</pre> <pre>&lt;#@ import <span class="kwrd">namespace</span>=<span class="str">"System.Text"</span> #&gt;</pre> <pre class="alt">&lt;#@ import <span class="kwrd">namespace</span>=<span class="str">"System.Collections.Generic"</span> #&gt;</pre> <pre>&lt;#@ output extension=<span class="str">".cs"</span> #&gt;</pre> <pre class="alt"><span class="kwrd">using</span> System;</pre> <pre><span class="kwrd">using</span> System.Runtime.CompilerServices;</pre> <pre class="alt"><span class="kwrd">using</span> System.Text.RegularExpressions;</pre> <pre>&nbsp;</pre> <pre class="alt"><span class="rem">// ReSharper disable once CheckNamespace</span></pre> <pre><span class="kwrd">namespace</span> Common.Logging</pre> <pre class="alt">{</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">class</span> LogExtensions</pre> <pre class="alt"> {</pre> <pre> <span class="kwrd">public</span> <span class="kwrd">static</span> Regex ClassNameRegex = <span class="kwrd">new</span> Regex(<span class="str">@"([\w]+)\.[\w]+$"</span>, RegexOptions.Compiled);</pre> <pre class="alt">&nbsp;</pre> <pre>&lt;# <span class="kwrd">foreach</span>(<span class="kwrd">var</span> level <span class="kwrd">in</span> <span class="kwrd">new</span> [] { <span class="str">"Trace"</span>, <span class="str">"Debug"</span>, <span class="str">"Info"</span>, <span class="str">"Warn"</span>, <span class="str">"Error"</span>, <span class="str">"Fatal"</span> }) { #&gt;</pre> <pre class="alt">&lt;# <span class="kwrd">for</span>(<span class="kwrd">var</span> argCount = 0; argCount &lt;= 8; argCount++) { #&gt;</pre> <pre>&lt;# <span class="kwrd">var</span> suffix = argCount == 0 ? <span class="str">""</span> : <span class="str">"Format"</span>; #&gt;</pre> <pre class="alt">&lt;# <span class="kwrd">var</span> varType = argCount == 0 ? <span class="str">"object"</span> : <span class="str">"string"</span>; #&gt;</pre> <pre>&lt;# <span class="kwrd">var</span> varName = argCount == 0 ? <span class="str">"message"</span> : <span class="str">"format"</span>; #&gt;</pre> <pre class="alt">&lt;# <span class="kwrd">var</span> vartoString = argCount == 0 ? <span class="str">".ToString()"</span> : <span class="str">""</span>; #&gt;</pre> <pre>&lt;# <span class="kwrd">for</span>(<span class="kwrd">var</span> hasException = 0; hasException &lt; 2; hasException++) { #&gt;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">static</span> <span class="kwrd">void</span> &lt;#= level #&gt;&lt;#= suffix #&gt;Call(</pre> <pre> <span class="kwrd">this</span> ILog log,</pre> <pre class="alt"> &lt;#= varType #&gt; &lt;#= varName #&gt;,</pre> <pre>&lt;# <span class="kwrd">if</span> (hasException == 1) { #&gt;</pre> <pre class="alt"> Exception ex,</pre> <pre>&lt;# } #&gt;</pre> <pre class="alt">&lt;# <span class="kwrd">for</span>(<span class="kwrd">var</span> i = 0; i &lt; argCount; i++) { #&gt;</pre> <pre> <span class="kwrd">object</span> arg&lt;#= i #&gt;,</pre> <pre class="alt">&lt;# } #&gt;</pre> <pre> BreakParameter breakParameter = <span class="kwrd">default</span>(BreakParameter),</pre> <pre class="alt"> [CallerMemberName] <span class="kwrd">string</span> memberName = <span class="str">""</span>,</pre> <pre> [CallerFilePath] <span class="kwrd">string</span> sourceFilePath = <span class="str">""</span>,</pre> <pre class="alt"> [CallerLineNumber] <span class="kwrd">int</span> sourceLineNumber = 0)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">if</span> (!log.Is&lt;#= level #&gt;Enabled) <span class="kwrd">return</span>;</pre> <pre> <span class="kwrd">var</span> newFormat = GetFormat(&lt;#= varName #&gt;&lt;#= vartoString #&gt;, memberName, sourceFilePath, sourceLineNumber);</pre> <pre class="alt"> log.&lt;#= level #&gt;&lt;#= suffix #&gt;(</pre> <pre> newFormat&lt;# <span class="kwrd">if</span> (hasException == 1) { #&gt;,</pre> <pre class="alt"> ex&lt;# } #&gt;&lt;# <span class="kwrd">for</span>(<span class="kwrd">var</span> i = 0; i &lt; argCount; i++) { #&gt;,</pre> <pre> arg&lt;#= i #&gt;&lt;# } #&gt;);</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt">&lt;# } #&gt;</pre> <pre>&lt;# } #&gt;</pre> <pre class="alt">&lt;# } #&gt;</pre> <pre> [MethodImpl(MethodImplOptions.AggressiveInlining)]</pre> <pre class="alt"> <span class="kwrd">private</span> <span class="kwrd">static</span> <span class="kwrd">string</span> GetFormat(<span class="kwrd">string</span> format, <span class="kwrd">string</span> memberName, <span class="kwrd">string</span> sourceFilePath, <span class="kwrd">int</span> sourceLineNumber)</pre> <pre> {</pre> <pre class="alt"> <span class="kwrd">var</span> m = ClassNameRegex.Match(sourceFilePath);</pre> <pre> <span class="kwrd">return</span> m.Success</pre> <pre class="alt"> ? $<span class="str">"{m.Groups[1].Value}.{memberName}({sourceLineNumber}) - {format}"</span></pre> <pre> : format;</pre> <pre class="alt"> }</pre> <pre>&nbsp;</pre> <pre class="alt"> <span class="kwrd">public</span> <span class="kwrd">struct</span> BreakParameter</pre> <pre> {</pre> <pre class="alt"> }</pre> <pre> }</pre> <pre class="alt">}</pre> </div> <p>Enjoy,<br>Tom</p>Tom DuPonthttp://www.blogger.com/profile/08233783264428691459noreply@blogger.com0

AltStyle によって変換されたページ (->オリジナル) /