Configuring AutoMapper for use with the ASP.NET Core DI Container

I’m generally a strong proponent of using separate models for commmunicating across service boundaries that are separate and distinct from my core domain model. In a typical web application that is backed by a database, this usually means that I have a domain model, a storage model, and a view model, and I map between storage model ↔ domain model and between domain model ↔ view model when crossing their respective boundaries. This keeps my domain model isolated, ensures that it is not forced to change due to concerns in another layer, and promotes adherence to the Single Responsibility Principle (a class should have one, and only one, reason to change).

Having multiple models implies that there is some mechanism for mapping between models. My go-to tool for this task is AutoMapper. Configuring AutoMapper for use in an ASP.NET Core project using the ASP.NET Core DI container differs slightly from my prior experiences with it on past projects. Here’s what I did to get up and running…

Install Nuget Packages

Let’s first install the Nuget packages that we’ll need.  I’m using AutoMapper 7.0.1 and AutoMapper.Extensions.Microsoft.DependencyInjection 5.0.1, which provides AutoMapper extensions on IServiceCollection that allow us to wire-up AutoMapper in Startup.ConfigureServices(IServiceCollection services).

Create Models

Suppose we have a Stock domain model class that looks like this:

    public class Stock
    {
        private readonly IStockRepository _repository;

        public Stock(IStockRepository repo)
        {
            _repository = repo ?? throw new ArgumentNullException(nameof(repo));
        }

        public string Name { get; set; }

        public string Symbol { get; set; }
    }

Note that we’re injecting an IStockRepository.  (I often have the aggregate roots in my domain models take a reference to their corresponding repository so I can do neat things like stock.SaveAsync(), but that’s another topic…)

For our purposes here it doesn’t really matter what IStockRepository and StockRepository look like – we’ll just leave them empty:

    public interface IStockRepository
    {
    }

    public class StockRepository : IStockRepository
    {
    }

We have a StockViewModel class that roughly corresponds with our Stock domain model and looks like this:

 
    public class StockViewModel
    {
        public string Name { get; set; }

        public string Symbol { get; set; }
    }

We’d also probably have a Stock storage model class and perhaps other corresponding view model representations of a stock, but we’ll keep it to just these two classes to keep things simple.

Create Mapping Profile

Next we’ll create our mapping profile, which is where we’ll configure our specific type mappings.  Usually I’ll create a profile per bounded context per layer I’m mapping.  So, for example, if I have a single bounded context but I map between storage model ↔ domain model and between domain model ↔ view model, I’ll have a ViewModelMappingProfile and a StorageModelMappingProfile.

Our ViewModelMappingProfile in this case looks like this:

 
    public class ViewModelMappingProfile : Profile
    {
        public ViewModelMappingProfile()
        {           
            CreateMap<StockViewModel, Stock>().ConstructUsingServiceLocator();
        }
    }

It derives from AutoMapper.Profile.  I’ve just got a single, simple mapping from StockViewModel to Stock, and I’ve called ConstructUsingServiceLocator() on the mapping to enable the resolution and injection of dependencies into the target.  Comprehensive documentation on configuring individual mappings can be found here.

Configure Services

Next we configure our services and add AutoMapper to the service collection.   In Startup.ConfigureServices(IServiceCollection services)we add the following after services.AddMvc():

    services.AddAutoMapper();

The line above does a couple of things: it registers IMapper with our DI container, and it searches our assembly for classes that inherit from AutoMapper.Profileand automatically loads them.  So our ViewModelMappingProfile gets loaded automatically at startup, and we can confirm that by putting a breakpoint in its constructor.

We also must register with the DI container any dependencies that will be injected in at map time (like our IStockRepository) as well as any type mappings that are configured with ConstructUsingServiceLocator()(like our Stock domain class).  This part is important when working with the ASP.NET Core DI container – other third-party containers that I’ve worked with in the past (such as Unity) will resolve concrete types automatically without requiring an explicit type mapping.  Not so with the ASP.NET Core DI container, and I spent a bit of time figuring this out.  Since we define the mapping to Stock with ConstructUsingServiceLocator(), it must be explicitly registered with the container.

So, we have the following somewhere in our Startup.ConfigureServices(IServiceCollection services) method:

    services.AddScoped<IStockRepository, StockRepository>();
    services.AddTransient<Stock, Stock>();

Map!

With our configuration complete, all that’s left to do is start using our mappings.  We can inject an instance of IMapper into any of our classes that are resolved using the DI container.

