Software development is a complex task and as a developer you gain nothing from "reinventing the wheel". I'm a firm believer that you should make your life as easy as possible as a developer, by using tried and tested packages where possible; it will take so much headache out of your development experience.
Below are a few of the packages that I use in most of my projects. I believe they make for an easier development experience and make my code much easier to maintain. I would recommend that you consider them for your own projects in the future. If I've missed a package that you think is super useful, please share in the comments section below.
My top 4 Nuget packages are:
Most software applications consist of different layers that each have their own concerns. For example, a common pattern is the 3-layered data access, logic, and presentation model. Since each layer has its own concerns and should be loosely coupled to the other layers, each layer normally has its own objects models too.
For example, a user object in the logic layer may contain methods that define the behaviour of that model, whereas a user object in the presentation layer may only contain the properties necessary to be displayed in the UI.
Therefore, each time data is passed up and down between layers, the objects of one layer must be converted to the objects of the next layer. For example, in an ASP.NET Core MVC application, you may typically see something like this in a controller action.
public class UserController : Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
public async Task<IActionResult> GetUser(int id)
{
var domainUser = await _userService.Get(id);
var presentationUser = new PresentationUser
{
Id = domainUser.Id,
FirstName = domainUser.FirstName,
LastName = domainUser.LastName,
// etc
};
return new JsonResult(presentationUser);
}
}
This process is known as mapping, and I'm sure you'll agree that it can get incredibly boring and tedious to write such code, not to mention it convoluting your code with trivial procedures.
This is where Automapper comes to save the day (and your sanity). It is a convention-based object-object mapper written by Jimmy Bogard and it really takes a lot of the pain out of mapping objects. It really takes minimal configuration in most cases and results in much cleaner code with all of the trivial mapping code removed:
public class UserController : Controller
{
private readonly IUserService _userService;
private readonly IMapper _mapper;
public UserController(IUserService userService, IMapper mapper)
{
_userService = userService;
_mapper = mapper;
}
public async Task<IActionResult> GetUser(int id)
{
var domainUser = await _userService.Get(id);
var presentationUser = _mapper.Map<PresentationUser>(domainUser);
return new JsonResult(presentationUser);
}
}
To find out more, read the Automapper documentation and find it in the NuGet Gallery.
Mediatr is another package by Jimmy Bogard and is an implementation of the mediator software design pattern. The mediator pattern is designed to reduced coupled between classes by having them interact through an intermediate mediator class. The result is easier-to-maintain code with fewer dependencies.
All interactions in Mediatr consist of a class containing the data that you want to send and a handler class for that data. There are two different types of interaction, a request and a notification.
A request expects to have just one handler implementation and can expect a value to be returned from the request.
public class UserRequest : IRequest<DomainUser>
{
public int UserId { get; set; }
}
public class UserRequestHandler : IRequestHandler<UserRequest, User>
{
private readonly IUserService _userService;
public UserRequestHandler(IUserService userService)
{
_userService = userService;
}
public async Task<User> Handle(UserRequest request, CancellationToken ct)
{
return await _userService.Get(request.UserId);
}
}
public class UserController : Controller
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
public async Task<IActionResult> GetUser(int id)
{
var request = new UserRequest { UserId = id };
var user = await _mediator.Send(request);
return new JsonResult(user);
}
}
A notification is similar to an event. It can be broadcast to multiple handlers, each of which can do their own logic based on the notification. Notifications do not return any value.
public class UserRetrievedNotification : INotification
{
public int UserId { get; set; }
}
public class UserRetrievedNotificationHandler1 : INotificationHandler<UserRetrievedNotification>
{
public async Task Handle(UserRetrievedNotification notification, CancellationToken ct)
{
// do some logic
}
}
public class UserRetrievedNotificationHandler2 : INotificationHandler<UserRetrievedNotification>
{
public async Task Handle(UserRetrievedNotification notification, CancellationToken ct)
{
// do some more logic
}
}
public class UserController : Controller
{
private readonly IMediator _mediator;
public UserController(IMediator mediator)
{
_mediator = mediator;
}
public async Task<IActionResult> GetUser(int id)
{
var request = new UserRequest { UserId = id };
var user = await _mediator.Send(request);
var notification = new UserRetrievedNotification { UserId = id };
await _mediator.Publish(notification);
return new JsonResult(user);
}
}
Another really cool feature of Mediatr is that you can configure a pipeline to actions to run before and after a request/notification, so you could implement exception handling or logging automatically for every request.
To find out more, read the Mediatr documentation and find it in the NuGet Gallery.
Swagger is a must-have if you're creating APIs using ASP.NET Core. It automatically scans all of your controllers and actions to generate an OpenAPI document, which makes it super easy to visualise and maintain all of your API endpoints.
Swagger is also really useful for testing as it allows you to send requests to each of your endpoints via the browser.
There's not really much more to say than that, since it requires hardly any configuration and generates everything for you. For instructions on how to install it, I would recommend this Microsoft article. To find out more, read the Swagger documentation and find it in the NuGet Gallery.
When receiving any input from a user, it is important to make sure that the data that they have supplied is valid and complete. You can do this to some extent on the front end using client-side form validation (and you definitely should do this for a better user experience), but all input should be validated again on the backend since there are a number of ways that a user could bypass the client-side validation (whether maliciously or not).
There are a number of ways that you could set up validation on the server, but ultimately it comes down to checking each property of the input class against a set of rules. The logic for these rules will need to be defined for each input class, and, similar to the object mapping example, can be rather boring, repetitive and trivial.
public class UserController : Controller
{
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
public async Task<IActionResult> CreateUser(User user)
{
if (user is null) throw new ArgumentNullException();
if (string.IsNullOrEmpty(user.FirstName))
throw new ArgumentException();
if (user.FirstName.Length > 50)
throw new ArgumentException();
// etc
var result = await _userService.Create(user);
return new JsonResult(result);
}
}
Fluent Validation makes this whole process much more pleasant by using a fluent interface pattern to configure validation in a much less tedious way. The logic for many common types of validation is already defined, along with helpful error messages, although this can all by configured further if you wish.
public class UserValidator : AbstractValidator<User>
{
public UserValidator()
{
RuleFor(u => u.FirstName).NotEmpty().MaximumLength(50);
RuleFor(u => u.LastName).NotEmpty().MaximumLength(50);
// etc
}
}
To find out more, read the Fluent Validation documentation and find it in the NuGet Gallery.
These were my top 4 Nuget packages that I use in almost all of my projects. I find that they make for a much nicer development and maintenance experience, and I hope that you will find them useful too. If you know any other packages that you think should have been in this list, please let me know in the comments section below.
I post mostly about full stack .NET and Vue web development. To make sure that you don't miss out on any posts, please follow this blog and subscribe to my newsletter. If you found this post helpful, please like it and share it. You can also find me on Twitter.
Previously published at https://samwalpole.com/my-top-4-nuget-packages-for-aspnet-core