August 17, 2017 | by Dominique St-Amand |

6 min read

It’s always a pleasure to see the community help each other out in ways we think are unimaginable. One of the best way some people help, is to open source their hard work into libraries, so you don’t have to code the behavior yourself. It’s always hard to know what’s out there, so in this post, I want to give a shootout to some of the .NET libraries I find could definitely enhance your application(s) and if not, beef up your toolbox. As a developer, it’s good to know they exist, so you can increase your knowledge of the available tools out there. They definitely can help you out in the future.

Note that I am listing here some libraries that I use and used and some that I could see myself using in the future if need be. Be aware that those are not the only libraries available around. Contributors did a good job listing quite a few (a lot of them!) here so you should definitely have a look at what is being developed.

If you find bugs, please report them and even better if you can fix them and make a pull request! You can only help others that way!

Edit (2017-08-27): Added Refit and and refactored Autofac into a DI/IOC Container section along with adding library SimpleInjector in the list. Thanks to the comments!

General:

Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.

Transient faults are errors that occur because of some temporary condition such as network connectivity issues or service unavailability. Typically, if you retry the operation that resulted in a transient error a short time later, you find that the error has disappeared 1.

Polly allows you to handle such errors and to define policies (actions) on what to do when such errors occur. Be careful, however, on what you define as a transient error.

Jot is a library for persisting and applying .NET application state. It decouples the dependence to Settings.Settings class/file. Great for desktop applications.

static class Services { public static StateTracker Tracker = new StateTracker(); } 1 2 3 4 static class Services { public static StateTracker Tracker = new StateTracker ( ) ; }

