Ahmadreza's Notes

On Software Development

Posts Tagged ‘MongoDB

AutoMapper and mapping Expressions

AutoMapper is a great library helps developer to almost get rid of left hand right hand assignment between different types. In real world C# .net applications there are lots of scenarios developer wants to map different types of objects to each other. Normally it happens in system boundaries, like between UI and services or between ViewModel and Model. Without AutoMapper you have to do it for all properties every time you want to map to objects. This task is cumbersome, hard to test and bug prone. Automapper with does this for you with minimum cost. It has its own conventiones to facilitate type mapping. On top of built in defaults you as developer can teach Automapper new tricks.

You can find lots of blogs and articles of how to use AutoMapper. In fact this, this and this can help you.

Following is also an example of rather complex mapping between Source and Destination types.

public class Source
{
public string FullName { get; set; }
public int NumberOfYears { get; set; }
}
public class Destination
{
[Key]
public int UserId { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class AutomapperQueriableTest
{
public AutomapperQueriableTest()
{
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.Name, opt => opt.MapFrom(s => s.FullName))
.ForMember(d => d.Age, opt => opt.MapFrom(s => s.NumberOfYears));
Mapper.CreateMap<Destination, Source>()
.ForMember(d => d.FullName, opt => opt.MapFrom(s => s.Name))
.ForMember(d => d.NumberOfYears, opt => opt.MapFrom(s => s.Age));
}
[Fact]
public void SimpleMappingTest()
{
var fixture = new Fixture();
var source = fixture.Create<Source>();
var destination = Mapper.Map<Destination>(source);
Assert.Equal(source.FullName, destination.Name);
Assert.Equal(source.NumberOfYears, destination.Age);
}
}

In this example I’ve used xUnit to test and AutoFixture to generate random data. Then in constructor of test class I have created mapping by using static method Mapper.CreateMap. this creates mapping at application domain context and you just need to set this up once and will be available across your application. After that in our test we created a source type and then mapped that to destination type finally checked the destination properties.

This post is not about normal scenario of using Automapper. When you are working with database you’d normally use repositories (arguably you might not need repository). in simple case of repository you would have following code

public interface IRepository<T>
{
void Add(T obj);
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
}

This is a simple interface for repository. Implementation of this doesn’t even need mapping (you might do the mapping in other layers, though). However if we want to separate domain model from persistence model we could introduce another interface like following:

public interface IRepository<T>
{
void Add(T obj);
IQueryable<T> Find(Expression<Func<T, bool>> predicate);
}

In this interface we have to generic types TI and TO. TI is our domain model and TO is our persistence model. For example we pass Source object to Add method but repository implementation would save that in Destination model (See bellow example as Add implementation)

public void Add(Source source)
{
_context.Users.Add(Mapper.Map<Destination>(source));
}

This is possible if we map Source object to Destination object. Automapper does that for us.

Now, the real issue is when we want to implement Find method which accepts Expression<Func<Source, bool. Mapping an expression of func of type source to expression of func of type destination is more difficult as Jimmy Bogard main author of Automapper stated in this mailing list. Anyway, with a quick search I’ve found this question where somebody asked in stackoverflow about AutoMapper for Func’s between selector types. Person how answered this question updated the answer later with answer to Expression mapping with Automapper. The first class which does this magic is an implementation of ExpressionVisitor. This class can traverese an expression tree and replace a single parameter with an arbitrary expression

class ParameterReplacer : ExpressionVisitor
{
private readonly ParameterExpression _parameter;
private readonly Expression _replacement;
private ParameterReplacer(ParameterExpression parameter, Expression replacement)
{
_parameter = parameter;
_replacement = replacement;
}
public static Expression Replace(Expression expression, ParameterExpression parameter, Expression replacement)
{
return new ParameterReplacer(parameter, replacement).Visit(expression);
}
protected override Expression VisitParameter(ParameterExpression parameter)
{
if (parameter == _parameter)
{
return _replacement;
}
return base.VisitParameter(parameter);
}
}

The second piece that comes into play is an extension method for Expression<Func<Z,Y>> called “Compose”  and it returns a new Expression<Func<X, Y>>.

public static class FunctionCompositionExtensions
{
public static Expression<Func<TX, TY>> Compose<TX, TY, TZ>(this Expression<Func<TZ, TY>> outer, Expression<Func<TX, TZ>> inner)
{
return Expression.Lambda<Func<TX, TY>>(
ParameterReplacer.Replace(outer.Body, outer.Parameters[0], inner.Body),
inner.Parameters[0]);
}
}

With this extension method and ExpressionVisitor Class I can complete the repository class for. This repository class is implementing SQL Serve implementation. Later on we will implement another repository class for MongoDB as well.

public class SourceRepositorySql : IRepository<Source, Destination>, IDisposable
{
private readonly MyContext _context;
public SourceRepositorySql(MyContext context)
{
_context = context;
}
public void Add(Source source)
{
_context.Users.Add(Mapper.Map<Destination>(source));
}
public IQueryable<Destination> Find(Expression<Func<Source, bool>> predicate)
{
Expression<Func<Destination, Source>> mapper = Mapper.Engine.CreateMapExpression<Destination, Source>();
Expression<Func<Destination, bool>> mappedSelector = predicate.Compose(mapper);
return _context.Users.Where(mappedSelector);
}
private bool _disposed;
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_context.Dispose();
}
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}

