Next major version

  • Silverlight support
  • Mono support
  • CF support
  • Composite mappings (mapping 2+ source types to 1 destination type)
  • First-class reverse mapping support
  • A StructureMap-like MappingRegistry configuration helper
  • Supporting layer supertypes/interfaces (i.e., configuration for a layer supertype or interface gets applied to subtypes or implementors)
  • Mapping to interfaces with read-only members
  • Mapping to any IEnumerable with an Add method (to support any collection type)

Last edited Nov 28, 2009 at 10:07 PM by jbogard, version 15

Comments

krokonoster Jan 19, 2011 at 5:31 AM 
Anyone used Automapper where the destination type have a dependency that need to be resolved.
At present, this is passed from the controller (also having this dependency, and resolved here) to the ViewModel constructor.
Changing this logic is not an option, and CreateMap<Source,Destination>() won't work, since Destination expect this depedency instance.

clement_911 Jan 8, 2011 at 3:12 AM 
Good stuff !
I'm looking forward to the Silverlight support.

ericpizon Nov 20, 2009 at 12:29 PM 
When is the release of the next major version? I am currently looking for object mapper that can work for both in desktop and silverlight projects.

binoj7 Jul 30, 2009 at 2:08 PM 
Well there has been a good attempt to do this at http://otis-lib.googlecode.com/svn/
But it does not convert DateTimes and Nullables :(

jbogard Jul 29, 2009 at 12:27 PM 
@binoj7

No. XML is the devil.

If I get a patch, then no problem, I'll put it in.

binoj7 Jul 29, 2009 at 11:01 AM 
Any plans to have xml configuration files that can define the mappings? Or is this already available?

hendryluk Jul 28, 2009 at 6:21 AM 
Thanks Jimmy.. Using ActionFilter is a good advice... Alas I can't use it because I'm using presentation-model pattern, in which each action returns *multiple* viewmodels, wrapped into a single presentationmodel.
E.g.
return View( new EditProductPresentationModel {
Product = Mapper.Map<ProductViewModel>(product),
CategoryOptions = Mapper.Map<IEnumerable<CategoryViewModel>>(categories),
CountryOptions = Mapper>Map<IEnumerable<CountryViewModel>>(countries) });
I.e. each controller Action returns multiple models, instead of one.. So not quite suitable for ActionFilter... (Altho this is a separate discussion)..

Mocking IMappingEngine is the best candidate for me at the moment. The problem is, merely a personal taste: I favor state-based verification over interaction-based. It's more BDD friendly because I could write like this:

public void should_return_selected_product(){
model.Product.ShouldBeAMappedDtoFrom(product);
}

public void should_all_beverage_types_as_category_options(){
model.CategoryOptions.ShouldBeAMappedDtoFrom(beverageCategories);
}

public void should_all_countries_as_options(){
model.CountryOptions.ShouldBeAMappedDtoFrom(allCountries);
}
I.e. each test verifies if the controller indeed returns the snapshot information of the current state of the desired entities... instead of mocking all interactions to the MappingEngine dependency in the SetUp method altogether... which is un-BDD-ish, bit noisy, and too fake-y.
Right now, ShouldBeAMappedDtoFrom is achieved by asking Automapper to generate expectedDto from expected entity, then do reflective comparisson against the actualDto.

However, while writing this, I realized that's not a good idea :P
I could have faked IMappingEngine into a proxy that remembers each DTO being mapped, so I can later interogate if a particular DTO instance was a mapping result from particular entity instance
So probably just ignore my enquiry about mapping-comparison feature ;)
Cheers

jbogard Jul 27, 2009 at 1:03 PM 
@hendryluk

Ah, I gotcha now. Two ways you can do this:

- Have your controller depend on an IMappingEngine (we do this in some places)
- Use an action filter to do the mapping - check out David Hayden's example on how to do this

Basically, we never have the real-deal holyfield mapper going in a controller unit test.

hendryluk Jul 26, 2009 at 6:33 AM 
@jbogard, I think you misunderstood. I'm not testing the mapping, I am testing the code that uses AutoMapper, e.g. MVC controller. In fact, what I want to achieve is to take mapping out of equation.
e.g. how would you test this controller action?
public ActionResult ProductDetail(int productId)
{
var product = productRepository.GetById(productId);
return View(Mapper.Map<ProductDto>(product));
}

This is how I would unit-test it:
// arrange
productRepository.When(x=>x.GetById(productId))
.ThenReturn(stubProduct);

// action
result = controller.ProductDetail(productId);

// assert
result.AssertViewRendered
.WithViewData<ProductDto>()
.ShouldBeAMappedDtoFrom(product);

