10 Hidden Gems for .NET Developers

Abnoan Muniz
11 min readFeb 7, 2023

--

Tools You Haven’t Heard Of

Photo by Dan Farrell on Unsplash

As a .NET developer, you always look for new tools and libraries to help you build better and more efficient applications. However, with so many options available, it can be challenging to find hidden gems that are worth your time and effort. In this blog, we will explore ten hidden gems for .NET developers that you might not have heard of. From libraries for performing CRUD operations to high-performance gRPC frameworks, these tools will help you streamline your development process and improve the overall quality of your applications. Whether you are a seasoned .NET developer or just starting out, this post will provide valuable insights into some of the tools and libraries available for .NET development. So, let’s dive in and discover the hidden gems that will take your .NET development skills to the next level!

1: Humanizer

We developers often work with data intended for machines, not humans. It can be challenging to present that data in a way that is easy to understand and engage with. This is where Humanizer comes in — a library for .NET that helps you humanize, transform, and format data in a human-readable way.

Humanizer makes it easy to convert numbers into words, format dates and times in a readable manner, and manipulate strings in a way that makes sense to users. It provides a simple, intuitive, consistent API that can transform data from its raw form into something manageable for humans to understand.

For example, let’s say you need to display the number of days between two dates in a user-friendly manner. With Humanizer, you can write a simple line of code to achieve this:

var daysBetween = (endDate - startDate).TotalDays;
var humanizedDays = daysBetween.ToWords();

The output of the above code would be something like “ninety-seven days.” This is much easier for users to understand than a plain number, adding extra professionalism to your application.

Another great feature of Humanizer is its ability to handle plurals and singular forms of words. This makes it easy to display information in a way that is grammatically correct and user-friendly.

If you’re looking for a tool that can help you humanize your data and make it easier for your users to understand, then Humanizer is worth a closer look. Check it out for yourself by visiting the Humanizer repository on GitHub.

2: Markdig

Markdown is a popular text format widely used in web development, documentation, and even writing. It provides a simple and clean way to write formatted text without the need for complex HTML tags. However, processing Markdown can be challenging, especially if you need to do it in an efficient and extensible manner. This is where Markdig comes in — a fast and extensible Markdown processor for .NET.

Markdig is designed to be fast, easy to use, and highly customizable. It provides a comprehensive set of features for processing Markdown, including support for most common extensions, such as table of contents, task lists, and more. Additionally, it provides a robust API that makes extending and customizing its functionality easy.

One of the best things about Markdig is its performance. It is designed to be fast, even when working with large amounts of text. This makes it ideal for use in applications requiring real-time Markdowns processing, such as forums, wikis, and document management systems.

Here’s a simple example of how to use Markdig to process a string of Markdown text and convert it to HTML:

using Markdig;

var markdown = @"# Hello, World!
This is a sample of *Markdown* text.

- This is a bullet point
- Another bullet point

1. This is a numbered list
2. Another item in the list

You can even include [links](https://www.example.com) and images:

![Image Alt Text](https://www.example.com/image.jpg)";

var html = Markdown.ToHtml(markdown);

Console.WriteLine(html);

This code uses the Markdown.ToHtml method to convert a stringof Markdown text into its equivalent HTML representation. The output of this code would be something like this:

<h1>Hello, World!</h1>
<p>This is a sample of <em>Markdown</em> text.</p>
<ul>
<li>This is a bullet point</li>
<li>Another bullet point</li>
</ul>
<ol>

If you’re looking for a tool that can help you process Markdown quickly, efficiently, and extensible manner, then Markdig is worth a closer look. Check it out for yourself by visiting the Markdig repository on GitHub.

3: FluentValidation

A library for creating and running validation rules for .NET objects! This library provides a simple and fluent API for defining and validating business rules on your objects, making it an excellent tool for building reliable and maintainable applications.

Here’s a simple example of how you can use FluentValidation to validate an object in C#:

using FluentValidation;

public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(x => x.Surname).NotEmpty();
RuleFor(x => x.Forename).NotEmpty().WithMessage("Please specify a first name");
RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
RuleFor(x => x.Address).Length(20, 250);
RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
}

private bool BeAValidPostcode(string postcode)
{
// custom postcode validation logic goes here
}
}

This example defines a CustomerValidator class that extends AbstractValidator<Customer>, where Customer is the type of object you want to validate. The validation rules are defined using the RuleFor method specifies the property to validate and the application validation logic. In this example, the rules include not allowing empty surnames or forenames, ensuring that the discount is not equal to zero if the customer has a discount, ensuring the address is within a certain length, and ensuring that the postcode is valid.

You can use the validator like this:

var customer = new Customer();
var validator = new CustomerValidator();
var results = validator.Validate(customer);

if (!results.IsValid)
{
// handle validation errors
}

