BaseController / BaseViewModel with Hijacked controllers
Hi Lee,
I've got Ditto up and running using a hijacked controller and the IPublishedContentModelFactory. I'm now wondering how I go about mapping common elements throughout the site such as my navigation which is a multi-node treepicker on my homepage node. Would I require another type converter to map a list of nodes to my NavigationItem model which just has two properties of 'Name' and 'Url'.
Would love some clarification on this as I'm a bit confused!
I'm assuming that your base POCO has a property using IEnumerable<NavigationItem> (or list, or array)?
Unfortunately you wont be able to assign a TypeConverter to a generic collection, so you will need to add a TypeConverter attribute property using it.
[TypeConverter(typeof(NavigationItemListTypeConverter))]
public IEnumerable<NavigationItem> Navigation { get; set; }
In this case NavigationItemListTypeConverter would be your custom TypeConverter. (Obviously I'm guessing your property name)
In your custom TypeConverter, you would have the value of the property (I'm not sure how multi-node treepicker stores it's value these days... guessing CSV?). Then from there you'd take the CSV value and call UmbracoHelper.TypedContent to get a IPublishedContent collection ... and then construct your list of NavigationItem objects.
Thanks for the help guys. I was looking at the hybrid framework last night and the way base viewmodels/controllers are done there and I couldn't seem to figure out how to combine bits from both to make something work.
What I currently have is this:
public class HomePageController : RenderMvcController
{
public override ActionResult Index(RenderModel model)
{
if(!(model.Content is HomePage))
{
var homePage = model.Content.As<HomePage>();
model = new RenderModel(homePage, model.CurrentCulture)
}
return base.Index(model);
}
}
but what I really want is for each of my controllers (HomePageController, ProductListingController, etc) to inherit from some kind of base controller which maps my primary nav / footer nav / widgets etc, then allows me to map extra things to my model before returning the model.
So something like:
public class HomePageController : BaseController
{
public override ActionResult Index(RenderModel model)
{
if(!(model.Content is HomePage))
{
var homePage = model.Content.As<HomePage>();
model = new RenderModel(homePage, model.CurrentCulture)
}
// somehow get base model with mapped properties
// maybe map something extra
// return model with base mapped properties plus automapped properties from current RenderModel
}
}
Hope this makes sense. The reason for the base controller mapping is that my navigation multi-node treepicker is on my HomePage node but I want the IEnumerable
Quick check, did you say that you were already using the DittoPublishedContentModelFactory?
If so, then you can remove the if(!(model.Content is HomePage)) check from your controller :-)
With respect to having common/base properties - Ditto will handle the mapping of those.
Do your HomePage and ProductListing classes inherit from a shared base class?
If so, in that base class, create the properties that you want. If they are of complex types, then they'll need custom TypeConverters, (as mentioned in my first reply), otherwise Ditto will automatically map them (using Umbraco core APIs).
Thinking about it some more... if you're using the DittoPublishedContentModelFactory, and unless you're doing additional logic - you don't even need to use hijacked controllers. Get rid of all the ceremony.
I had a google for the ValueFactory exception message... turns out there's an issue with .NET's Lazy<T>, which Umbraco core is probably using, I'm not sure of the finer details of the inner workings.
At this point my advice would be to remove the ToList() and have:
var navigationNodes = helper.TypedContent(navigationIds.Split(','));
See if that works without error.
Failing that, if you can post the full stack-trace, that might help pinpoint the underlying issue.
I'm wondering if there is something suspect with the value of navigationIds - are you able to debug it and check the value, to make sure that it actually is a CSV (as opposed to an XML or JSON string ... I'm guessing here).
Alternatively you could look at using Jeavon's Core PropertyValueConverters - as this will convert the MNTP value to an IEnumerable<IPublishedContent> collection. Then in your custom TypeConverter you will have access to that object type, rather than dealing with string-parsing and UmbracoHelper.
invalid navigationIds value was my first thought so I made sure that was correctly populated, which it is, the value is something like 1085,1087 which correctly matches the nodes selected in my MNTP.
I can give you the stack trace when I get home from work. Really hoping we can solve this one as Ditto works beautifully with the bread and butter automatic model mapping.
Joining the party, a bit late. We do definitively use Lazy a lot to run the converters. Would be really intersted in a detailed stack trace for the "value factory" exception. I think that what you're doing with the type converters etc... are pushing the core to the limits, which is fine ;-)
Note: that exception basically means that the code that generates the value of the Lazytried to use that value. It could happen if a converter triggered some sort of loop code that would cause it to be called again... so... stack trace would be great!
Hmmm, I'm wondering if it is getting caught in a loop too. As in the model that is trying to be created (via Ditto/ModelFactory) is trying to create a nested instance of itself... if that makes sense?
Meaning that whatever the NavigationItemListTypeConverter.ConvertFrom logic is trying to do, (assuming that it is creating a list of IPublishedContent objects for links?), then it has a nodeId reference to the containing node.
If that's the case, then I'm a worried, as I'm not sure of a resolution :-(
Let me know what the values of the nodeId and the 'NavigationItemList' property.
My next post was going to explain that :) You've got it spot on though I think its getting caught up in a loop because my BaseViewModel has a recursive UmbracoProperty for 'primaryNavigation' which uses a NavigationItemListTypeConverter. The Converter uses UmbracoHelper (to load the MNTP ids) which is where the issue is caused I think.
I've got small fix in place I'm not massively happy with using the Our.Umbraco.PropertyEditorConverters, tweaking the Converter AND then manually new'ing up my BaseViewModel(ipublishedcontentnode) instead of using As<>().
I was thinking of just pulling out all the navigation from my baseviewmodel into a simple service as there's potentially a lot of overhead, for example when loading up a list of child pages, it would end up populating all the navigation fields for each one...
Make sense... any thoughts?
Been away from Umbraco for a bit just getting back in to v7 and trying to pick a ModelBuilder (lots of options!) but liked the look of Ditto.
@Lee, @Aaron: looking at Aaron's exception stack... it definitively is entering a loop. It tries to get a content from the cache, which triggers the creation of a model for that content, and it seems that the creation of the model does in turn tries to get content from the cache... and at some point it tries to get a content that it is already getting.
I don't see an easy resolution, apart from making sure to not access the cache when creating the model (ie don't access the cache in the ctor, do it lazily when the content is actually needed).
Point is... the cache is not really re-entrant at that point. And I'm not sure how to make the XML cache re-entrant at that point.
Would like to figure out whether this (a) is a constraint that can be accepted or (b) is going to be a blocking issue at some point.
Unfortunately not good news, it's looking more and more that Ditto doesn't play well with the ModelFactory approach.
Due to Ditto using reflection to populate the POCO models, there is a performance hit when mapping the properties - this isn't really noticeable when doing a single node/POCO, but becomes much more noticeable when using the ModelFactory. Which is why I adding caching (using the RequestCache) to Ditto's ModelFactory implementation.
By removing the caching from here, I think, would resolve the looping issue... however the performance would be much worse - worse to the point that you'd think "What's the point in using Ditto's ModelFactory?"
Unless someone knows of a better way?
@Aaron - have you looked at Stephan's Zbu.ModelsBuilder as an alternative?
@Lee: to get the factory to handle loops, we'd need to keep track of which content has been converted already - which is complicated by the fact that the current XML cache does create a new instance of IPublishedContent each time you GetById(). So the cache keeps generating new objects - and that probably is the reason why you have implemented some cache in the factory, so that all those objects are mapped to the same model.
Caching per request should be OK to some extend - not sure.
There's no quick and easy fix to that one. I'm currently deep into refactoring the XML cache (and introducing a new cache) and working on all sorts of similar consistency questions...
The solution would probably be to make sure that the cache returns one unique instance of an IPublishedContent. The current XML cache would make sure that it creates one instance per request, and the new cache would store instances directly. Each IPublishedContent would then be mapped only once, therefore you wouldn't need to cache in the factory, and we could implement loop-detection in the factory.
BaseController / BaseViewModel with Hijacked controllers
Hi Lee,
I've got Ditto up and running using a hijacked controller and the IPublishedContentModelFactory. I'm now wondering how I go about mapping common elements throughout the site such as my navigation which is a multi-node treepicker on my homepage node. Would I require another type converter to map a list of nodes to my NavigationItem model which just has two properties of 'Name' and 'Url'.
Would love some clarification on this as I'm a bit confused!
Hi Martin,
Take a read of a gist I wrote up for someone about how to map the more complex property-editor types:
https://gist.github.com/leekelleher/513c48c634fc44729900
I'm assuming that your base POCO has a property using
IEnumerable<NavigationItem>
(or list, or array)?Unfortunately you wont be able to assign a
TypeConverter
to a generic collection, so you will need to add aTypeConverter
attribute property using it.In this case
NavigationItemListTypeConverter
would be your customTypeConverter
. (Obviously I'm guessing your property name)In your custom
TypeConverter
, you would have the value of the property (I'm not sure how multi-node treepicker stores it's value these days... guessing CSV?). Then from there you'd take the CSV value and callUmbracoHelper.TypedContent
to get aIPublishedContent
collection ... and then construct your list ofNavigationItem
objects.I hope this makes sense so far?
Cheers,
- Lee
Hello,
If you want an example on how to use a base controller / base model have a look at this blog post (for Umbraco 6): http://24days.in/umbraco/2013/hybrid-framework/
There is a complete solution available as an example project.
Hybrid Framework Umbraco 6
Hybrid Framework Umbraco 7
The Hybrid Framework for Umbraco 7 uses the Models Builder, but Ditto could also be used.
Jeroen
Thanks for the help guys. I was looking at the hybrid framework last night and the way base viewmodels/controllers are done there and I couldn't seem to figure out how to combine bits from both to make something work.
What I currently have is this:
but what I really want is for each of my controllers (HomePageController, ProductListingController, etc) to inherit from some kind of base controller which maps my primary nav / footer nav / widgets etc, then allows me to map extra things to my model before returning the model.
So something like:
Hope this makes sense. The reason for the base controller mapping is that my navigation multi-node treepicker is on my HomePage node but I want the IEnumerable
Hello,
What you're trying to do is exactly what the Hybrid Framework solves. Did you read the blog post?
Jeroen
Hi Martin,
Quick check, did you say that you were already using the
DittoPublishedContentModelFactory
?If so, then you can remove the
if(!(model.Content is HomePage))
check from your controller :-)With respect to having common/base properties - Ditto will handle the mapping of those.
Do your
HomePage
andProductListing
classes inherit from a shared base class?If so, in that base class, create the properties that you want. If they are of complex types, then they'll need custom
TypeConverters
, (as mentioned in my first reply), otherwise Ditto will automatically map them (using Umbraco core APIs).Cheers,
- Lee
Thinking about it some more... if you're using the
DittoPublishedContentModelFactory
, and unless you're doing additional logic - you don't even need to use hijacked controllers. Get rid of all the ceremony.Lee, I'm getting the following exception when using the typeconverter:
Here's my code:
Anything I'm doing wrong?
Hi Martin,
I had a google for the
ValueFactory
exception message... turns out there's an issue with .NET'sLazy<T>
, which Umbraco core is probably using, I'm not sure of the finer details of the inner workings.At this point my advice would be to remove the
ToList()
and have:See if that works without error.
Failing that, if you can post the full stack-trace, that might help pinpoint the underlying issue.
Thanks,
- Lee
Hi Lee,
Removing the
ToList()
from that line causes the exception to be thrown further down in the execution on the following line:as soon as it tries to access the
navigationNodes
object.Perhaps you could share how you've accomplished common site elements such as Navigations in ditto projects you've worked on?
Hi Martin,
On my recent sites, I've used Archetype for the navigation (rather than a MNTP). I use the same approach that I put in the gist:
https://gist.github.com/leekelleher/513c48c634fc44729900
I'm wondering if there is something suspect with the value of
navigationIds
- are you able to debug it and check the value, to make sure that it actually is a CSV (as opposed to an XML or JSON string ... I'm guessing here).Alternatively you could look at using Jeavon's Core PropertyValueConverters - as this will convert the MNTP value to an
IEnumerable<IPublishedContent>
collection. Then in your custom TypeConverter you will have access to that object type, rather than dealing with string-parsing and UmbracoHelper.Cheers,
- Lee
Hi Lee,
invalid
navigationIds
value was my first thought so I made sure that was correctly populated, which it is, the value is something like1085,1087
which correctly matches the nodes selected in my MNTP.I can give you the stack trace when I get home from work. Really hoping we can solve this one as Ditto works beautifully with the bread and butter automatic model mapping.
Joining the party, a bit late. We do definitively use Lazy a lot to run the converters. Would be really intersted in a detailed stack trace for the "value factory" exception. I think that what you're doing with the type converters etc... are pushing the core to the limits, which is fine ;-)
Note: that exception basically means that the code that generates the value of the Lazytried to use that value. It could happen if a converter triggered some sort of loop code that would cause it to be called again... so... stack trace would be great!
See: http://stackoverflow.com/questions/6300398/invalidoperationexception-in-my-lazy-value-factory
See also: http://www.davesquared.net/2012/12/threading-and-lazy-t.html (or, I might have to check the LazyThreadSafetyMode of some of those Lazy)
Please... Stack Trace!
Hi all,
Just thought I'd add to this as I've stumbled across the same issue, first off here's my stack trace:
Let me know if you need anything else,
Aaron.
Thanks Aaron,
Hmmm, I'm wondering if it is getting caught in a loop too. As in the model that is trying to be created (via Ditto/ModelFactory) is trying to create a nested instance of itself... if that makes sense?
Meaning that whatever the
NavigationItemListTypeConverter.ConvertFrom
logic is trying to do, (assuming that it is creating a list ofIPublishedContent
objects for links?), then it has a nodeId reference to the containing node.If that's the case, then I'm a worried, as I'm not sure of a resolution :-(
Let me know what the values of the nodeId and the 'NavigationItemList' property.
Thanks,
- Lee
Hi Lee, thanks for the quick response.
My next post was going to explain that :) You've got it spot on though I think its getting caught up in a loop because my BaseViewModel has a recursive UmbracoProperty for 'primaryNavigation' which uses a NavigationItemListTypeConverter. The Converter uses UmbracoHelper (to load the MNTP ids) which is where the issue is caused I think.
I've got small fix in place I'm not massively happy with using the Our.Umbraco.PropertyEditorConverters, tweaking the Converter AND then manually new'ing up my BaseViewModel(ipublishedcontentnode) instead of using As<>().
I was thinking of just pulling out all the navigation from my baseviewmodel into a simple service as there's potentially a lot of overhead, for example when loading up a list of child pages, it would end up populating all the navigation fields for each one...
Make sense... any thoughts?
Been away from Umbraco for a bit just getting back in to v7 and trying to pick a ModelBuilder (lots of options!) but liked the look of Ditto.
@Lee, @Aaron: looking at Aaron's exception stack... it definitively is entering a loop. It tries to get a content from the cache, which triggers the creation of a model for that content, and it seems that the creation of the model does in turn tries to get content from the cache... and at some point it tries to get a content that it is already getting.
I don't see an easy resolution, apart from making sure to not access the cache when creating the model (ie don't access the cache in the ctor, do it lazily when the content is actually needed).
Point is... the cache is not really re-entrant at that point. And I'm not sure how to make the XML cache re-entrant at that point.
Would like to figure out whether this (a) is a constraint that can be accepted or (b) is going to be a blocking issue at some point.
Unfortunately not good news, it's looking more and more that Ditto doesn't play well with the ModelFactory approach.
Due to Ditto using reflection to populate the POCO models, there is a performance hit when mapping the properties - this isn't really noticeable when doing a single node/POCO, but becomes much more noticeable when using the ModelFactory. Which is why I adding caching (using the
RequestCache
) to Ditto's ModelFactory implementation.See code comments here: DittoPublishedContentModelFactory.cs#L49
(Which goes against Stephan's WARNING about factories caching models - sorry!)
By removing the caching from here, I think, would resolve the looping issue... however the performance would be much worse - worse to the point that you'd think "What's the point in using Ditto's ModelFactory?"
Unless someone knows of a better way?
@Aaron - have you looked at Stephan's Zbu.ModelsBuilder as an alternative?
Cheers,
- Lee
@Lee: to get the factory to handle loops, we'd need to keep track of which content has been converted already - which is complicated by the fact that the current XML cache does create a new instance of IPublishedContent each time you GetById(). So the cache keeps generating new objects - and that probably is the reason why you have implemented some cache in the factory, so that all those objects are mapped to the same model.
Caching per request should be OK to some extend - not sure.
There's no quick and easy fix to that one. I'm currently deep into refactoring the XML cache (and introducing a new cache) and working on all sorts of similar consistency questions...
The solution would probably be to make sure that the cache returns one unique instance of an IPublishedContent. The current XML cache would make sure that it creates one instance per request, and the new cache would store instances directly. Each IPublishedContent would then be mapped only once, therefore you wouldn't need to cache in the factory, and we could implement loop-detection in the factory.
Making sense?
@Stephan - makes sense and sounds good to me! :-)
Thanks,
- Lee
is working on a reply...