public MainWindow() { InitializeComponent(); Services.Tracker.Configure(this)//the object to track .IdenitifyAs("main window")//a string by which to identify the target object .AddProperties<Window>(w => w.Height, w => w.Width, w => w.Top, w => w.Left, w => w.WindowState)//properties to track .RegisterPersistTrigger(nameof(SizeChanged))//when to persist data to the store .Apply();//apply any previously stored data } 1 2 3 4 5 6 7 8 9 10 public MainWindow ( ) { InitializeComponent ( ) ; Services . Tracker . Configure ( this ) //the object to track . IdenitifyAs ( "main window" ) //a string by which to identify the target object . AddProperties < Window > ( w = > w . Height , w = > w . Width , w = > w . Top , w = > w . Left , w = > w . WindowState ) //properties to track . RegisterPersistTrigger ( nameof ( SizeChanged ) ) //when to persist data to the store . Apply ( ) ; //apply any previously stored data }

AutoMapper is an object-object mapper. Object-object mapping works by transforming an input object of one type into an output object of a different type. What makes AutoMapper interesting is that it provides some interesting conventions to take the dirty work out of figuring out how to map type A to type B.

Mapper.Initialize(cfg => { cfg.CreateMap<Foo, FooDto>(); cfg.CreateMap<Bar, BarDto>(); }); 1 2 3 4 Mapper . Initialize ( cfg = > { cfg . CreateMap < Foo , FooDto > ( ) ; cfg . CreateMap < Bar , BarDto > ( ) ; } ) ;

var fooDto = Mapper.Map<FooDto>(foo); var barDto = Mapper.Map<BarDto>(bar); 1 2 var fooDto = Mapper . Map < FooDto > ( foo ) ; var barDto = Mapper . Map < BarDto > ( bar ) ;

DI/IOC Containers:

Autofac is an Inversion Of Control (IOC) container.

var builder = new ContainerBuilder(); // Register individual components builder.RegisterInstance(new TaskRepository()) .As<ITaskRepository>(); builder.RegisterType<TaskController>(); builder.Register(c => new LogManager(DateTime.Now)) .As<ILogger>(); // Scan an assembly for components builder.RegisterAssemblyTypes(myAssembly) .Where(t => t.Name.EndsWith("Repository")) .AsImplementedInterfaces(); var container = builder.Build(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var builder = new ContainerBuilder ( ) ; // Register individual components builder . RegisterInstance ( new TaskRepository ( ) ) . As < ITaskRepository > ( ) ; builder . RegisterType < TaskController > ( ) ; builder . Register ( c = > new LogManager ( DateTime . Now ) ) . As < ILogger > ( ) ; // Scan an assembly for components builder . RegisterAssemblyTypes ( myAssembly ) . Where ( t = > t . Name . EndsWith ( "Repository" ) ) . AsImplementedInterfaces ( ) ; var container = builder . Build ( ) ;

The goal of Simple Injector is to provide .NET application developers with an easy, flexible, and fast Dependency Injection library that promotes best practice to steer developers towards the pit of success.

var container = new Container(); // 2. Configure the container (register) container.Register<IUserRepository, SqlUserRepository>(Lifestyle.Transient); container.Register<ILogger, MailLogger>(Lifestyle.Singleton); // 3. Optionally verify the container's configuration. container.Verify(); // 4. Register the container as MVC3 IDependencyResolver. DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container)); 1 2 3 4 5 6 7 8 9 10 11 12 var container = new Container ( ) ; // 2. Configure the container (register) container . Register < IUserRepository , SqlUserRepository > ( Lifestyle . Transient ) ; container . Register < ILogger , MailLogger > ( Lifestyle . Singleton ) ; // 3. Optionally verify the container's configuration. container . Verify ( ) ; // 4. Register the container as MVC3 IDependencyResolver. DependencyResolver . SetResolver ( new SimpleInjectorDependencyResolver ( container ) ) ;

It also works with ASP.NET Core.

Storage:

Dapper is a simple object mapper for your database. If you are not using Entity Framework and you are looking for a micro-ORM, Dapper is your friend.

public class Dog { public int? Age { get; set; } public Guid Id { get; set; } public string Name { get; set; } public float? Weight { get; set; } public int IgnoredProperty { get { return 1; } } } var guid = Guid.NewGuid(); var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid }); dog.Count().IsEqualTo(1); dog.First().Age.IsNull(); dog.First().Id.IsEqualTo(guid); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class Dog { public int ? Age { get ; set ; } public Guid Id { get ; set ; } public string Name { get ; set ; } public float ? Weight { get ; set ; } public int IgnoredProperty { get { return 1 ; } } } var guid = Guid . NewGuid ( ) ; var dog = connection . Query < Dog > ( "select Age = @Age, Id = @Id" , new { Age = ( int ? ) null , Id = guid } ) ; dog . Count ( ) . IsEqualTo ( 1 ) ; dog . First ( ) . Age . IsNull ( ) ; dog . First ( ) . Id . IsEqualTo ( guid ) ;

One of the best, if not the defacto Redis client to use to communicate with Redis.

Features

High performance multiplexed design, allowing for efficient use of shared connections from multiple calling threads

Abstraction over redis node configuration: the client can silently negotiate multiple redis servers for robustness and availability

Convenient access to the full redis feature-set

Full dual programming model both synchronous and asynchronous usage, without requiring “sync over async” usage of the TPL

Support for redis “cluster”

Processes and Command Line Interfaces (CLI):

Provides a wrapper around command line interface executables.

Examples:

Execute a command and get standard output as a string:

var cli = new Cli("some_cli.exe"); string result = cli.Execute("verb --option").StandardOutput; 1 2 var cli = new Cli ( "some_cli.exe" ) ; string result = cli . Execute ( "verb --option" ) . StandardOutput ;

Execute a command asynchronously:

var cli = new Cli("some_cli.exe"); string result = (await cli.ExecuteAsync("verb --option")).StandardOutput; 1 2 var cli = new Cli ( "some_cli.exe" ) ; string result = ( await cli . ExecuteAsync ( "verb --option" ) ) . StandardOutput ;

Cancel execution:

var cli = new Cli("some_cli.exe"); var cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(1)); // e.g. timeout of 1 second var output = await cli.ExecuteAsync("verb --option", cts.Token); 1 2 3 4 5 6 var cli = new Cli ( "some_cli.exe" ) ; var cts = new CancellationTokenSource ( ) ; cts . CancelAfter ( TimeSpan . FromSeconds ( 1 ) ) ; // e.g. timeout of 1 second var output = await cli . ExecuteAsync ( "verb --option" , cts . Token ) ;

Similar to CliWrap, Gt.SubProcess provides other functionality to deal with sub processes

SubProcess.Call("ls", "-l"); SubProcess.CheckCall("psql", "my-database", "fred"); // throws if exits with non 0 exit code SubProcess p = new SubProcess("ssh", "me@mysite.com") { Out = new FileStream("ssh-output.txt", FileMode.OpenOrCreate), Error = SubProcess.Capture, In = SubProcess.Pipe }; p.Wait(); Console.WriteLine(p.ErrorString); 1 2 3 4 5 6 7 8 9 10 11 12 13 SubProcess . Call ( "ls" , "-l" ) ; SubProcess . CheckCall ( "psql" , "my-database" , "fred" ) ; // throws if exits with non 0 exit code SubProcess p = new SubProcess ( "ssh" , "me@mysite.com" ) { Out = new FileStream ( "ssh-output.txt" , FileMode . OpenOrCreate ) , Error = SubProcess . Capture , In = SubProcess . Pipe } ; p . Wait ( ) ; Console . WriteLine ( p . ErrorString ) ;

Hangfire is an open-source framework that helps you to create, process and manage your background jobs, i.e. operations you don’t want to put in your request processing pipeline such as:

mass notifications/newsletter

batch import from xml, csv, json

creation of archives

firing off web hooks

deleting users

building different graphs

image/video processing

purge temporary files

recurring automated reports

database maintenance

BackgroundJob.Enqueue(() => Console.WriteLine("Simple!")); 1 BackgroundJob . Enqueue ( ( ) = > Console . WriteLine ( "Simple!" ) ) ;

Security:

NWebsec consists of several security libraries for ASP.NET applications. These libraries work together to remove version headers, control cache headers, stop potentially dangerous redirects, and set important security headers. Consult the docs to learn how they work.

Visual Studio Addon:



Roselyn Security Guard:

Security Guard is a set of Roslyn analyzers that aim to help security audits on .NET applications. The idea is to have an analyzer (security), which can try to detect potential vulnerabilities in code like SQL injection, csrf, cryptography weaknesses, hard coded passwords, cross side scripting (XSS), etc.

Compression:

Zip, GZip, Tar and BZip2 library written entirely in C# for the .NET platform.

Benchmarking:

Curious about performance? This library will help you micro benchmark certain parts of your application.