It would be good if the last line (ShouldBeAMappedDtoFrom) could be implemented by asking AutoMapper to compare the properties mappings between supplied DTO and original entity if they match.
Alternative way is to mock the mapper (like pete's comment), which i don't quite like because it adds noise to the test, and makes the test too 'interaction-centric' (i.e. too 'fake') to my liking.
Cheers

jbogard Jul 24, 2009 at 1:33 PM 
@hendryluk

Part of the power of AutoMapper is not having to test individual mappings. We test AutoMapper with exactly two lines of code - AutoMapperConfiguration.Initialize(); then Mapper.AssertConfigurationIsValid(); The first method is something we wrote for all of our mapping configuration.

hendryluk Jul 24, 2009 at 8:06 AM 
FYI, at the moment the way I implement ShouldBeAMappedDtoFrom() is:
var expectedDto = Mapper.Map<Target>(expectedSource);
Assert.IsTrue(ReflectiveCompare(expectedDto, actualDto));

So it reperforms the mapping from expected entity, resulting in an expected DTO, then do a reflection comparisson between all properties (and subproperties) of DTO and expected DTO. It's pretty hacky... And I'm thinking if it's sensible to implement this comparisson in mapping level within AutoMapper instead.
Cheers

hendryluk Jul 24, 2009 at 8:03 AM 
Instead of mocking, I'm looking for a way for AutoMapper to assert if the mapped target matches a certain source (by comparing all mapping properties and its subproperties). As such, I could do something like this in unit-test: viewData.ShouldBeAMappedDtoFrom(stubbedCustomerList);
SUT code: viewData = Mapper.Map<CustomerSummaryDTO>(customerList);
What I'm looking for in my ShouldBeAMappedDtoFrom() extension method is really to ask AutoMapper to evaluate the mapping to all properties and subproperties to perform equality check between them.
Does this actually make sense? Because I think mocking the mapper is too glanural for most unit-test purposes.
Cheers
Hendry

jbogard Jun 28, 2009 at 3:26 PM 
@mrpmorris

On the first point - thanks! Fixed it in R89, to lock on another object instead of the Type object.

As for using in IoC - check out this post I made a while back:

http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/05/11/automapper-and-ioc.aspx

The two main interfaces are IConfigurationProvider and IMappingEngine - the Mapper static class just provides a static helper for those two guys. But there's nothing special about them, they can be used independently.

mrpmorris Jun 28, 2009 at 10:12 AM 
Hi

Just had a quick (5 min) look at your project and here are my observations :-)

The first one is that according to Joe Duffy you should avoid lock(typeof(X)) in code. He says it could cause problems if you have multiple app domains, and because it exposes the object you are locking so source code outside of your control could also lock it and may incorrectly not release it. I think the multi app-domain thing is probably the only potential issue.

I was also hoping there could be an option to maybe remove the "static" declaration on the Mapper class? If someone wants a static reference then they can add one to their app, or you might even want to consider adding an additional StaticMapper class - an IMapper interface would be nice too!

The reasons I ask for this are
1: For unit testing I can mock the IMapper interface.
2: I want to use a dependency injection container.

So on my base DTO class I can have members which do not come from the source object, but instead come from information provided by an injected service. For example if my DTO has

public IEnumerable<IConstraint> Constraints { get; set; }

These constraints won't be on the domain class, instead they would be in an IConstraintProvider<T> service obtainable from the injection container. In this case I would create a descendent Mapper

public class ViewModelMapper : Mapper
{
readonly IConstraintProvider<User> UserConstraintProvider;

public ViewModelMapper(IConstraintProvider<User> userConstraintProvider)
{
UserConstraintProvider = userConstraintProvider;
AddMappings();
}

void AddMappings()
{
//Add a mapping from User to UserVM, and use UserConstraintProvider to set its constraints
}
}

Regards

Pete

juliusganns May 23, 2009 at 7:35 PM 
Hey Guys,

just took a first look at your project (will try it out today) and I noticed, that it is pretty similiar to our efforts for creating a SOA toolkit for .NET. We have two projects that are comparable to your ideas, namely the DataTransferObjectManager (first release already available as BETA, release with more features to come soon) and the ObjectMapper (not released yet). Maybe there are some opportunities in getting together some time. Let me know (servicetoolkit@juliusganns.com) if you have time to take a look at http://servicetoolkit.codeplex.com.

Best,
Julius

jbogard Feb 27, 2009 at 2:58 AM 
@CNemo

Internal design is on my todo list as well...especially extension points. It's been tough as the internal structure has changed quite a bit, but there are a few pieces I'm starting to make non-internal as their design is a little more concrete.

As for enterprise use, I'm not sure how to answer that one, but we've been using it at our current project for around 6 months. I don't really know about any other products, however, so I couldn't really talk about them. Thanks for the interest!

CNemo7539 Feb 26, 2009 at 5:01 PM 
Oh! Cool stuff!

Would be very nice if you can highlight internal design a little bit. Is this aimed for enterprise use? What stand it apart from other products?