AutoMapper, ProjectTo() – Instance Version

Full source code available here.

In my previous post I showed how to use the wonderful AutoMapper ProjectTo() feature, the demo code shown worked with AutoMapper up to v8.1.1. It looked like this –

_salesContext.Products.OrderBy(p => p.ProductId).Take(count).ProjectTo<ProductModel>()

But this will not work with AutoMapper v9 and above. You will be promptly informed that No overload for the method ‘ProjectTo’ takes 0 arguments.

What happend? Well, AutoMapper moved away from a static API to an instance only API.
To get this working you need to make a few minor changes compared to previous post.
In ConfigureServices(…) replace this –

Mapper.Initialize(cfg => {
    cfg.AddProfile<MappingProfile>();
});

With this –

services.AddAutoMapper(typeof(Startup));

Then in the controller, change this

public class ProductsController : ControllerBase
{
    private readonly SalesContext _salesContext;  

    public ProductsController (SalesContext salesContext)
    {
        _salesContext = salesContext;
    }
    // snip…

To this –

 
private readonly SalesContext _salesContext;
private readonly IMapper _mapper;

public ProductsController (SalesContext salesContext, IMapper mapper)
{
    _salesContext = salesContext;
    _mapper = mapper;
}
// snip..

And finally, the ProjectTo call changes from this –

[HttpGet("projected/{count}")]
public ActionResult GetProjected(int count)
{
    return Ok(_salesContext.Products.OrderBy(p => p.ProductId).Take(count).ProjectTo<ProductModel>());
}

To this –

[HttpGet("projected/{count}")]
public ActionResult GetProjected(int count)
{
    return Ok(_mapper.ProjectTo<ProductModel>(_salesContext.Products.Take(count).OrderBy(p => p.ProductId).Take(count)));
}

The SQL produced by the instance ProjectTo() is the same as what was produced by the static version.

That’s it, easy once you figure it out.

Full source code available here.

AutoMapper, ProjectTo() – Static Version

Full source code available here.

I’ve been using AutoMapper for quite a few years, but one of the features that I like the most is the ProjectTo method. When using Entity Framework it lets me reduce the number of fields I query from the database to match the model I want to return to the caller.
For example, I have a Product table in the database and a DTO that represents the Product that looks like this –

public class Product
{
	public int ProductId { get; set; }
	public string Name { get; set; }
	public ProductCategory ProductCategory { get; set; }
	public string Description { get; set; }
	public decimal Price { get; set; }
	public string SKU { get; set; }
	public string Code { get; set; }
}  

But say I wanted to return only the ProductId, Name and Code, I could run a query to return the full Product and the map the results to a model that looks like this –

public class ProductModel
{
	public int ProductId { get; set; }
	public string Name { get; set; }
	public string Code { get; set; }
}

In this scenario, the generated SELECT statement will request all seven fields in product and return them to the application, I then have to them to the ProductModel with the three fields I want.
Here is the SQL produced an executed –

SELECT TOP(@__p_0) [p].[ProductId], [p].[Code], [p].[Description], [p].[Name], [p].[Price], [p].[ProductCategory], [p].[SKU]
FROM [Products] AS [p]
ORDER BY [p].[ProductId]

Instead of requesting all seven fields the ProjectTo method will examine the model and generate only the SQL needed to return the relevant fields.

Here is the SQL produced –

SELECT TOP(@__p_0) [p].[Code], [p].[Name], [p].[ProductId]
FROM [Products] AS [p]
ORDER BY [p].[ProductId]

And here is how to write the C# code that performs this time saving, energy saving work –

var productModels = _salesContext.Products.OrderBy(p => p.ProductId).Take(count).ProjectTo<ProductModel>();

To get this to work you need to do some wiring up.
First add the nuget package AutoMapper.Extensions.Microsoft.DependencyInjection v6.1.1 to your project.

Then create a mapping profile, for this scenario it is very simple. The below code maps Product to ProductModel.

public class MappingProfile : Profile
{
	public MappingProfile()
	{
		CreateMap<Product, ProductModel>();
	}
}

In starup.cs add a call to Mapper.Initialize as shown here.

public void ConfigureServices(IServiceCollection services)
{
	Mapper.Initialize(cfg => {
		cfg.AddProfile<MappingProfile>();
	});

That’s all you need, but that’s not the end of the story…

This approach works with AutoMapper.Extensions.Microsoft.DependencyInjection up to v6.1.1, and AutoMapper v8.1.1, but if you move to newer versions you need a slightly different approach because the static API has been removed.

I’ll show that in the next blog post.

Full source code available here.