Telerik blogs

Here are five more packages that will help you jump-start your learning in ASP.NET Core.

NuGet packages play a vital role in ASP.NET Core development, providing an easy way to manage dependencies, improve efficiency, ensure security and speed up the development process—making them indispensable for building modern and robust web applications, and saving time and effort.

In the first part of Essential NuGet Packages for Beginners, we explored five packages that help simplify the development of a web application by automatically generating databases and tables, registration of information, validations and documentation.

In this second part, we will check out five more packages that will help you to evolve an application, making it easier to maintain and guarantee its quality.

By the end of the post, you can develop robust and good-quality web applications in ASP.NET Core using five NuGet packages: Dapper, RestSharp, Newtonsoft.json, XUnit and Humanizer.

Prerequisites

  • To create the application you must have .NET version 7 or higher installed.
  • You also need a terminal to run .NET commands.

Creating the Application and Installing the NuGet Packages

For the example in this post, we will create a minimal API. You can access the application source code here.

To create the API base template, run the command below in the terminal:

dotnet new web -o CustomerManagement

Open the application with your favorite IDE—this post uses Visual Studio Code.

Now, let’s create the classes that will represent the application’s entity. Inside the project, create a new folder called “Models” and, inside that, create the classes and the record below:

  • Customer
namespace CustomerManagement.Models;

public class Customer
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public Address Address { get; set; }

    public Customer(Guid id, string name, string email, Address address)
    {
        Id = id;
        Name = name;
        Email = email;
        Address = address;
    }
}
  • Address
namespace CustomerManagement.Models;

public class Address
{
    public Guid Id { get; set; }
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
    public Guid CustomerId { get; set; }

    public Address(Guid id, string street, string city, string state, string postalCode, string country, Guid customerId)
    {
        Street = street;
        City = city;
        State = state;
        PostalCode = postalCode;
        Country = country;
        CustomerId = customerId;
    }
}
  • CustomersByCountryDto
namespace CustomerManagement.Models;

public record CustomersByCountryDto(Guid Id, string Name, string Email);

Now let’s install the first NuGet package, an excellent ORM for working with data, Dapper.

1. Dapper

Dapper is an open-source object-relational mapping (ORM) library developed for .NET that aims to facilitate access and manipulation of data in relational databases through a lightweight and high-performance alternative to traditional ORM frameworks.

Dapper is widely used as it allows developers to run SQL queries directly in their applications, mapping the results to C# objects efficiently and quickly. Using reflection and metaprogramming capabilities, Dapper minimizes overhead and improves efficiency, making it a popular choice for projects that require optimized performance in .NET applications.

So, use the command below to install Dapper in the project:

dotnet add package Dapper

An important point is that in this example we will use MySQL as the database, so you need to have a MySQL server running in your local environment. This post does not teach you how to configure MySQL locally, but there are several tutorials on the internet teaching how to do this. The GitHub project repository has a docker configuration file if you prefer to use MySQL in a docker container.

Another required NuGet package is MySql.Data, used to perform SQL operations on the MySQL database. Then, use the command below to download MySQL.Data dependencies into the project.

dotnet add package MySql.Data

To use Dapper, let’s create a repository class to connect to the database. Then create a new folder called “Data” and inside it create the interface and class below:

  • ICustomerRepository
using CustomerManagement.Models;

public interface ICustomerRepository
{
    Task<List<CustomersByCountryDto>> FindByCountry(string country);
}
  • CustomerRepository
using System.Data;
using CustomerManagement.Models;
using Dapper;
using Microsoft.Extensions.Options;
using MySql.Data.MySqlClient;

namespace CustomerManagement.Data;

public class CustomerRepository : ICustomerRepository
{
    private readonly IDbConnection _db;
    public CustomerRepository(IOptions<ConnectionString> connectionString)
    {
        _db = new MySqlConnection(connectionString.Value.ProjectConnection);
    }

    public async Task<List<CustomersByCountryDto>> FindByCountry(string country)
    {
        string query = @"select 
                            c.id,
                            c.name,
                            c.email
                        from customers c
                        inner join addresses a
                        on c.id = a.customerId
                        where a.country = @Country";

        var customersByCountry = await _db.QueryAsync<CustomersByCountryDto>(query, new { Country = country });
        return customersByCountry.ToList();
    }
}

Note that in the code above we are passing the database connection string to the class constructor and creating a method to return data from the database customers.

Note also that we declare the SQL code in a string and pass it to Dapper’s QueryAsync() method to execute.

The database and tables don’t exist yet so you can use the SQL script below to create the database and tables, and insert some sample data. Just run them on your local MySQL server.

  • Script to create the database and tables
-- Create customer_db database
CREATE DATABASE IF NOT EXISTS customer_db;