using System; using System.Security.Cryptography; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; namespace MyBenchmarks { public class Md5VsSha256 { private const int N = 10000; private readonly byte[] data; private readonly SHA256 sha256 = SHA256.Create(); private readonly MD5 md5 = MD5.Create(); public Md5VsSha256() { data = new byte[N]; new Random(42).NextBytes(data); } [Benchmark] public byte[] Sha256() => sha256.ComputeHash(data); [Benchmark] public byte[] Md5() => md5.ComputeHash(data); } public class Program { public static void Main(string[] args) { var summary = BenchmarkRunner.Run<Md5VsSha256>(); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 using System ; using System . Security . Cryptography ; using BenchmarkDotNet . Attributes ; using BenchmarkDotNet . Running ; namespace MyBenchmarks { public class Md5VsSha256 { private const int N = 10000 ; private readonly byte [ ] data ; private readonly SHA256 sha256 = SHA256 . Create ( ) ; private readonly MD5 md5 = MD5 . Create ( ) ; public Md5VsSha256 ( ) { data = new byte [ N ] ; new Random ( 42 ) . NextBytes ( data ) ; } [ Benchmark ] public byte [ ] Sha256 ( ) = > sha256 . ComputeHash ( data ) ; [ Benchmark ] public byte [ ] Md5 ( ) = > md5 . ComputeHash ( data ) ; } public class Program { public static void Main ( string [ ] args ) { var summary = BenchmarkRunner . Run < Md5VsSha256 > ( ) ; } } }

Scientist.net is a great library to test out the outcome of new path/refactoring of your code without having to actually use in production. It lets you try your new code while using your old code.

public bool CanAccess(IUser user) { return Scientist.Science<bool>("widget-permissions", experiment => { experiment.Use(() => IsCollaborator(user)); // old way experiment.Try(() => HasAccess(user)); // new way }); // returns the control value } 1 2 3 4 5 6 7 8 public bool CanAccess ( IUser user ) { return Scientist . Science < bool > ( "widget-permissions" , experiment = > { experiment . Use ( ( ) = > IsCollaborator ( user ) ) ; // old way experiment . Try ( ( ) = > HasAccess ( user ) ) ; // new way } ) ; // returns the control value }

You can then implement an in-memory publisher (logger) that will log the results of the before and after so you can test your results to see if they match your expectations.

Parsers:

It may happen that sometimes you need to parse stuff. Here are some parses that can be helpful.

This is an agile HTML parser that builds a read/write DOM and supports plain XPATH or XSLT (you actually don’t HAVE to understand XPATH nor XSLT to use it, don’t worry…).

// From File var doc = new HtmlDocument(); doc.Load(filePath); // From String var doc = new HtmlDocument(); doc.LoadHtml(html); // From Web var url = "http://html-agility-pack.net/"; var web = new HtmlWeb(); var doc = web.Load(url); // With XPath var value = doc.DocumentNode .SelectNodes("//td/input") .First() .Attributes["value"].Value; // With LINQ var nodes = doc.DocumentNode.Descendants("input") .Select(y => y.Descendants() .Where(x => x.Attributes["class"].Value == "box")) .ToList(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // From File var doc = new HtmlDocument ( ) ; doc . Load ( filePath ) ; // From String var doc = new HtmlDocument ( ) ; doc . LoadHtml ( html ) ; // From Web var url = "http://html-agility-pack.net/" ; var web = new HtmlWeb ( ) ; var doc = web . Load ( url ) ; // With XPath var value = doc . DocumentNode . SelectNodes ( "//td/input" ) . First ( ) . Attributes [ "value" ] . Value ; // With LINQ var nodes = doc . DocumentNode . Descendants ( "input" ) . Select ( y = > y . Descendants ( ) . Where ( x = > x . Attributes [ "class" ] . Value == "box" ) ) . ToList ( ) ;

AngleSharp is a .NET library that gives you the ability to parse angle bracket based hyper-texts like HTML, SVG, and MathML.

// Setup the configuration to support document loading var config = Configuration.Default.WithDefaultLoader(); // Load the names of all The Big Bang Theory episodes from Wikipedia var address = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes"; // Asynchronously get the document in a new context using the configuration var document = await BrowsingContext.New(config).OpenAsync(address); // This CSS selector gets the desired content var cellSelector = "tr.vevent td:nth-child(3)"; // Perform the query to get all cells with the content var cells = document.QuerySelectorAll(cellSelector); // We are only interested in the text - select it with LINQ var titles = cells.Select(m => m.TextContent); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // Setup the configuration to support document loading var config = Configuration . Default . WithDefaultLoader ( ) ; // Load the names of all The Big Bang Theory episodes from Wikipedia var address = "https://en.wikipedia.org/wiki/List_of_The_Big_Bang_Theory_episodes" ; // Asynchronously get the document in a new context using the configuration var document = await BrowsingContext . New ( config ) . OpenAsync ( address ) ; // This CSS selector gets the desired content var cellSelector = "tr.vevent td:nth-child(3)" ; // Perform the query to get all cells with the content var cells = document . QuerySelectorAll ( cellSelector ) ; // We are only interested in the text - select it with LINQ var titles = cells . Select ( m = > m . TextContent ) ;

One of the most popular, and defacto json serializer/deserializer in ASP.NET core.

// SERIALIZE Product product = new Product(); product.Name = "Apple"; product.Expiry = new DateTime(2008, 12, 28); product.Sizes = new string[] { "Small" }; string json = JsonConvert.SerializeObject(product); // { // "Name": "Apple", // "Expiry": "2008-12-28T00:00:00", // "Sizes": [ // "Small" // ] // } // DESERIALIZE string json = @"{ 'Name': 'Bad Boys', 'ReleaseDate': '1995-4-7T00:00:00', 'Genres': [ 'Action', 'Comedy' ] }"; Movie m = JsonConvert.DeserializeObject<Movie>(json); string name = m.Name; // Bad Boys 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 // SERIALIZE Product product = new Product ( ) ; product . Name = "Apple" ; product . Expiry = new DateTime ( 2008 , 12 , 28 ) ; product . Sizes = new string [ ] { "Small" } ; string json = JsonConvert . SerializeObject ( product ) ; // { // "Name": "Apple", // "Expiry": "2008-12-28T00:00:00", // "Sizes": [ // "Small" // ] // } // DESERIALIZE string json = @"{ 'Name': 'Bad Boys', 'ReleaseDate': '1995-4-7T00:00:00', 'Genres': [ 'Action', 'Comedy' ] }" ; Movie m = JsonConvert . DeserializeObject < Movie > ( json ) ; string name = m . Name ; // Bad Boys

CSVHelper is a library to read and write CSVFiles. No need to have a dependence on oledb jet engine or make your own parser.

var csv = new CsvReader( textReader ); var records = csv.GetRecords<MyClass>(); 1 2 var csv = new CsvReader ( textReader ) ; var records = csv . GetRecords < MyClass > ( ) ;

Logging:

Simple .NET logging that focuses on fully-structured events. You can think of it as a simple ETW.

NLog is a rich logging platform to help your log stuff.

Communication:

SSH.NET is a Secure Shell (SSH-2) library for .NET

var connectionInfo = new ConnectionInfo("sftp.foo.com", "guest", new PasswordAuthenticationMethod("guest", "pwd"), new PrivateKeyAuthenticationMethod("rsa.key")); using (var client = new SftpClient(connectionInfo)) { client.Connect(); } 1 2 3 4 5 6 7 8 var connectionInfo = new ConnectionInfo ( "sftp.foo.com" , "guest" , new PasswordAuthenticationMethod ( "guest" , "pwd" ) , new PrivateKeyAuthenticationMethod ( "rsa.key" ) ) ; using ( var client = new SftpClient ( connectionInfo ) ) { client . Connect ( ) ; }

FluentFTP is a fully managed FTP and FTPS library for .NET & .NET Standard, optimized for speed. It provides extensive FTP commands, File uploads/downloads, SSL/TLS connections, Automatic directory listing parsing, File hashing/checksums, File permissions/CHMOD, FTP proxies, UTF-8 support, Async/await support and more.

/ create an FTP client FtpClient client = new FtpClient("123.123.123.123"); // if you don't specify login credentials, we use the "anonymous" user account client.Credentials = new NetworkCredential("david", "pass123"); // begin connecting to the server client.Connect(); // get a list of files and directories in the "/htdocs" folder foreach (FtpListItem item in client.GetListing("/htdocs")) { // if this is a file if (item.Type == FtpFileSystemObjectType.File){ // get the file size long size = client.GetFileSize(item.FullName); } // get modified date/time of the file or folder DateTime time = client.GetModifiedTime(item.FullName); // calculate a hash for the file on the server side (default algorithm) FtpHash hash = client.GetHash(item.FullName); } // upload a file client.UploadFile(@"C:\MyVideo.mp4", "/htdocs/big.txt"); // rename the uploaded file client.Rename("/htdocs/big.txt", "/htdocs/big2.txt"); // download the file again client.DownloadFile(@"C:\MyVideo_2.mp4", "/htdocs/big2.txt"); // delete the file client.DeleteFile("/htdocs/big2.txt"); // delete a folder recursively client.DeleteDirectory("/htdocs/extras/"); // check if a file exists if (client.FileExists("/htdocs/big2.txt")){ } // check if a folder exists if (client.DirectoryExists("/htdocs/extras/")){ } // upload a file and retry 3 times before giving up client.RetryAttempts = 3; client.UploadFile(@"C:\MyVideo.mp4", "/htdocs/big.txt", FtpExists.Overwrite, false, FtpVerify.Retry); // disconnect! good bye! client.Disconnect(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 / create an FTP client FtpClient client = new FtpClient ( "123.123.123.123" ) ; // if you don't specify login credentials, we use the "anonymous" user account client . Credentials = new NetworkCredential ( "david" , "pass123" ) ; // begin connecting to the server client . Connect ( ) ; // get a list of files and directories in the "/htdocs" folder foreach ( FtpListItem item in client . GetListing ( "/htdocs" ) ) { // if this is a file if ( item . Type == FtpFileSystemObjectType . File ) { // get the file size long size = client . GetFileSize ( item . FullName ) ; } // get modified date/time of the file or folder DateTime time = client . GetModifiedTime ( item . FullName ) ; // calculate a hash for the file on the server side (default algorithm) FtpHash hash = client . GetHash ( item . FullName ) ; } // upload a file client . UploadFile ( @ "C:\MyVideo.mp4" , "/htdocs/big.txt" ) ; // rename the uploaded file client . Rename ( "/htdocs/big.txt" , "/htdocs/big2.txt" ) ; // download the file again client . DownloadFile ( @ "C:\MyVideo_2.mp4" , "/htdocs/big2.txt" ) ; // delete the file client . DeleteFile ( "/htdocs/big2.txt" ) ; // delete a folder recursively client . DeleteDirectory ( "/htdocs/extras/" ) ; // check if a file exists if ( client . FileExists ( "/htdocs/big2.txt" ) ) { } // check if a folder exists if ( client . DirectoryExists ( "/htdocs/extras/" ) ) { } // upload a file and retry 3 times before giving up client . RetryAttempts = 3 ; client . UploadFile ( @ "C:\MyVideo.mp4" , "/htdocs/big.txt" , FtpExists . Overwrite , false , FtpVerify . Retry ) ; // disconnect! good bye! client . Disconnect ( ) ;

CoreFTP is a simple .NET FTP library written entirely in C#, it is targeted at netstandard 1.6, meaning it will run under .NET Core (which is also where it derives its name) and the full .NET framework. This package was inspired due to a lack of packages providing FTP functionality compiled with support for the netstandard API surface.

using ( var ftpClient = new FtpClient( new FtpClientConfiguration { Host = "localhost", Username = "user", Password = "password" } ) ) { var tempFile = new FileInfo( "C:\\test.png" ); await ftpClient.LoginAsync(); using ( var ftpReadStream = await ftpClient.OpenFileReadStreamAsync( "test.png" ) ) { using ( var fileWriteStream = tempFile.OpenWrite() ) { await ftpReadStream.CopyToAsync( fileWriteStream ); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using ( var ftpClient = new FtpClient ( new FtpClientConfiguration { Host = "localhost" , Username = "user" , Password = "password" } ) ) { var tempFile = new FileInfo ( "C:\\test.png" ) ; await ftpClient . LoginAsync ( ) ; using ( var ftpReadStream = await ftpClient . OpenFileReadStreamAsync ( "test.png" ) ) { using ( var fileWriteStream = tempFile . OpenWrite ( ) ) { await ftpReadStream . CopyToAsync ( fileWriteStream ) ; } } }

The automatic type-safe REST library for .NET Core, Xamarin and .NET. It is a library heavily inspired by Square’s Retrofit library, and it turns your REST API into a live interface.

public interface IGitHubApi { [Get("/users/{user}")] Task<User> GetUser(string user); } 1 2 3 4 5 public interface IGitHubApi { [ Get ( "/users/{user}" ) ] Task < User > GetUser ( string user ) ; }