I'm trying to map a collection of Ipublishedcontent which is media. I've got a custom mapper for media types which maps to my custom media model.
The problem i'm having is that when a collection of ipublishedcontent is mapped to a list of a type which has a custom mapper assigned umbraco mapper doesn't check for custom mappings.
Custom mapping only seem to be check for in the map method.
Yes, makes sense, but you should find custom mappings are picked up for mapping collections as well as when mapping from single instances of IPublishedContent - we've certainly used it with that scenario. You can see from the code here that the call to map a collection simply iterates that collection and then calls the same method as used for a single instance - at which point the custom mappings you've registered should be picked up. There's a unit test too to verify this.
If you can post the relevant parts of your code somewhere happy to take a look.
I had a look at the unit test, it is slightly different to my scenario.
for example:-
var mapper = new UmbracoMapper();
mapper.AddCustomMapping(typeof(Media).FullName, MediaMapper.MapMediaFile);
IEnumerable<IPublishedContent> mediaContent = Umbraco.TypedMedia(new string[] {"1234,1235,1236"});
var model = new List<Media>();
mapper.MapCollection(mediaContent,model);
I want to map a collection of IPublishedContent to a collection of type that has a custom mapping. In your unit test the collection is inside an object. I could quite easily move my collection inside an object but just seems like i'm creating an object for the sake of it.
Just for reference i'm mapping from macro parameters so slightly different from the normal setup.
I see what you mean now James. Yes, the mapping code loops through all the properties of the object you are mapping to, and checks the type of each of those for custom mappings. It doesn't check the type of the object itself.
Not come across this yet - I think even if my view model was only a single collection, I'd still look to make a particular view model class for it. Just for naming conventions if nothing else, so I'd have a MediaGalleryViewModel containing an IEnumerable<Media> property or something similar. So you could do that as you say, but there might be an easy way to update the code to get this to work as you have it. I'll take a look and update later.
UPDATE 21/9/14 - removed dll as have now added to 1.4.9 (see comment below).
I've put up an updated dll here James - let me know if this works for you and if so I'll publish the code/update the NuGet package etc.
Seems to work fine in my tests. The only change I've made is this in the MapCollection method:
foreach (var content in contentCollection)
{
var itemToCreate = new T();
// Check for custom mappings for the type itself (in the Map() method we'll check for custom mappings on each property)
// - Any custom mappings used here cannot be based on a single property, as we don't have a property to map to to work out what this should be.
// So we just pass an empty string into the custom mapping call.
var customMappingKey = itemToCreate.GetType().FullName;
if (_customMappings.ContainsKey(customMappingKey))
{
itemToCreate = _customMappings[customMappingKey](this, content, string.Empty, false) as T;
}
else
{
// Otherwise map the single content item as normal
Map(content, itemToCreate, propertyMappings, recursiveProperties, propertySet);
}
modelCollection.Add(itemToCreate);
}
As the comment says, in this case as we are mapping object to object rather than IPublishedContent field to object property, the custom mapping method itself must "know" what property or properties to use. as we can't pass one. So I'm just passing an empty string for that parameter. Seems to work OK so hope you don't mind testing it out before I publish it in case I've missed something obvious.
Sorry for the delay in getting back, What i found was my custom mapper would have to be slightly adapted because if there was an empty string passed it would need to use the contentToMapFrom instead of finding a property from contentToMapFrom. So what i did was to do a check for _customObjectMappings (I already had a custom object mapping setup) which worked straight away. I just thought this was a more practical way because you are passing objects.
if (_customObjectMappings.ContainsKey(customMappingKey))
{
itemToCreate = (T)_customObjectMappings[customMappingKey](this, content);
}
else
{
// Otherwise map the single content item as normal
Map(content, itemToCreate, propertyMappings, recursiveProperties, propertySet);
}
Yes, very true - I didn't like that passing of the empty string, but couldn't see how to use an existing custom mapping without passing something. Even though in this scenario (without a model property, rather a whole model, for mapping) I couldn't avoid providing something for this parameter. But the custom object mapping would work for this as you have done. This wasn't really the intention of these - they were for use when mapping from a dictionary rather than IPublishedContent - but I can't see a good reason not to support what you've done.
Mapping collection custom mapper
I'm trying to map a collection of Ipublishedcontent which is media. I've got a custom mapper for media types which maps to my custom media model.
The problem i'm having is that when a collection of ipublishedcontent is mapped to a list of a type which has a custom mapper assigned umbraco mapper doesn't check for custom mappings.
Custom mapping only seem to be check for in the map method.
Hope this makes sense?
Is this by design or could this be added?
Hi James
Yes, makes sense, but you should find custom mappings are picked up for mapping collections as well as when mapping from single instances of IPublishedContent - we've certainly used it with that scenario. You can see from the code here that the call to map a collection simply iterates that collection and then calls the same method as used for a single instance - at which point the custom mappings you've registered should be picked up. There's a unit test too to verify this.
If you can post the relevant parts of your code somewhere happy to take a look.
Cheers
Andy
Thanks for the quick reply Andy
I had a look at the unit test, it is slightly different to my scenario.
for example:-
I want to map a collection of IPublishedContent to a collection of type that has a custom mapping. In your unit test the collection is inside an object. I could quite easily move my collection inside an object but just seems like i'm creating an object for the sake of it.
Just for reference i'm mapping from macro parameters so slightly different from the normal setup.
I see what you mean now James. Yes, the mapping code loops through all the properties of the object you are mapping to, and checks the type of each of those for custom mappings. It doesn't check the type of the object itself.
Not come across this yet - I think even if my view model was only a single collection, I'd still look to make a particular view model class for it. Just for naming conventions if nothing else, so I'd have a MediaGalleryViewModel containing an IEnumerable<Media> property or something similar. So you could do that as you say, but there might be an easy way to update the code to get this to work as you have it. I'll take a look and update later.
Could you post your implementation of MediaMapper.MapMediaFile please James?
It's the same implementation as your damp custom map i've just added a couple of extra properties.
UPDATE 21/9/14 - removed dll as have now added to 1.4.9 (see comment below).
I've put up an
updated dll hereJames - let me know if this works for you and if so I'll publish the code/update the NuGet package etc.Seems to work fine in my tests. The only change I've made is this in the MapCollection method:
As the comment says, in this case as we are mapping object to object rather than IPublishedContent field to object property, the custom mapping method itself must "know" what property or properties to use. as we can't pass one. So I'm just passing an empty string for that parameter. Seems to work OK so hope you don't mind testing it out before I publish it in case I've missed something obvious.
Cheers
Andy
I've added this code to version 1.4.9 of Umbraco Mapper James, if you or anyone else coming across this post requires this functionality.
Andy
Sorry for the delay in getting back, What i found was my custom mapper would have to be slightly adapted because if there was an empty string passed it would need to use the contentToMapFrom instead of finding a property from contentToMapFrom. So what i did was to do a check for _customObjectMappings (I already had a custom object mapping setup) which worked straight away. I just thought this was a more practical way because you are passing objects.
Yes, very true - I didn't like that passing of the empty string, but couldn't see how to use an existing custom mapping without passing something. Even though in this scenario (without a model property, rather a whole model, for mapping) I couldn't avoid providing something for this parameter. But the custom object mapping would work for this as you have done. This wasn't really the intention of these - they were for use when mapping from a dictionary rather than IPublishedContent - but I can't see a good reason not to support what you've done.
So have added to 1.4.10.
Thanks for the question... and the solution!
Andy
is working on a reply...