This code creates an instance of the CustomerValidator class and uses the Validate method to validate a Customer object. The Validate method returns a ValidationResult object that you can use to check if the object is valid and retrieve any validation errors encountered.

FluentValidation is an excellent tool for ensuring the quality and consistency of your data, and its easy-to-use API makes it a must-have for any .NET developer. Check it out at fluentvalidation.net.

4: Ocelot

An API gateway for .NET applications! This library provides a unified entry point for your API and makes it easier to manage cross-cutting concerns such as authentication, rate limiting, and request routing.

Here’s a simple example of how you can use Ocelot in your .NET application to route requests:

using Ocelot.Configuration.File;

var configuration = new FileConfiguration
{
ReRoutes = new List<FileReRoute>
{
new FileReRoute
{
DownstreamPathTemplate = "/api/v1/values",
DownstreamScheme = "http",
DownstreamHostAndPorts = new List<FileHostAndPort>
{
new FileHostAndPort
{
Host = "localhost",
Port = 5010,
}
},
UpstreamPathTemplate = "/values",
},
},
};

var builder = new WebHostBuilder();

builder.ConfigureServices(s => {
s.AddOcelot(configuration);
});

builder.UseStartup<Startup>();

var host = builder.Build();

host.Run();

In this example, we create an FileConfiguration object that defines the routing rules for our API. We use the FileReRoute class to specify the downstream path template, scheme, host and port, and upstream path template. In this case, we're routing all requests to the /api/v1/values endpoint to the localhost on port 5010.

We then configure Ocelot in our ASP.NET Core application by calling AddOcelot on IServiceCollection in the ConfigureServices method. The UseStartup method is used to specify the startup class for our application. Finally, we build and run the web host using the Build and Run methods.

Ocelot makes it easy to manage your API routes and provides a flexible and scalable solution for managing cross-cutting concerns in your .NET application. Check it out at ocelot.readthedocs.io.

Unfortunately, the project is no longer being actively maintained, which means that users may not receive the support and updates that they need to keep their applications running smoothly. While it’s still a great tool, the lack of maintenance may be a cause for concern for some users. However, there are still many other great API gateway options available for .NET, so developers should not be discouraged from exploring other solutions that are actively maintained and updated.

5: Polly

A .NET resilience and the transient-fault-handling library! This library makes it easy to express policies for your .NET applications, such as retry, circuit breaker, timeout, and more.

Here’s a simple example of how you can use Polly to add retry behavior to your .NET application:

using System;
using System.Net.Http;
using Polly;

var policy = Policy
.Handle<HttpRequestException>()
.Retry(3, (exception, retryCount) =>
{
Console.WriteLine($"Retry attempt {retryCount}");
});

var client = new HttpClient();

policy.Execute(() =>
{
var response = client.GetAsync("https://www.example.com").Result;

if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException();
}
});

In this example, we create an Policy object using the Handle and Retry methods. The Handle method is used to specify the exception type that we want to retry, in this case, HttpRequestException. The Retry method is used to specify the number of retry attempts and a delegate that will be called after each retry attempt.

We then use the Execute method on the policy object to execute the code we want to retry. In this case, we're using an HttpClient object to make a GET request to an example URL. If the request fails and returns an unsuccessful status code, we throw an HttpRequestException to trigger the retry behavior.

Polly makes adding resilience and fault tolerance to your .NET applications easy. Check it out at thepollyproject.org/

6: MagicOnion

A high-performance gRPC framework for .NET! This framework makes it easy to build fast and scalable microservices in .NET.

Here’s a simple example of how you can use MagicOnion to build a gRPC service in .NET:

using Grpc.Core;
using MagicOnion;
using MagicOnion.Server;

public interface IMyService : IService<IMyService>
{
Task<int> SumAsync(int x, int y);
}

public class MyService : ServiceBase<IMyService>, IMyService
{
public async Task<int> SumAsync(int x, int y)
{
return x + y;
}
}

public class Program
{
public static void Main(string[] args)
{
var service = MagicOnionEngine.BuildServerServiceDefinition(isReturnExceptionStackTraceInErrorDetail: true);

var server = new Server
{
Services = { service },
Ports = { new ServerPort("localhost", 12345, ServerCredentials.Insecure) }
};

server.Start();

Console.WriteLine("Server listening on port 12345");
Console.WriteLine("Press any key to stop the server...");
Console.ReadKey();

server.ShutdownAsync().Wait();
}
}

In this example, we define an interface for our gRPC service, IMyServicewhich extends MagicOnion.IService<IMyService>. This interface contains the methods that our service will expose over gRPC.

We then implement this interface in a class, MyServicewhich implements the SumAsync method to return the sum of two integers.

Next, in the Main method, we build the server definition using MagicOnionEngine.BuildServerServiceDefinition and specify the port to listen on. Finally, we start the server using the Start method and shut it down when the user presses any key.