-- Using customer_db database
USE customer_db;

-- Create customers table 
CREATE TABLE IF NOT EXISTS customers (
    id CHAR(36) PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) NOT NULL
);

-- Create addresses table 
CREATE TABLE IF NOT EXISTS addresses (
    id CHAR(36) PRIMARY KEY,
    street VARCHAR(100) NOT NULL,
    city VARCHAR(50) NOT NULL,
    state VARCHAR(50) NOT NULL,
    postalCode VARCHAR(20) NOT NULL,
    country VARCHAR(50) NOT NULL,
    customerId CHAR(36) NOT NULL,
    CONSTRAINT fk_addresses_customers FOREIGN KEY (customerId)
    REFERENCES customers(Id)
    ON DELETE CASCADE
);
  • Script to insert the data
-- Using the customer_db database
USE customer_db;

-- Insert in customers
INSERT INTO customers (id, name, email)
VALUES
    ('ba7991a3-22c3-473b-8421-676b714c2181', 'John Doe', 'john.doe@example.com'),
    ('8bc247b0-bfa8-44d9-b1ff-0533655d74c9', 'Jane Smith', 'jane.smith@example.com'),
    ('81f4a9ef-2cc3-422d-89fd-6ba7c4a5fda3', 'David D. Clifford', 'david.cli@example.com');

-- Insert in addresses
INSERT INTO addresses (id, street, city, state, postalCode, country, customerId)
VALUES
    ('b41a07a6-6cfc-4195-8579-c6bf0b605ea1', '2592 Boundary Street', 'Jacksonville', 'Florida', '32202', 'USA', 'ba7991a3-22c3-473b-8421-676b714c2181'),
    ('6b155fb2-3be8-4fdc-8c00-947670d28644', '456 Elm Avenue', 'Hythe', 'Alberta', '67890', 'Canada', '8bc247b0-bfa8-44d9-b1ff-0533655d74c9'),
    ('a432b04c-c380-46f9-bbc2-7b68240c557d', '1224 James Martin Circle', 'Columbus', 'Ohio', '43212', 'USA', '81f4a9ef-2cc3-422d-89fd-6ba7c4a5fda3');

The next step is to add the connection string and repository class configuration. In the “appsettings.json” file found at the root of the project, add the code below. Remember to change it with your MySQL credentials.

"ConnectionStrings": {
  "ProjectConnection": "host=localhost; port=3306; database=customer_db; user=YOURMYSQLUSER; password=YOURMSQLPASSWORD;"
  },

Now in the “Program.cs” file, add the following lines of code just below where the “builder” variable is created:

builder.Services.AddTransient<ICustomerRepository, CustomerRepository>();
builder.Services.Configure<ConnectionString>(builder.Configuration.GetSection("ConnectionStrings"));

The last step to test the API is to create an endpoint to access the repository and return the data. Still in the “Program.cs” file, add the code below:

app.MapGet("/v1/customers/by_country/{country}", async ([FromServices] ICustomerRepository repository, string country) =>
{
    var customers = await repository.FindByCountry(country);
    return customers.Any() ? Results.Ok(customers) : Results.NotFound("No records found");
})
.WithName("FindCustomersByCountry");

To test the application, just run the command below in the terminal:

dotnet run --urls=http://localhost:5000

If you access in your browser the address http://localhost:5000/v1/customers/by_country/USA, you will have the following result:

Running the application

Our API is functional and returning data correctly, but imagine that you need to call the endpoint /v1/customers/by_country through another API. How would that be done? There are several ways, and one of the simplest and most straightforward is through a NuGet package called RestSharp.

2. RestSharp

RestSharp is a NuGet package that makes communicating with RESTful APIs simpler and more efficient. Through its advanced set of features, RestSharp allows developers to easily send HTTP requests such as GET, POST and DELETE and also supports manipulation of data in standard formats such as JSON and XML.

In addition, the library facilitates object serialization and deserialization, making application integration with web services more fluid by automatically converting data between object formats and data structures.

RestSharp allows developers to focus on application logic while the library handles the complex aspects of inter-API communication.

To implement RestSharp, we are going to create a new API. So use the command below in the terminal:

dotnet new web -o CustomerProcess

Then open the project with your IDE and run the following command in the terminal to download the RestSharp dependency in the project:

dotnet add package RestSharp

Now let’s implement the communication with the API we created earlier. For that, in the “CustomerProcess” project, replace the existing code in the “Program.cs” file with the following code:

using RestSharp;
using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IRestClient, RestClient>();
var app = builder.Build();

app.MapGet("/v1/get_customers_by_country/{country}", async ([FromServices] IRestClient restClient, string country) =>
{
    var request = new RestRequest($"http://localhost:5000/v1/customers/by_country/{country}", Method.Get);
    var response = await restClient.ExecuteAsync(request);

    if (response.IsSuccessful)
        return Results.Ok(response.Content);
    else
        return Results.BadRequest($"Error: {response.StatusCode}");
});