Here’s an example usage in an OnPostAsync() method on a Razor page model that maps from a posted view model to the domain model and then saves the data:

        public async Task<IActionResult> OnPostAsync(CancellationToken cancellationToken)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            var stock = _mapper.Map<StockViewModel, Stock>(Stock);
            await stock.SaveAsync(cancellationToken);

            return RedirectToPage($"./Stock?id={stock.Id}");
        }

Wrapping Up

We’ve shown how to configure and start using AutoMapper with the ASP.NET Core DI container, for a quick and easy way to get model mapping with DI working in your ASP.NET Core project.

It’s worth noting, though, that while the core DI container may be adequate for small, simple projects, it’s not as feature-rich as most other third-party DI containers.  More complex projects might require a more robust and capable DI container to meet their specific needs.  (This post on Stack Overflow gives a nice overview of some of its shortcomings.)

Adding Simple Pagination to a Bootstrap Table in ASP.NET Core

I recently needed to add simple pagination to a Bootstrap table on a page in my ASP.NET Core project.  I’m using MVC Razor Pages, but a similar approach will work with Razor Views.

My data set contains 270+ rows, and originally I was returning all 270+ rows to the client and displaying them in a table that scrolled well past the end of the page.  I only want to show ten at a time with a simple paging control.  Here’s what the final product will look like:

The following is a standard Bootstrap table generated from items in Model.Stocks from our page model:

<table class="table table-striped table-bordered table-sm table-responsive">
    <thead>
        <tr>
            <th scope="col">Symbol</th>
            <th scope="col">Name</th>
            <th scope="col">Sector</th>
            <th scope="col">Price</th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Stocks)
        {
            <tr>
                <th scope="row">@item.Symbol</th>
                <td>@item.Name</td>
                <td>@item.Sector</td>
                <td>@item.Price</td>
            </tr>
        }
    </tbody>
</table>

Here is the page model:

    public class DividendPicksModel : PageModel
    {
        private readonly IStockService _stockService;

        public DividendPicksModel(
            IStockService stockService)
        {
            _stockService = stockService ?? throw new ArgumentNullException(nameof(stockService));
        }

        public PaginatedList<StockViewModel> Stocks { get; private set; }

        public async Task<IActionResult> OnGetAsync(int? pageIndex, CancellationToken cancellation)
        {
            var stocks = await _stockService.GetCachedStockResources(cancellation) ?? new List<Stock>();

            Stocks = PaginatedList<StockViewModel>.Create(stocks.Select(s => new StockViewModel(s)), pageIndex ?? 1, 10, 5);

            return Page();
        }
    }

The page implements

public async Task<IActionResult> OnGetAsync(int? pageIndex, CancellationToken cancellation)

This is responsible for handling GET requests for the page. There’s not too much to it: retrieve the data from a domain service, set the Stocks collection property on the page model, and return the page.

Note that the method takes int? pageIndex as a parameter which indicates which page of the table is being requested by the client. Not passing this parameter will cause the the first page of the table to be returned.

The interesting thing to note here is that the Stocks property is not a standard .NET collection. It’s a PaginatedList<Stock>PaginatedList<T>derives from List<T>  and represents a “page” of our data, and it exposes the properties needed to enable paging functionality for the collection on the view.  Let’s take a look at it:

    public class PaginatedList<T> : List<T>
    {
        private PaginatedList(List<T> items, int count, int pageIndex, int pageSize, int countOfPageIndexesToDisplay)
        {
            PageIndex = pageIndex;
            TotalPages = (int)Math.Ceiling(count / (double)pageSize);

            SetPageIndexesToDisplay(pageIndex, countOfPageIndexesToDisplay);

            AddRange(items);
        }

        public int PageIndex { get; }

        public int TotalPages { get; }

        public bool HasPreviousPage => (PageIndex > 1);

        public bool HasNextPage => (PageIndex < TotalPages);

        public List<PageIndex> PageIndexesToDisplay { get; private set; }

        public static PaginatedList<T> Create(
            IEnumerable<T> source, int pageIndex, int pageSize, int countOfPageIndexesToDisplay = 3)
        {
            var list = source.ToList();
            var count = list.Count;
            var items = list
                .Skip((pageIndex - 1) * pageSize)
                .Take(pageSize).ToList();
            return new PaginatedList<T>(items, count, pageIndex, pageSize, countOfPageIndexesToDisplay);
        }

        private void SetPageIndexesToDisplay(int pageIndex, int countOfPageIndexesToDisplay)
        {
            PageIndexesToDisplay = new List<PageIndex>();
            if (pageIndex > TotalPages - countOfPageIndexesToDisplay + Math.Floor(countOfPageIndexesToDisplay / 2.0m))
            {
                for (var i = Math.Max(TotalPages - countOfPageIndexesToDisplay + 1, 0); i <= TotalPages; i++)
                {
                    PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
                }
            }
            else if (pageIndex < countOfPageIndexesToDisplay - Math.Floor(countOfPageIndexesToDisplay / 2.0m))
            {
                for (var i = 1; i <= Math.Min(countOfPageIndexesToDisplay, TotalPages); i++)
                {
                    PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
                }
            }
            else
            {
                var startIndex = pageIndex - (int)Math.Floor(countOfPageIndexesToDisplay / 2.0m);
                for (var i = startIndex; i <= startIndex + countOfPageIndexesToDisplay - 1; i++)
                {
                    PageIndexesToDisplay.Add(new PageIndex(i, i == pageIndex));
                }
            }

        }
    }

