Search code examples
c#mongodbmongodb-.net-driver.net-8.0

How to add projection to an aggregation with pipeline


I have the following model class and its DTO:

public class User
{
    public string FullName { get; set; }
    public string DomainEmail { get; set; }
}

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

And I have to do this query with aggregation:

var matchStage = new BsonDocument(Match, new BsonDocument
{
    {  nameof(User.FullName), new BsonDocument(Eq, "Foo bar") }
});

var pipeline = new[] { matchStage };

var result = await _collection.Aggregate<UserDto>(pipeline).ToListAsync();

return result;

My problem is that I don't know how to map the properties from my model to my dto. This is a basic example, but in my real scenario, my model has more than 25 fields and the query is really bigger. I've created a projection, but I don't know how to apply this projection to my .Aggregate:

var projection = Builders<User>.Projection.Expression(m => new UserDto
{
    Name = m.FullName,
    Email = m.DomainEmail
});

var result = await _collection
    .Aggregate<UserDto>(pipeline)
    .Project(projection) // This doesn't work
    .ToListAsync();

return result;

Mapped classes:

CreateMap<User, UserDto>()
    .ForMember(u => u.Name, opts => opts.MapFrom(u => u.FullName))
    .ForMember(u => u.Email, opts => opts.MapFrom(u => u.DomainEmail));

Is there a way to solve this?

  • .NET 8
  • MongoDB Driver version 2.24

Solution

  • I am not sure, but I think you are missing the projection

    var result = await _collection
                    .Aggregate<PipelineDefinition<User, UserDto>>(pipeline)
                    .ToListAsync().ConfigureAswait(false);
    

    This assumes you have registered the user:

    IMongoCollection<User>
    

    Based on your answer you need to map the properties manually:

    var config = new MapperConfiguration(cfg => {
        cfg.CreateMap<User, UserDto>()
            .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName))
            .ForMember(dest => dest.Email, opt => opt.MapFrom(src => src.DomainEmail));
    });
    IMapper mapper = config.CreateMapper();