app.Run();

In the code above we are creating the RestSharp configuration through the AddTransient<IRestClient, RestClient>() method. Then we define an endpoint to access the API, which creates a new object of type RestRequest passing the route of the API created earlier.

In real-world scenarios, external API routes are typically accessed through files with a “.env” extension, but to keep things simple, in this example, we declare the route directly in the object.

Finally, we use RestSharp’s ExecuteAsync() method to execute the request and return the result.

To test, make sure that the API of the “CustomerManagement” project is running on port 5000, and then run the following command in the terminal of the “CustomerProcess” project:

dotnet run --urls=http://localhost:5054

Then, in your browser, go to the following address:

http://localhost:5054/v1/get_customers_by_country/USA

And you should have the following result:

Accessing API with RestSharp

Note that we can access the API that returns customer data easily by passing the route to be accessed to RestSharp. In the post scenario, we have only one API, but imagine if we had dozens. It would be very simple to implement multiple requests.

In this post, we are just looking at a simple implementation of RestSharp, but there are many other resources available. If you wish, you can explore them on the official RestSharp website.

3. Newtonsoft.Json

Despite being simple, Newtonsoft.Json is very useful, as it facilitates the work of web developers who need to deal with request and response data on a daily basis.

Furthermore, the ability to customize the serialization and deserialization process, along with advanced features such as LINQ support, make the package a versatile and powerful tool for manipulating JSON data in a variety of development scenarios.

To download NewtonSoft.Json into the “CustomerProcess” project, use the following command:

dotnet add package Newtonsoft.Json

Now, still in the “CustomerProcess” project, create a new folder called “Models” and, inside it, create the record below:

  • Customer
namespace CustomerProcess.Models;

public record Customer(Guid Id, string Name, string Email);

Then in the “Program.cs” file, replace the existing code with the following code:

using RestSharp;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using CustomerProcess.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddTransient<IRestClient, RestClient>();
var app = builder.Build();

app.MapGet("/v1/get_customers_by_country/{country}", async ([FromServices] IRestClient restClient, string country) =>
{
    var request = new RestRequest($"http://localhost:5000/v1/customers/by_country/{country}", Method.Get);
    var response = await restClient.ExecuteAsync(request);
    var customers = JsonConvert.DeserializeObject<List<Customer>>(response.Content);

    if (response.IsSuccessful)
        return Results.Ok(customers);
    else
        return Results.BadRequest($"Error: {response.StatusCode}");
});

app.Run();

Note that in the code above we are receiving data from the API of the “CustomerManagements” project and passing the response to the Newtonsoft.Json method: JsonConvert.DeserializeObject<List<Customer>>(response.Content) and returning the JSON object in the API response.

Now let’s test and see the difference. Run both projects and in your browser access the address http://localhost:5054/v1/get_customers_by_country/USA again in your browser. You will now have the data formatted, as Newtonsoft.JSON deserializes the object.

Deserializing the data

4. XUnit

XUnit is a widely used unit testing library in the .NET ecosystem.

Through a simple and extensible approach to writing and running tests, XUnit becomes an excellent option for test-driven development (TDD), in addition to supporting advanced features such as parameterized tests and context sharing between test cases, making it a popular choice for projects of all sizes.

To learn about some features of XUnit, let’s create a class with a method to validate customer data in the CustomerProcess project. Create a new folder called “Services” and, inside it, create a new class called “CustomerService.cs” and put the following code in it:

using CustomerProcess.Models;
namespace CustomerProcess.Services;

public class CustomerService
{
    public string ValidateCustomer(Customer customer)
    {
        string errorMessage = string.Empty;
        if (string.IsNullOrEmpty(customer.Name))
            errorMessage += "Customer name cannot be null or blank";

        if (string.IsNullOrEmpty(customer.Email))
            errorMessage += "Customer email cannot be null or blank";

        return errorMessage;
    }
}

Now let’s create a test project. By definition, ASP.NET Core already has a template for implementing an XUnit test project. To create it, just run the commands below:

dotnet new xunit -n CustomerProcessTest
cd CustomerProcessTest
dotnet add reference ../CustomerProcess.csproj

When executing the above commands, a test project with the name “CustomerProcessTest” was created. When you open it you will notice that there is a file with the name “Test1.cs”. Rename it “ValidationTest” and then replace the existing code with the code below:

using CustomerProcess.Models;
using CustomerProcess.Services;

