I tried to debug the code and for the properties in the Archetype, it only seems to map by _customObjectMappings or by property name mapping? and not the custom mapping
I've just added following code in the UmbracoMapper.cs line 277ish and it seems it's working.
if (_customObjectMappings.ContainsKey(namedCustomMappingKey))
{ ... }
else if (_customObjectMappings.ContainsKey(unnamedCustomMappingKey))
{ ... }
else if (dictionary[propName] is IPublishedContent && _customMappings.ContainsKey(namedCustomMappingKey))
{
var value = _customMappings[namedCustomMappingKey](this, (IPublishedContent)dictionary[propName], propName, false);
if (value != null)
{
property.SetValue(model, value);
}
}
else if (dictionary[propName] is IPublishedContent && _customMappings.ContainsKey(unnamedCustomMappingKey))
{
var value = _customMappings[unnamedCustomMappingKey](this, (IPublishedContent)dictionary[propName], propName, false);
if (value != null)
{
property.SetValue(model, value);
}
}
else if (dictionary[propName] is IPublishedContent)
{ ... }
else ...
I'll run more test to see this breaks anything but do you see any reason i can't do this here?
I don't see any reason why what you have wouldn't work, but I think it's actually duplicating something that's already there - so I'm not quite sure why what you were originally trying wasn't working.
As you've seen for mappings from IPublishedContent you can supply a custom mapping.
We also - leading from our previous conversations - support mapping from a dictionary of <string, object> which you can use for Archetype mappings.
In the mapping from the dictionary - here - we have a check to see if the object you are mapping from is actually itself an IPublishedContent. If it is, we then call the Map() override that takes an IPublishedContent - and that process should pick up any custom mappings you have defined. So you shouldn't need these additional checks you've added in the code above, as they are already in the Map() method that gets called.
I'll try to replicate the scenario and take a look. Will be on holiday next week from Monday so hopefully before I go, but if not and no further response that'll be why.
OK - have put together a quick example and it does seem to pick the custom mappings up OK. Here's what I've done... maybe you can spot the difference with your scenario and see either how you can amend or how yours is different and so illustraing a use case we may have missed.
Taking the example I used on my blog post as a starting point, I've added a new complex type to the MatchReportTeaser view model class. Not very real-world but something simple to illustrate, I've added a field that holds the Name and URL of the IPublishedContent concantenated together:
public class MatchReportTeaser
{
public string Name { get; set; }
public string Url { get; set; }
public NameAndUrlCombined NameAndUrl { get; set; }
}
public class NameAndUrlCombined
{
public string Value { get; set; }
}
I've then registered a custom mapping for this new NameAndUrlCombined class in my base controller:
Sorry about the late reply, I didn't get to have chance to look at it till now.
I looked into more details and it wasn't working. It looked like it was working because i have changed the property name to URL and it was doing the property name mapping.
Then it goes to UmbracoMapper.cs line 80, then line 107 and start doing property by property mapping.
Okay, I think i just figured this out.
The main difference between my code and your code was, my code look something like this
public class MatchReportTeaser
{
public string Name { get; set; }
public string Url { get; set; }
public string Link { get; set; }
Public int PageId { get; set; }
}
I think in your scenario when it was doing the property matching, it found a type of "NameAndURlCombined"
public NameAndUrlCombined NameAndUrl { get; set; }
Then it searched for the CustomMapping, where in my case, it's just a string and int property.
so the Custom Mapping didn't take place.
public string Link { get; set; }
Public int PageId { get; set; }
I'm not sure what's the best practice here, but having another children in the "MatchReportTeaser" feels like the hierarchy is getting too big?
Really sorry about the late reply, I was hoping i can get back to you yesterday but took me a while to figure this out.
This is not a show stopper so we can discuses this later, mean while i might uncomment the the code above.
Thanks - am back now!... not sure if you've got any further with this but here's a few more thoughts.
Firstly - yes, completely agree the example I've created above with an additional child class in the view model is taking it too far. It was just that I wanted something simple to demonstrate. In your case really you are looking to map to a primitive type - i.e. a string - so ideally you don't want to have to create a class for this.
So there's a couple of things to note here first.
Custom mappings as you've found require a type to map to - you basically say any time you find a particular type on your view model, map it this way, and hence it makes most sense for actual complex types you've created on your view model. Like the geo-coordinate example here. If actually you want a custom mapping for a built-in type - like a string - it's not going to work as well. You only want to use this custom mapping for a particular string, not all strings of course.
One option that is possible is to add a custom mapping for a particular type AND property name. You can do that by passing the propertyName parameter to the AddCustomMapping method. By doing that your custom mapping will only apply to view model properties that match both the type and the name.
So that would work, but still isn't really the best way. If I've understood correctly, you can just apply an override to the standard mapping that maps from IPublishedContent to your view model. By convention, mapping is made using the name - so a field called "Url" will be mapped from the content's Url property.
If in your case your property is called "Link", you can map it from the Url property by providing an override to this convention, either using attributes or by passing a dictionary detailing the overrides you need.
So - to get to the point(!) - I think all you need to do is decorate your view model like this:
public class MatchReportTeaser
{
public string Name { get; set; }
[PropertyMapping(SourceProperty = "Url")]
public string Link { get; set; }
[PropertyMapping(SourceProperty = "Id")]
public int PageId { get; set; }
}
If I've followed you correctly hopefully that helps.
During the week, I hit another issue where i couldn't map archtype within the archtype (2 level archetype). Then I realised I think i was making things too complex by trying to be more custom. Well in my scenario, i didn't really need 2 level archetype. (I was grouping Data Types in in archtype so i can create the DocumentTypes easy. )
Then i went back to the issue above. Initially i was worried about having a complex data type and trying to map that to the custom object. I think if i stick with the Umbraco Data Type, and its naming convention then i don't think i will ever have this issue.
Currently i still have the code i written in my previous post, but I'll look into this again.
Custom Mapping doesn't seem to work for the property types in Archetype
Hi guys,
I'm trying to custom map the property in the Archetype, but it doesn't seems to work.
I've got similar scenario from Andy's blog here
http://web-matters.blogspot.com.au/2014/08/using-umbraco-mapper-with-archetype_8.html
But difference is instead of
I have
and I have this in the Base controller
I tried to debug the code and for the properties in the Archetype, it only seems to map by _customObjectMappings or by property name mapping? and not the custom mapping
Just wondering why is this?
Shinsuke
I've just added following code in the UmbracoMapper.cs line 277ish and it seems it's working.
I'll run more test to see this breaks anything but do you see any reason i can't do this here?
Hi Shinsuke
I don't see any reason why what you have wouldn't work, but I think it's actually duplicating something that's already there - so I'm not quite sure why what you were originally trying wasn't working.
As you've seen for mappings from IPublishedContent you can supply a custom mapping.
We also - leading from our previous conversations - support mapping from a dictionary of <string, object> which you can use for Archetype mappings.
In the mapping from the dictionary - here - we have a check to see if the object you are mapping from is actually itself an IPublishedContent. If it is, we then call the Map() override that takes an IPublishedContent - and that process should pick up any custom mappings you have defined. So you shouldn't need these additional checks you've added in the code above, as they are already in the Map() method that gets called.
I'll try to replicate the scenario and take a look. Will be on holiday next week from Monday so hopefully before I go, but if not and no further response that'll be why.
Cheers
Andy
OK - have put together a quick example and it does seem to pick the custom mappings up OK. Here's what I've done... maybe you can spot the difference with your scenario and see either how you can amend or how yours is different and so illustraing a use case we may have missed.
Taking the example I used on my blog post as a starting point, I've added a new complex type to the MatchReportTeaser view model class. Not very real-world but something simple to illustrate, I've added a field that holds the Name and URL of the IPublishedContent concantenated together:
I've then registered a custom mapping for this new NameAndUrlCombined class in my base controller:
And implemented it like this:
Sure enough in my view I see the value coming out as expected:
I.e.
<div>Custom mapping test: Go Further|/go-further/</div>
Hope that helps resolve it.
Cheers
Andy
Thank you for your reply Andy,
i just commented out the code i've written aaaaand it seems to be working now.
hmmm weird one, i spent most friday trying to get this to work.
I'll look into details tomorrow morning.
maybe something was cached >.<
Shinsuke
Hi Andy,
Sorry about the late reply, I didn't get to have chance to look at it till now.
I looked into more details and it wasn't working. It looked like it was working because i have changed the property name to URL and it was doing the property name mapping.
So i went in debugging and i got to this point
UmbracoMapper.cs line 279 ish
Then it goes to UmbracoMapper.cs line 80, then line 107 and start doing property by property mapping.
Okay, I think i just figured this out.
The main difference between my code and your code was, my code look something like this
I think in your scenario when it was doing the property matching, it found a type of "NameAndURlCombined"
Then it searched for the CustomMapping, where in my case, it's just a string and int property.
so the Custom Mapping didn't take place.
I'm not sure what's the best practice here, but having another children in the "MatchReportTeaser" feels like the hierarchy is getting too big?
Really sorry about the late reply, I was hoping i can get back to you yesterday but took me a while to figure this out.
This is not a show stopper so we can discuses this later, mean while i might uncomment the the code above.
Cheers
Shinsuke
Edit
Enjoy your holiday :)
Thanks - am back now!... not sure if you've got any further with this but here's a few more thoughts.
Firstly - yes, completely agree the example I've created above with an additional child class in the view model is taking it too far. It was just that I wanted something simple to demonstrate. In your case really you are looking to map to a primitive type - i.e. a string - so ideally you don't want to have to create a class for this.
So there's a couple of things to note here first.
Custom mappings as you've found require a type to map to - you basically say any time you find a particular type on your view model, map it this way, and hence it makes most sense for actual complex types you've created on your view model. Like the geo-coordinate example here. If actually you want a custom mapping for a built-in type - like a string - it's not going to work as well. You only want to use this custom mapping for a particular string, not all strings of course.
One option that is possible is to add a custom mapping for a particular type AND property name. You can do that by passing the propertyName parameter to the AddCustomMapping method. By doing that your custom mapping will only apply to view model properties that match both the type and the name.
So that would work, but still isn't really the best way. If I've understood correctly, you can just apply an override to the standard mapping that maps from IPublishedContent to your view model. By convention, mapping is made using the name - so a field called "Url" will be mapped from the content's Url property.
If in your case your property is called "Link", you can map it from the Url property by providing an override to this convention, either using attributes or by passing a dictionary detailing the overrides you need.
So - to get to the point(!) - I think all you need to do is decorate your view model like this:
If I've followed you correctly hopefully that helps.
Cheers
Andy
Hi Andy
thank you for your reply.
yes that make sense and thank you.
During the week, I hit another issue where i couldn't map archtype within the archtype (2 level archetype). Then I realised I think i was making things too complex by trying to be more custom. Well in my scenario, i didn't really need 2 level archetype. (I was grouping Data Types in in archtype so i can create the DocumentTypes easy. )
Then i went back to the issue above. Initially i was worried about having a complex data type and trying to map that to the custom object. I think if i stick with the Umbraco Data Type, and its naming convention then i don't think i will ever have this issue.
Currently i still have the code i written in my previous post, but I'll look into this again.
Thank you
Shinsuke
is working on a reply...