This is the Entity Framework implementation of our repository. Interesting point is “Find” Method which is taking advantage of Mapper.Engine.CreateMapExpression and Compose extension method. CreateMapExpression in AutoMapper creates an expression for mapping between Source to Destination. In the next line we use the mapper expression and pass this to Compose extension method to build a new expression. And finally we call Where method on Users DBSet and it returns IQueriable of type Destination. I have written a Unit Test to test this.

[Fact]
[UseDatabase]
public void ShouldConvertPredicates()
{
var connStr = ConfigurationManager.ConnectionStrings["SQLExpressConnection"].ConnectionString;
var myContext = new MyContext(connStr);
using (var repository = new SourceRepositorySql(myContext))
{
AddRecords(repository);
myContext.SaveChanges();
const string name = "Ahmadreza";
var query = repository.Find(s => s.FullName == name && s.NumberOfYears > 10);
var result = query.Project().To<Source>().ToList();
Assert.Equal(1, result.Count);
Assert.Equal(name, result.First().FullName);
}
}
private static void AddRecords(IRepository<Source, Destination> repository)
{
repository.Add(new Source { FullName = "Ahmadreza", NumberOfYears = 35 });
repository.Add(new Source { FullName = "Ahmadreza", NumberOfYears = 9 });
repository.Add(new Source { FullName = "Jon", NumberOfYears = 20 });
}

This test successfully passes. As you see I used Source type to add records and query the records. The repository maps the add part to destination and also map predicates expression.

When I debug the code I can see entity framework successfully constructed the query. This is using Destination type.

SELECT
[Extent1].[UserId] AS [UserId],
[Extent1].[Name] AS [Name],
[Extent1].[Age] AS [Age]
FROM [dbo].[Destinations] AS [Extent1]
WHERE (N'Ahmadreza' = [Extent1].[Name]) AND ([Extent1].[Age] > 10)

Well this is really good, although there is a down side to this approach. This is good and pretty mych abstracted the Destination class and I just work with Source type. However, if I want to join results of two different IQueriables I have to mention the property name of the Model.

Anyway it worked reasonably good with SQL server and EntityFramework. Let see if it works with MongoDB.
In order to connect to MongoDB I use mongo csharp driver and on top of that I’m using MongoRepository. Now let see if the same scenario works for Repository Implemented for MongoDb.

In following code I have implemented the IRepository for MongoDB.

public class SourceRepositoryMongo : IRepository<Source, Destination>
{
private readonly MongoRepository<Destination> _mongoRepository;
public SourceRepositoryMongo()
{
_mongoRepository = new MongoRepository<Destination>();
}
public void Add(Source obj)
{
_mongoRepository.Add(Mapper.Map<Destination>(obj));
}
public IQueryable<Destination> Find(Expression<Func<Source, bool>> predicate)
{
Expression<Func<Destination, Source>> mapper = Mapper.Engine.CreateMapExpression<Destination, Source>();
Expression<Func<Destination, bool>> mappedSelector = predicate.Compose(mapper);
return _mongoRepository.Where(mappedSelector);
}
public IQueryable<Destination> Find(Expression<Func<Destination, bool>> predicate)
{
return _mongoRepository.Where(predicate);
}
}

The target is to test Find(Expression<Func<Source, bool>> predicate) and see if it works, however, I have added another Find with Find(Expression<Func<Destination, bool>> predicate) signature and I will describe why I added that method.

Now the unit test to test this implementation.

[Fact]
[UseMongo]
public void MongoTest()
{
var repositoryMongo = new SourceRepositoryMongo();
AddRecords(repositoryMongo);
var name = "Ahmadreza";
//var query = repositoryMongo.Find(s => s.Name == name && s.Age > 10);
var query = repositoryMongo.Find(s => s.FullName == name && s.NumberOfYears > 10);
var result = query.Project().To<Source>();
Assert.Equal(1, result.Count());
Assert.Equal(name, result.First().FullName);
}

This test fails!. This complains about full name not having serialization information

Additional information: Unable to determine the serialization information for the expression: <MemberInitExpression>.FullName.

This approach doesn’t work for MongoDB (probably Mongo CSharp driver could not handle that). I have written another method that accepts Expression<Func<Destination, bool>> predicate, If you uncomment this method call and comment the other in unit test it’ll work.

Having looked at transformed expression shows that this approach does not completely re-write the expression.

.Lambda #Lambda1<System.Func`2[AutomapperQuesriableTest.Destination,System.Boolean]>(AutomapperQuesriableTest.Destination $dto)
{
(.New AutomapperQuesriableTest.Source(){
FullName = $dto.Name,
NumberOfYears = $dto.Age
}).FullName == "ahmadreza" && (.New AutomapperQuesriableTest.Source(){
FullName = $dto.Name,
NumberOfYears = $dto.Age
}).NumberOfYears > 10
}

This is just wrapper around another lambda that does the mapping. Interestingly EntityFramework handles that very well but MongoDB CSharp Driver not.

On option it to find a way to rewrite the whole expression based on the mapping configuration. But as Jimmy mentioned it could be quite hard to implement.

You can find the full implementation and tests here

Written by Ahmadreza Atighechi

September 20, 2014 at 10:19 am

Posted in Blog

Tagged with , , ,