Palmer 1.0.0

dotnet add package Palmer --version 1.0.0
NuGet\Install-Package Palmer -Version 1.0.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Palmer" Version="1.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Palmer --version 1.0.0
#r "nuget: Palmer, 1.0.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Palmer as a Cake Addin
#addin nuget:?package=Palmer&version=1.0.0

// Install Palmer as a Cake Tool
#tool nuget:?package=Palmer&version=1.0.0

Palmer

Palmer is a .NET library that presents a fluent-API which makes it easier to write retry logic. Palmer is developed and maintained by Mitch Denny and is released under the MIT license.

How do I get my hands on Palmer?

You can download Palmer is source from GitHub, however as a .NET developer it would be far easier to use NuGet. Simply bring up your package management console and install the package.

Install-Package Palmer

How to do I use Palmer?

Once you have the library installed using Palmer is simple. Just wrap a call to Palmer around the offending code block and it will manage the retry logic for you.

Retry.On<WebException>().For(TimeSpan.FromSeconds(15)).With(context =>
  {
	// Code that might periodically fail due to connectivity issues.
  });

With these few lines of code we are telling Palmer to keep retrying the code for 15 seconds, or until it succeeds. If the code still fails after 15 seconds then a RetryException is thrown which provides the developer with access to the latest exception, and all previously raised exceptions. If the code throws any other exception that exception is allowed to bubble out of Palmer.

Why is it called "Palmer"?

The origins of the library name are tied to the origins of the quote:

"If at first you don't succeed, try, try again." - Thomas H. Palmer

Thomas H. Palmer was an educator who wrote a teachers manual from which this quote was taken. I felt that the quote was appropriate for what the Palmer library does. At the time I named the library I was tempted to name it after W.C. Fields who had an extended version of the quote.

"If at first you don't succeed, try, try again. Then quit. There's no point being a damn fool about it." - W.C. Fields.

This is ultimately why you need to use Palmer. Palmer allows you to succinctly declare retry logic and clearly express under which conditions the code should just give up.

More Examples

Palmer is still a young library and I don't have an exhaustive API reference (feel free to get involved). Rather than provide no documentation I thought I might provide some examples of how you might use Palmer in your code.

###Deadlock Retry This code shows you to example the last raised exception to determine if it was a deadlock. It retries five times before giving up. This demonstrates a "count-based" retry strategy instead of a duration based retry strategy.

Retry.On<SqlException>(handle => (handle.Context.LastException as SqlException).Number == 1205).For(5).With(context =>
	{
		// Code that might result in a SQL deadlock.
	});

###Retry Forever Normally you wouldn't retry forever, but the capability to retry forever is included for completeness.

Retry.On<WebException>().Indefinitely().With(context =>
	{
		// Code that might throw a web exception.
	});

###Non-Exception Failures Some APIs don't throw exceptions. Rather than forcing you to throw an exception when you don't need to, Palmer allows you to write an expression to determine whether a retry is necessary.

var completedSuccessfully = false;

Retry.On(handle => completedSuccessfully).For(5).With(context =>
	{
		completedSuccessfully = SomeApiThatReturnsTrueOrFailsAsSuccessStatus();
	});

Multiple Exceptions

Sometimes the code that you write might throw multiple different exceptions, Palmer allows you to handle this.

Retry.On<WebException>().For(5).AndOn<SqlException>().For(5).With(context =>
	{
		// Code that might throw a web exception, or a sql exception.
	});

In the case above the counts for each exception are independent of each other.

Post Conditions with Until

Post conditions are very similar to detecting non-exception failures. Where they differ is their intended usage. The On/AndOn methods which take predicates are evaluated against every exception or on every run. The predicate specified with the Until method is only evaulated once an exception has been detected. At this point this method could be used to fail after a total number of exceptions has been reached.

Retry
	.On<WebException>().Until(handle => handle.Context.Exceptions.Count() > 10)
	.AndOn<SqlException().Until(handle => handle.Context.Exceptions.Count() > 10)
	.With(context =>
	{
		// Code that might throw a web exception.
	});

Getting Help

If you are using Palmer I would love to hear about it. The easiest way get help is by logging an issue on GitHub. Alternatively you can get me on Twitter at @MitchDenny or via my blog at http://blog.mitchdenny.com.

Contributors

I'm always happy to have someone contribute pull requests to the project. If you do I'll add you to the contributors section here.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Palmer:

Package Downloads
Mirabeau.uTransporter

An continuous deployment tool for the Umbraco CMS. Synchronizes document types, tab and templates.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.0.0 10,078 6/2/2019
1.0.0-preview.58 311 6/1/2019
0.1.4724.20297 34,014 12/7/2012
0.1.4724.19830 1,343 12/7/2012
0.1.4724.7256 1,634 12/7/2012
0.1.4723.43045 1,476 12/7/2012
0.1.4723.40614 1,315 12/6/2012