If I convert from custom json, should source and object have the same value in a property value converter?
I'm building my first property value converter for a custom data type that stores the model in JSON format and even after reading the very good documentation I still cannot understand when Source and Object are used and why Source matters.
I understood that Object is what is returned by GetPropertyValue and in my case it should return the deserialized JSON object into a .NET class, but what should the Source be? The native representation of my JSON object is either the string (in that case is would be the same as Data) or the deserialized JSON (in which case is would be the same as Object).
In this last few hours I've done a bit of searching and best option is to extend PropertyValueConverterBase and implement the ConvertDataToSource method only.
The original requirement driving it all is this: we want to limit the number of conversions, so that if, in the same view, you invoke Model.GetPropertyValue("foo") twice we run the converter only once. Meaning that, with strongly typed models, you can render @Model.Foo as many times as you want without thinking about performance issues.
This means that we have to cache the results of the conversions, somehow. With the current XML cache, it is easy: we just store the value in the IPublishedContent object, since the cache re-creates these objects for each request anyways.
It is not perfect, though: if a conversion is expensive, and is repeated on every page, ie on every request... this has a cost.
To improve that situation, new versions of the cache (such as NuCache) propose to cache IPublishedContent objects and the converted values accross requests. Ie, there would only be one instance of the content object for content node 1234, shared by all views. And conversion would therefore run only once.
The issue, then, is cache refresh. If conversion is purely local to the content, for example converting a string into a DateTime, then the result of the conversion is valid until the content itself is replaced.
However, if the result of the conversion depends on other content items, eg in the case of a content picker, this is not true anymore. The result of the conversion becomes invalid as soon as any other content changes.
With that in mind, we (I) devised the following mechanism: conversion is a two-step operation, going from 'Raw' to 'Source' and then from 'Source' to 'Object', and 'Source' and 'Object' can be cached at different levels.
The basic example would be: 'Raw' is a string containing the ID of a node, 'Source' is the corresponding integer and can be cached for as long as the content does not change, 'Object' is the target content object and can be cached for as long as no other content changes.
Each converter can declare at which level its 'Source' and 'Object' values should be cached.
What should I implement?
Considering that projects, today, run on the XML cache, the two-steps thing can be safely ignored. Which means that you should decide to implement either 'Raw' to 'Source' or 'Source' to 'Object' and be done with it. As Jeroen says... implement ConvertSourceToObject and be happy.
What about the future?
Practically, no project has ever used the two-steps system, except for my own experiments. And it tends to be cumbersome or not bring the expected improvements. To make it short, it's probably too complex to have variable cache levels per converter.
As we slowly look into merging NuCache into v8, I am looking into refactoring that part. Not sure how to do it and gladly accepting ideas. Latest was: have 'Source' cached at content level (ie cached for as long as the 'Raw' value does not change) and 'Object' not cached at all.
Today I've been doing some more experiments with NuCache. I've been having some issues with MultipleTextStringValueConverter and RteMacroRenderingValueConverter.
If you look at the source code you can see that these do some very different things in ConvertDataToSource and ConvertSourceToObject
NuCache at the moment uses both ConvertDataToSource, and ConvertSourceToObject, and can handle different levels of cache for Source and Object. That was the whole point of having Source and Object, different levels of cache.
Now, in theory, if eg ConvertDataToSource is not implemented, then Source is assumed to be identical to Data, and that is it. Similarly, if ConvertSourceToObject is not implemented, Object is assumed to be identical to Source.
The important difference between NuCache and XmlCache is that NuCache as one and only one copy of a given IPublishedContent which is then shared by all requests. Meaning that anything that is not cached at request level is cached for a loooong time, whereas in XmlCache it is re-calculated on each request. This is where ppl may see differences.
And this is why we have this Data-to-Source then Source-to-Object thing. Source could be cached at content level (eg parsing a JSON string into a JSON structure) and Object at request level (eg the IPublishedContent object corresponding to an ID).
However, I am not entirely convinced that this system is that efficient. And, the good thing with having clean code, it should be quite easy to amend. I'm open to discussions!
If I convert from custom json, should source and object have the same value in a property value converter?
I'm building my first property value converter for a custom data type that stores the model in JSON format and even after reading the very good documentation I still cannot understand when
Source
andObject
are used and whySource
matters.I understood that
Object
is what is returned byGetPropertyValue
and in my case it should return the deserialized JSON object into a .NET class, but what should theSource
be? The native representation of my JSON object is either the string (in that case is would be the same asData
) or the deserialized JSON (in which case is would be the same asObject
).Can someone help clear this for me? Thx
Hello,
I've created some simple property value converters. I always only use ConvertSourceToObject and never had any issues. For example: https://github.com/jbreuer/Hybrid-Framework-for-Umbraco-v7-Best-Practises/blob/master/Umbraco.Extensions/PropertyConverters/ImageCropperConverter.cs
Jeroen
In this last few hours I've done a bit of searching and best option is to extend
PropertyValueConverterBase
and implement theConvertDataToSource
method only.It is then
PropertyValueConverterBase
that copiesSource
toObject
Why 'Source' and 'Object'
The original requirement driving it all is this: we want to limit the number of conversions, so that if, in the same view, you invoke
Model.GetPropertyValue("foo")
twice we run the converter only once. Meaning that, with strongly typed models, you can render@Model.Foo
as many times as you want without thinking about performance issues.This means that we have to cache the results of the conversions, somehow. With the current XML cache, it is easy: we just store the value in the
IPublishedContent
object, since the cache re-creates these objects for each request anyways.It is not perfect, though: if a conversion is expensive, and is repeated on every page, ie on every request... this has a cost.
To improve that situation, new versions of the cache (such as NuCache) propose to cache
IPublishedContent
objects and the converted values accross requests. Ie, there would only be one instance of the content object for content node 1234, shared by all views. And conversion would therefore run only once.The issue, then, is cache refresh. If conversion is purely local to the content, for example converting a string into a
DateTime
, then the result of the conversion is valid until the content itself is replaced.However, if the result of the conversion depends on other content items, eg in the case of a content picker, this is not true anymore. The result of the conversion becomes invalid as soon as any other content changes.
With that in mind, we (I) devised the following mechanism: conversion is a two-step operation, going from 'Raw' to 'Source' and then from 'Source' to 'Object', and 'Source' and 'Object' can be cached at different levels.
The basic example would be: 'Raw' is a string containing the ID of a node, 'Source' is the corresponding integer and can be cached for as long as the content does not change, 'Object' is the target content object and can be cached for as long as no other content changes.
Each converter can declare at which level its 'Source' and 'Object' values should be cached.
What should I implement?
Considering that projects, today, run on the XML cache, the two-steps thing can be safely ignored. Which means that you should decide to implement either 'Raw' to 'Source' or 'Source' to 'Object' and be done with it. As Jeroen says... implement
ConvertSourceToObject
and be happy.What about the future?
Practically, no project has ever used the two-steps system, except for my own experiments. And it tends to be cumbersome or not bring the expected improvements. To make it short, it's probably too complex to have variable cache levels per converter.
As we slowly look into merging NuCache into v8, I am looking into refactoring that part. Not sure how to do it and gladly accepting ideas. Latest was: have 'Source' cached at content level (ie cached for as long as the 'Raw' value does not change) and 'Object' not cached at all.
Making sense?
Thanks for the detailed explanation of the whole system.
So, for now, I'll just implement
ConvertSourceToObject
where source is basically the raw data stored in the property, and in the future we'll see :)You can experiment with NuCache ("Le Cache Nouveau") already. The source code is available here: https://github.com/umbraco/Umbraco-CMS/tree/dev-v7-contentcache
In this video I explain how I've upgraded a project to NuCache: https://youtu.be/DWjbJiIUQdk?t=31m8s
It can be downloaded here: https://github.com/jbreuer/1-1-multilingual-example/tree/NuCache
Jeroen
Today I've been doing some more experiments with NuCache. I've been having some issues with MultipleTextStringValueConverter and RteMacroRenderingValueConverter.
If you look at the source code you can see that these do some very different things in ConvertDataToSource and ConvertSourceToObject
https://github.com/umbraco/Umbraco-CMS/blob/d50e49ad37fd5ca7bad2fd6e8fc994f3408ae70c/src/Umbraco.Web/PropertyEditors/ValueConverters/RteMacroRenderingValueConverter.cs#L64
https://github.com/umbraco/Umbraco-CMS/blob/d50e49ad37fd5ca7bad2fd6e8fc994f3408ae70c/src/Umbraco.Core/PropertyEditors/ValueConverters/TinyMceValueConverter.cs#L20
In the NuCache version the following doesn't work for RTE properties:
The following does work:
In MultipleTextStringValueConverter even only ConvertDataToSource is implemented.
https://github.com/umbraco/Umbraco-CMS/blob/d50e49ad37fd5ca7bad2fd6e8fc994f3408ae70c/src/Umbraco.Core/PropertyEditors/ValueConverters/MultipleTextStringValueConverter.cs#L20
So if NuCache will start using ConvertDataToSource some pieces probably need to be updated because even the core value converters aren't consistent.
Jeroen
Few things.
NuCache at the moment uses both ConvertDataToSource, and ConvertSourceToObject, and can handle different levels of cache for Source and Object. That was the whole point of having Source and Object, different levels of cache.
Now, in theory, if eg ConvertDataToSource is not implemented, then Source is assumed to be identical to Data, and that is it. Similarly, if ConvertSourceToObject is not implemented, Object is assumed to be identical to Source.
The important difference between NuCache and XmlCache is that NuCache as one and only one copy of a given IPublishedContent which is then shared by all requests. Meaning that anything that is not cached at request level is cached for a loooong time, whereas in XmlCache it is re-calculated on each request. This is where ppl may see differences.
And this is why we have this Data-to-Source then Source-to-Object thing. Source could be cached at content level (eg parsing a JSON string into a JSON structure) and Object at request level (eg the IPublishedContent object corresponding to an ID).
However, I am not entirely convinced that this system is that efficient. And, the good thing with having clean code, it should be quite easy to amend. I'm open to discussions!
is working on a reply...