Writing Value Mappers
Question: Do you need a value mapper?
Answer: Most of the time, no.
Value mappers help uSync to understand how to map IDs between two Umbraco installations. If you don't store content or media IDs within your property editor then you don't need a value mapper.
If you do store IDs, then store them as UDI
or GUID
values. uSync ensures that all items being synced share their GUID values, so if you reference something by GUID on one site, it will still reference that same item on the synchronized site.
If you are using uSync and storing as GUID values then you shouldn't need to write a mapper (unless you are using uSync.publisher)
If you do need to write a value mapper, check out the uSync Mappers Source, which has all the default mappers in it.
Dependencies
If you plan to use uSync.Publisher and you are storing IDs for media or content then you will need to write a value mapper so that uSync can calculate the items needed to render your content on the target site.
A Basic Value Mapper
Never never never store Integer IDs in your property editors. The example below shows you how to transferer these IDs, but just don't do it then you won't need this code.
Value mappers are very similar to Umbraco Deploy ValueConnectors (uSync came first). So for simplicity, we are building our code for the example BadMedia Property Editor, which stores a Media ID in a field.
public class BadMediaValueMapper : SyncValueMapperBase, ISyncMapper
{
public BadMediaValueMapper(IEntityService entityService)
: base(entityService)
{ }
public override string Name => "Bad Media Value Mapper";
public override string[] Editors => new string[] { "BadMediaPicker " };
public override string GetExportValue(object value, string editorAlias)
{
if (value == null)
return string.Empty;
var attempt = value.TryConvertTo<int>();
if (!attempt || attempt.Result <= 0)
return string.Empty;
var getKeyAttempt = entityService.GetKey(attempt.Result, UmbracoObjectTypes.Media);
if (getKeyAttempt.Success)
{
var udi = new GuidUdi(Constants.UdiEntityType.Media, getKeyAttempt.Result);
return udi.ToString();
}
return string.Empty;
}
public override string GetImportValue(string value, string editorAlias)
{
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
if (GuidUdi.TryParse(value, out GuidUdi udi) && udi.Guid != Guid.Empty)
{
var getIdAttempt = entityService.GetId(udi.Guid, UmbracoObjectTypes.Media);
if (getIdAttempt)
{
return getIdAttempt.Result.ToString();
}
}
return string.Empty;
}
}
Dependency Mapping,
So, value mapping is a really bad example, because you should never store Integer IDs in your editors. A more realistic scenario, is when you use uSync publisher, and you need uSync to know that your property editor referenced media or other content, so that it can be transferred along with your content.
Writing a Custom Dependency Check
If you are storing the data in a more complicated way, then you will need to write a custom dependency check. The best place to start may be the uSync Source Code, which contains all the default mappers so you can see how it works.
If your mapper stores a single UDI or a comma separated list of UDIs, you can override the UdiPickerMapper class, and add a custom editor with no extra dependency code.
public class UdiListValueMapper : UdiPickerMapper
{
public override string[] Editors
=> new string[] { "MyUdiListValueMapper" };
public UdiListValueMapper(IEntityService entityService)
: base(entityService) {}
}
If you do need to write your own checker, then dependency checking is an additional call on the value mapper.
public override IEnumerable<uSyncDependency> GetDependencies(object value, string editorAlias, DependencyFlags flags)
The example below would assume your data is stored as JSON blob, and you would have to get the individual UDI values out of it.
public override IEnumerable<uSyncDependency> GetDependencies(object value, string editorAlias, DependencyFlags flags)
{
var stringValue = GetValueAs<string>(value);
if (string.IsNullOrEmpty(stringValue))
return Enumerable.Empty<uSyncDependency>();
var jsonValue = JsonConvert.DeserializeObject<JObject>(stringValue);
if (jsonValue == null)
return Enumerable.Empty<uSyncDependency>();
// do something here to get your ids, etc
List<uSyncDependency> dependencies = new List<uSyncDependency>();
if (flags.HasFlag(DependencyFlags.IncludeDependencies))
{
// dependencies are things like doctypes, or datatypes that
// are needed based on the internal content,
// you don't need to say you depend on the propertyEditor itself as
// that is included as part of the Content/Media Item.
// base class has helper to add the dependency for you.
dependencies.Add(CreateDependency(myUdi, flags));
}
if (flags.HasFlag(DependencyFlags.IncludeMedia)) {
// if you store media, then include when this
// flag is set and the media will be sent across
// base class has helper to add the dependency for you.
dependencies.Add(CreateDependency(myUdi, flags));
}
if (flags.HasFlag(DependencyFlags.IncludeLinked)) {
// if you link to content, then include that dependency here.
// base class has helper to add the dependency for you.
dependencies.Add(CreateDependency(myUdi, flags));
}
return dependencies;
}