namespace CustomerProcessTest
{
    public class ValidationTest
    {
        [Fact]
        public void ValidateCustomerValid()
        {
            //Arrange
            var customer = new Customer(Guid.NewGuid(), "John", "john@mail.com");
            var service = new CustomerService();

            //Act
            string errorMessage = service.ValidateCustomer(customer);

            //Assert
            Assert.Empty(errorMessage);
        }

        [Fact]
        public void ValidateCustomerInValid()
        {
            //Arrange
            var customer = new Customer(Guid.NewGuid(), string.Empty, string.Empty);
            var service = new CustomerService();

            //Act
            string errorMessage = service.ValidateCustomer(customer);

            //Assert
            Assert.NotEmpty(errorMessage);
            Assert.Contains("Customer name cannot be null or blank", errorMessage);
            Assert.Contains("Customer email cannot be null or blank", errorMessage);
        }
    }
}

Note that, in the above code, we are creating two test methods. Both have the [Fact] attribute to indicate to XUnit that they should be executed. It is common to find the structure Arrange > Act > Assert in unit tests—where in Arrange we define the variables and objects, in Act the test is executed and in Assert the result is verified.

In this example, in the first method we are validating if a customer is valid. In this case, the string “errorMessage” must be empty. In the second test, we are checking if it is invalid; in this case, the string must be filled in and with the corresponding error messages.

To run the tests, just open a terminal in the “CustomerProcessTest” project and run the command dotnet test and you should have the following result:

Test result

5. Humanizer

Humanizer is a very useful NuGet package for ASP.NET Core apps as it makes it easy to format data, making it more readable and user-friendly.

Humanizer’s features include number and quantity formatting, date and time formatting, pluralization and singularization, and capitalization, among others, which help developers format numbers, dates, times and quantities in a more natural and understandable way, without the need to implement complex methods and functions.

Then, in the terminal of the Project “CustomerProcess” execute the following command to download the Humanizer in the project:

dotnet add package Humanizer

Now, let’s explore some of Humanizer’s features.

Formatting Numbers

With Humanizer, we can transform numbers written in full. For example, the number 1000 becomes one thousand.

To do this, in the Program file add a reference to the Humanizer: using Humanizer;. Then add the code below:

int number = 1000;
string formattedNumber = number.ToWords();
Console.WriteLine(formattedNumber);

Now if you run the command dotnet run you will get the following result:

Formatting numbers

Formatting Date and Time

To format dates and times just use the extension methods:

  • date.Humanize() to indicate the current moment
  • pastDate.Humanize() to indicate a moment in the past
  • futureDate.Humanize() to indicate a time in the future

So to test the date and time formatting functions, add the following code:

DateTime date = DateTime.Now;
string humanizedDate = date.Humanize();
Console.WriteLine(humanizedDate);

DateTime pastDate = DateTime.Now.AddHours(-2);
string humanizedPastDate = pastDate.Humanize();
Console.WriteLine(humanizedPastDate);

DateTime futureDate = DateTime.Now.AddDays(1);
string humanizedFutureDate = futureDate.Humanize();
Console.WriteLine(humanizedFutureDate);

Run the dotnet run command again, and you will have the following output in the console:

Formatting date and time

Formatting Timespan

Another important feature of Humanizer is displaying dates based on time range. For example, add the code below to the project then run dotnet run.

TimeSpan.FromMilliseconds(2).Humanize();
TimeSpan.FromDays(1).Humanize();
TimeSpan.FromDays(16).Humanize();

Note that the Humanizer transformed the reported data into milliseconds, days and weeks:

Formatting timespan

Formatting File Size

Humanizer makes it possible to manipulate information regarding the size of data such as KB, MB and GB in a simple way. Let’s implement some functions and see how it works. So, add the code below to the project:

//3 - Formatting data size
long sizeInBytes = 1024;
var KBSize = sizeInBytes.Bytes().Humanize();
Console.WriteLine(KBSize);

sizeInBytes = 2097152;
var MBSize = sizeInBytes.Bytes().Humanize();
Console.WriteLine(MBSize);

sizeInBytes = 3221225472;
var GBSize = sizeInBytes.Bytes().Humanize();
Console.WriteLine(GBSize);

sizeInBytes = 5497558138880;
string TBBytes = sizeInBytes.Bytes().Humanize();
Console.WriteLine(TBBytes);

Now if you run the command dotnet run you will get the following result:

Formatting data size

Conclusion

In this second part of Essential NuGet Packages for Beginners, we saw five important packages that help developers create quality applications and save time by using their valuable resources.

So whenever you develop a new application and functionality, consider using NuGet packages as they can help you with almost any challenge.


assis-zang-bio
About the Author

Assis Zang

Assis Zang is a software developer from Brazil, developing in the .NET platform since 2017. In his free time, he enjoys playing video games and reading good books. You can follow him at: LinkedIn and Github.

Related Posts

Comments

Comments are disabled in preview mode.