We create a PaginatedList by calling into the static Create method and passing it the collection that will be paged, the index of the page we need to display, the page size, and the number of page indexes we want to display in the pagination control at a time (in the screenshot at the beginning of the post it’s five).

PaginatedList exposes several properties about the page of data that it represents:

  • PageIndex
  • TotalPages
  • HasPreviousPage
  • HasNextPage
  • PageIndexesToDisplay

… and, of course, since it derives from List<T>, we have access to the list of items on our page.

The most complex part of PaginatedList<T> is SetPageIndexesToDisplay(..), which builds the collection of page indices to display in the Bootstrap pagination control based on the current page index and the total count of indices to display.  PageIndex is a simple POCO that contains the index number and a boolean indicating whether that page index is the active (displayed) page:

    public class PageIndex
    {
        public PageIndex(int index, bool isActive)
        {
            Index = index;
            IsActive = isActive;
        }

        public int Index { get; }

        public bool IsActive { get; }
    }

The final piece to pull this all together is the actual pagination control on the view. Immediately below the table in our HTML we have the following:

@{
    var prevDisabled = !Model.Stocks.HasPreviousPage ? "disabled" : "";
    var nextDisabled = !Model.Stocks.HasNextPage ? "disabled" : "";
}
<nav aria-label="Pagination" class="col-12">
    <ul class="pagination justify-content-end">
        <li class="page-item @prevDisabled">
            <a class="page-link" 
                asp-page="./MyPage" 
                asp-route-pageIndex="@(Model.Stocks.PageIndex - 1)"
                aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
                <span class="sr-only">Previous</span>
            </a>
        </li>
        @foreach (var i in Model.Stocks.PageIndexesToDisplay)
        {
            var activeClass = i.IsActive ? "active" : "";
            
            <li class="page-item @activeClass"><a class="page-link"  
                                        asp-page="./MyPage"
                                        asp-route-pageIndex="@(i.Index)">@i.Index</a></li>
        }
        <li class="page-item @nextDisabled">
            <a class="page-link" 
                asp-page="./MyPage"
                asp-route-pageIndex="@(Model.Stocks.PageIndex + 1)"
                aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
                <span class="sr-only">Next</span>
            </a>
        </li>
    </ul>
</nav>

Here we use the properties exposed by our PaginatedList<Stock> to control how the pagination control is rendered and functions.  We enable/disable the previous/next buttons based on the HasPreviousPage and HasNextPage properties, respectively.  We build the numeric page index buttons from the PageIndexesToDisplay collection, and we set the link appropriately for the given index.

Clicking one of the links in the pagination control requests the page again passing the given page index and the PaginatedList<Stock> is rebuilt for the requested page.

Wrapping Up

We’ve explored here a simple, no-frills way to add pagination to a Bootstrap table.  There are a couple of improvements that could be made:

  • Interacting with the pagination control causes a refresh of the entire page.  This could be modified to execute a little Javascript to hit an endpoint that returns the new page of data and update the table and pagination control in-place without reloading the whole page.
  • This implementation retrieves the entire data set from the service and then applies paging within our app service.  This is fine for my purposes since my dataset is small, but for larger datasets I would push the execution of the paging (skip/take operators) to the database.  (If using Entity Framework, this could be as simple as deferring query execution until after the Skip and Take are applied in PaginatedList<T>.Create(..).)

I may consider these enhancements in the future, but for now this implementation suits my needs just fine.