Saturday, November 29, 2014

.NET 4.5 HttpClient is Thread Safe

Good news everyone, the .NET 4.5 HttpClient is thread safe!

This means that you can share instances of your HttpClients across your entire application. This is useful in that it allows you to reuse persisted connections. One of the best ways to do this is to create a class that can manage the object lifetime of those clients for you.

Below is a simple HttpClientManager that will create one HttpClient per authority. Why per authority? Because you might have to have different settings or credentials for different websites.

Sample Unit Tests

public class HttpClientManagerTests
{
    [Fact]
    public void GetForAuthority()
    {
        using (var manager = new HttpClientManager())
        {
            var client1 = manager.GetForAuthority("http://tomdupont.net/");
            var client2 = manager.GetForAuthority("https://tomdupont.net/");
            Assert.Same(client1, client2);
 
            var client3 = manager.GetForAuthority("http://google.com/");
            Assert.NotSame(client1, client3);
        }
    }
 
    [Fact]
    public void TryRemoveForAuthority()
    {
        const string uri = "http://tomdupont.net/";
 
        using (var manager = new HttpClientManager())
        {
            Assert.False(manager.TryRemoveForAuthority(uri));
 
            manager.GetForAuthority(uri);
 
            Assert.True(manager.TryRemoveForAuthority(uri));
 
        }
    }
}

Friday, November 28, 2014

Web API - Return Correct Status Codes for Exceptions

Returning the appropriate HTTP Response Codes back from your web server is a very important best practice. Fortunately for .NET developers, Web API makes it very easy to use Exception Filters to return the appropriate response codes from your exceptions.

By implementing a custom ExceptionFilterAttribute you can generically create and return HttpResponseMessages for unhandled exceptions based on type. This is great in that you do not have to wrap all of your controller actions in try catch blocks to handle exceptions from other application layers.

Sample Controller

public class ValuesController : ApiController
{
    public string Get(int id)
    {
        switch (id)
        {
            case 1:
                throw new KeyNotFoundException("Hello World");
 
            case 2:
                throw new ArgumentException("Goodnight Moon");
 
            default:
                return "value";
        }
    }
}

Saturday, November 22, 2014

Web API - Bad Request when Model State is Invalid

When you using Web API, would you like to always return a 400 (bad request) response whenever the model state is invalid? It is easy, just add the following filter attribute to your global list:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, 
    AllowMultiple = false, 
    Inherited = true)]
public class InvalidModelStateFilterAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.ModelState.IsValid)
        {
            actionContext.Response = actionContext.Request.CreateErrorResponse(
                HttpStatusCode.BadRequest, 
                actionContext.ModelState);
        }
    }
}

Enjoy,
Tom

Saturday, November 15, 2014

AutoCancellationTokenSource

With the following code you can create a CancellationTokenSource that will signal cancellation on dispose. This provides an alternative to having to wrap a normal CancellationTokenSource in a try finally block.

Code

public class AutoCancellationTokenSource : CancellationTokenSource
{
    private bool _isDisposed;
 
    public AutoCancellationTokenSource()
    {
    }
 
    public AutoCancellationTokenSource(params CancellationToken[] linkedTokens)
    {
        foreach (var linkedToken in linkedTokens)
            if (linkedToken.IsCancellationRequested)
                TryCancel();
            else
                linkedToken.Register(TryCancel, false);
    }
 
    protected override void Dispose(bool disposing)
    {
        if (_isDisposed)
            return;
 
        TryCancel();
 
        base.Dispose(disposing);
 
        _isDisposed = true;
    }
 
    private void TryCancel()
    {
        if (!_isDisposed && !IsCancellationRequested)
            Cancel();
    }
}

Friday, October 31, 2014

FireAndForget a Task with AggressiveInlining

When working with tasks you will get a warning if you do not use a task returned from a method. However, you might actually want to fire and forget that task. So what do you do?

One option is to create an extension method for your task to mark it as fire and forget. Aside from removing the warning, it also gives you the nice ability to find all usages.

When creating this method it is a good idea to mark it with the aggressive inlining attribute. This will cause the compiler to try and inline the method to try and optimize performance.

Implementation and Unit Test

public static class TaskExtensions
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void FireAndForget(this Task task)
    {
        // Do Nothing
    }
}
 
public class TaskExtensionTests
{
    [Fact]
    public void FireAndForget()
    {
        Task
            .Delay(100)
            .ContinueWith(t =>
            {
                // TODO: Stuff!
            })
            .FireAndForget();
    }
}

Enjoy,
Tom

Thursday, October 30, 2014

Tuesday, October 14, 2014

Share ReSharper and StyleCop Configuration via NuGet

Would you to share your coding style standards between projects?

Tools such as ReSharper and StyleCop both allow you to share your settings files between projects by placing a configuration file in the solution directory. However just sharing those settings across projects in a single solution might not be enough to truly enforce your coding standards for a whole team.

You can share your configuration settings across multiple solutions via a NuGet package. These configuration files can not just be standard content files in your NuGet package, they need to be installed via the init PowerShell script inside of the package.

Init.ps1

param($installPath, $toolsPath, $package)
 
Write-Host "==================================="
Write-Host "Initing: CSharpConventions"
 
# Get the active solution
$solution = Get-Interface $dte.Solution ([EnvDTE80.Solution2])
$solutionDir = Get-Item $solution.FullName
 
# Copy StyleCopy file
$copFileName = "Settings.StyleCop"
$newCopPath = join-path $solutionDir.Directory $copFileName
$oldCopPath = join-path $toolsPath $copFileName
Write-Host "Copying " $oldCopPath " to " $newCopPath
Copy-Item $oldCopPath $newCopPath 
 
# Copy and rename DotSettings file
$newDotPath = $solution.FullName + ".DotSettings"
$oldDotPath = join-path $toolsPath "Solution.sln.DotSettings"
Write-Host "Copying " $oldDotPath " to " $newDotPath
Copy-Item $oldDotPath $newDotPath 
 
Write-Host "Completed: CSharpConventions"
Write-Host "====================================="

NuSpec File

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>CSharpConventions</id>
        <version>1.0.0</version>
        <authors>tdupont</authors>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>
            StyleCop and R# settings file to enforce C# coding standards.
        </description>
    </metadata>
    <files>
        <file src="tools\init.ps1" target="tools\init.ps1" />
        <file src="tools\Settings.StyleCop" 
            target="tools\Settings.StyleCop" />
        <file src="tools\Solution.sln.DotSettings" 
            target="tools\Solution.sln.DotSettings" />
    </files>
</package>

Enjoy,
Tom

Real Time Web Analytics