AutoMapper does not like properties of type EntityCollection<T>. I've just spent a few hours rummaging around through AutoMapper source code and found that an entitycollection when cast to IListSource returns an EntityView from GetList().
This causes the first issue which is that ListSourceMapper calls Clear on that returned IList which throws a NotSupportedException.
If you work through that (the how not being important just now) the next problem you have is that EntityCollection properties of your entities are all readonly and AutoMapper tries to SET them to a new entitycollection.
AutoMapper is not a joy to extend. Rather than making the ConfigurationProvider settable (if you can't set why the smeg even have one???). Instead there are a bunch of wacky static delegates that return an array of all the type mappers and the mappers used by automapper. This is where you can start fiddling.
Annoying caveat: All the AutoMappers type mappers are "private" classes. So you can't "modify" any of them. I copied them all out of the source and re-implemented them (so to speak) with my changes. Fortunately there are only five.
ListSourceMapper.GetEnumerableFor was changed to:
protected override IList GetEnumerableFor(object destination)
{
if (destination is IList)//newline
return (IList)destination;//newline
var listSource = (IListSource)destination;
return listSource.GetList();
}
Secondly the PropertyMapMappingStrategy.AssignValue was modified like so:
protected virtual void AssignValue(PropertyMap propertyMap, object mappedObject, object propertyValueToAssign)
{
if (!propertyMap.UseDestinationValue && propertyMap.CanBeSet)
propertyMap.DestinationProperty.SetValue(mappedObject, propertyValueToAssign);
else//newline
{//newline
if (!propertyMap.CanBeSet && propertyValueToAssign is IEntityCollection2)//newline
{//newline
var ec = (IEntityCollection2)propertyMap.DestinationProperty.GetValue(mappedObject);//newline
ec.AddRange((IEntityCollection2)propertyValueToAssign);//newline
}//newline
}//newline
}
Now you can automap entities. You could do it before but u ended up having to map the collections seperately and then add in the entities to the objects collections manually. Which sucked.
Heres a semi-complete example (altered automapper classes not included - see the attachment):
class Program
{
static void Main(string[] args)
{
Contact c = new Contact();
c.CreditCards = new List<SalesOrderHeader>();
c.CreditCards.Add(new SalesOrderHeader() { Comment = "4444333322221111" });
AutoMapper.Mappers.TypeMapObjectMapperRegistry.AllMappers = () => new AutoMapper.Mappers.ITypeMapObjectMapper[]
{
//new AutoMapper.Mappers.TypeMapObjectMapperRegistry.CustomMapperStrategy(),
//new AutoMapper.Mappers.TypeMapObjectMapperRegistry.NullMappingStrategy(),
//new AutoMapper.Mappers.TypeMapObjectMapperRegistry.CacheMappingStrategy(),
new NewObjectPropertyMapMappingStrategy()//,
//new AutoMapper.Mappers.TypeMapObjectMapperRegistry.ExistingObjectMappingStrategy()
};
AutoMapper.Mappers.MapperRegistry.AllMappers = () => new AutoMapper.IObjectMapper[]
{
new AutoMapper.Mappers.DataReaderMapper(),
new AutoMapper.Mappers.TypeMapMapper(AutoMapper.Mappers.TypeMapObjectMapperRegistry.AllMappers()),
new AutoMapper.Mappers.StringMapper(),
new AutoMapper.Mappers.FlagsEnumMapper(),
new AutoMapper.Mappers.EnumMapper(),
new AutoMapper.Mappers.ArrayMapper(),
new AutoMapper.Mappers.EnumerableToDictionaryMapper(),
new AutoMapper.Mappers.DictionaryMapper(),
new ListSourceMapper(),
new AutoMapper.Mappers.EnumerableMapper(),
new AutoMapper.Mappers.AssignableMapper(),
new AutoMapper.Mappers.TypeConverterMapper(),
new AutoMapper.Mappers.NullableMapper()
};
AutoMapper.Mapper.CreateMap<Contact, ContactEntity>()
.ForMember(p => p.SalesOrderHeader, p => p.MapFrom(x => x.CreditCards))
;
AutoMapper.Mapper.CreateMap<SalesOrderHeader, SalesOrderHeaderEntity>();
IList foo = new EntityCollection<SalesOrderHeaderEntity>();
foo.Add(new SalesOrderHeaderEntity());
var b = AutoMapper.Mapper.Map<Contact, ContactEntity>(c);
Console.WriteLine(b.SalesOrderHeader.Count);
Console.WriteLine(b.SalesOrderHeader[0].Comment);
Console.Read();
}
}
public class Contact
{
public List<SalesOrderHeader> CreditCards
{
get;
set;
}
}
public class SalesOrderHeader
{
public string Comment
{
get;
set;
}
}
Hope this helps someone. Enjoy!
Attachments
Filename |
File size |
Added on |
Approval |
AutomapperTest.zip
|
5,246 |
02-Mar-2010 08:28.31 |
Approved |