MagicOnion makes it easy to build fast and scalable gRPC services in .NET. Check it out for yourself by visiting the repository on GitHub.

7: MediatR

A simple, unopinionated library for building event-driven systems in .NET! This library makes it easy to build event-driven systems with a clean and simple API.

Here’s a simple example of how you can use MediatR to handle a request in .NET:

using MediatR;
using System.Threading;
using System.Threading.Tasks;

public class Ping : IRequest<string> { }

public class PingHandler : IRequestHandler<Ping, string>
{
public Task<string> Handle(Ping request, CancellationToken cancellationToken)
{
return Task.FromResult("Pong");
}
}

public class Program
{
public static async Task Main(string[] args)
{
var mediator = new Mediator(type => new PingHandler());

var result = await mediator.Send(new Ping());

Console.WriteLine(result);
}
}

In this example, we define a request, Ping, which is a simple message that our system will handle.

We then define a handler, PingHandler, which implements the MediatR.IRequestHandler<Ping, string> interface. This handler implements the Handle method to return a string "Pong" when it receives a Ping request.

Finally, in the Main method, we create an instance of the MediatR.Mediator class and use the Send method to send the Ping request and get the result.

MediatR makes it easy to build event-driven systems in .NET. Check it out on GitHub.

8: Scrutor

A library for scanning assemblies and automatically registering components with the .NET Dependency Injection framework! This library makes managing your dependencies easy and ensures all necessary components are registered and available.

Here’s a simple example of how you can use Scrutor to register components with the Dependency Injection framework:

using Microsoft.Extensions.DependencyInjection;
using Scrutor;

public class Program
{
public static void Main(string[] args)
{
var services = new ServiceCollection();

services.Scan(scan => scan
.FromAssemblyOf<Program>()
.AddClasses(classes => classes.AssignableTo<ITransientService>())
.AsImplementedInterfaces()
.WithTransientLifetime());

var provider = services.BuildServiceProvider();
}
}

public interface ITransientService { }

public class TransientService : ITransientService { }

In this example, we create a new instance of the Microsoft.Extensions.DependencyInjection.ServiceCollection class and use the Scan method to scan the assembly containing the Program class.

The AddClasses method is used to specify that we want to register all classes that implement the ITransientService interface. The AsImplementedInterfaces method is used to register these classes as their implemented interfaces, and the WithTransientLifetime method is used to specify that these components should have a transient lifetime.

Finally, we use the BuildServiceProvider method to create a Microsoft.Extensions.DependencyInjection.ServiceProvider instance that can be used to resolve dependencies.

Scrutor makes managing your dependencies easy and ensures that all necessary components are registered with the Dependency Injection framework. Check it out on GitHub.

9: Dapper

A fast and simple ORM for .NET! This library provides a lightweight, simple way to interact with databases in .NET applications.

Here’s a simple example of how you can use Dapper to query a database:

using System.Data.SqlClient;
using Dapper;

public class Program
{
public static void Main(string[] args)
{
using (var connection = new SqlConnection("Data Source=(local);Initial Catalog=TestDB;Integrated Security=True"))
{
connection.Open();

var query = "SELECT * FROM Customers";
var customers = connection.Query<Customer>(query).ToList();
}
}
}

public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}

In this example, we create a new instance of the System.Data.SqlClient.SqlConnection class and use the Query method to execute a query against a database. The Query method returns a System.Collections.Generic.IEnumerable<T> of Customer objects that can be used in our application.

Dapper provides a fast and simple way to interact with databases in .NET applications. Check it out on GitHub.

10: Bogus

A library for generating fake data in .NET! This library is a simple and convenient way to generate test data for your .NET applications.

Here’s a simple example of how you can use Bogus to generate fake data:

using Bogus;

public class Program
{
public static void Main(string[] args)
{
var customerGenerator = new Faker<Customer>()
.RuleFor(x => x.Name, f => f.Name.FullName())
.RuleFor(x => x.Email, f => f.Internet.Email());

var customers = customerGenerator.Generate(10);
}
}

public class Customer
{
public string Name { get; set; }
public string Email { get; set; }
}

In this example, we use the Faker class to create a generator for Customer objects. The RuleFor method allows us to specify the values for each property in our Customer class. Finally, we use the Generate method to generate 10 instances of the Customer class.

Bogus is a simple and convenient way to generate test data for your .NET applications. Check it out at Gihub.

Thanks for reading! Before you go:

If you find this article helpful, please consider giving claps and following me. 👏👉

Take advantage of my other stories! 👇🔽

--

--

Abnoan Muniz

Senior .NET Developer, Passionate about problem-solving. Support me: https://ko-fi.com/abnoanmuniz, Get in touch: linktr.ee/AbnoanM