Well, I am not sure if it's a bug in Glass Mapper or not (I think it's not), but there seems to be an article on the Internet that kind of addresses the problem: https://ctor.io/shared-field-references-in-glass-mapper/
However, the article is rather outdated (it has been written for Glass.Mapper V3) and it covers only the attribute part of the problem without even touching the FluentAPI which I prefer to attributes.
So I decided to extend the solution proposed there and add support for FluentAPI mappings.
I took the SitecoreSharedFieldTypeMapper
and SitecoreSharedFieldConfiguration
almost as they are (The SitecoreSharedFieldConfiguration' does not have the
Copy()` override anymore, so I removed it) and added two more classes:
public class SitecoreSharedField<T> : AbstractPropertyBuilder<T, SitecoreSharedFieldConfiguration>
{
public SitecoreSharedField(Expression<Func<T, object>> ex) : base(ex)
{
this.Configuration.FieldName = this.Configuration.PropertyInfo.Name;
}
public SitecoreSharedField<T> IsCodeFirst()
{
this.Configuration.CodeFirst = true;
return this;
}
public SitecoreSharedField<T> FieldId(string id)
{
return this.FieldId(new ID(id));
}
public SitecoreSharedField<T> FieldId(Guid id)
{
return this.FieldId(new ID(id));
}
public SitecoreSharedField<T> FieldId(ID id)
{
this.Configuration.FieldId = id;
return this;
}
public SitecoreSharedField<T> FieldName(string name)
{
this.Configuration.FieldName = name;
return this;
}
public SitecoreSharedField<T> FieldSource(string fieldSource)
{
this.Configuration.FieldSource = fieldSource;
return this;
}
public SitecoreSharedField<T> FieldTitle(string fieldTitle)
{
this.Configuration.FieldTitle = fieldTitle;
return this;
}
public SitecoreSharedField<T> FieldType(SitecoreFieldType fieldType)
{
this.Configuration.FieldType = fieldType;
return this;
}
public SitecoreSharedField<T> IsShared()
{
this.Configuration.IsShared = true;
return this;
}
public SitecoreSharedField<T> IsUnversioned()
{
this.Configuration.IsUnversioned = true;
return this;
}
public SitecoreSharedField<T> CustomFieldType(string fieldType)
{
this.Configuration.CustomFieldType = fieldType;
return this;
}
public SitecoreSharedField<T> Setting(SitecoreFieldSettings setting)
{
this.Configuration.Setting = setting;
return this;
}
public SitecoreSharedField<T> SectionName(string sectionName)
{
this.Configuration.SectionName = sectionName;
return this;
}
public SitecoreSharedField<T> ReadOnly()
{
this.Configuration.ReadOnly = true;
return this;
}
public SitecoreSharedField<T> FieldSortOrder(int order)
{
this.Configuration.FieldSortOrder = order;
return this;
}
public SitecoreSharedField<T> SectionSortOrder(int order)
{
this.Configuration.SectionSortOrder = order;
return this;
}
public SitecoreSharedField<T> ValidationErrorText(string text)
{
this.Configuration.ValidationErrorText = text;
return this;
}
public SitecoreSharedField<T> ValidationRegularExpression(string regex)
{
this.Configuration.ValidationRegularExpression = regex;
return this;
}
public SitecoreSharedField<T> IsRequired()
{
this.Configuration.IsRequired = true;
return this;
}
}
and
public static class SitecoreTypeExtensions
{
public static SitecoreSharedField<T> SharedField<T>(this SitecoreType<T> type, Expression<Func<T, object>> ex)
{
SitecoreSharedField<T> sitecoreField = new SitecoreSharedField<T>(ex);
type.Config.AddProperty((AbstractPropertyConfiguration)sitecoreField.Configuration);
return sitecoreField;
}
}
The first one simply adds a proper instance SitecoreSharedFieldConfiguration
class to the field configuration and the second one is an extension method for SitecoreType
that adds the new field to the FluentApi.
Now, whenever I have an item in Sitecore which I want glass mapper to take any available language version of and get the values from it, I use the following FluentApi configuration:
var themeConfiguration = loader.Add<Core.Models.Theme>();
themeConfiguration.TemplateId(TemplateIDs.MaterialThemeTemplate);
themeConfiguration.SharedField(x => x.DarkerPrimaryColor).FieldId(FieldIDs.DarkerPrimaryColor);
themeConfiguration.SharedField(x => x.LighterPrimaryColor).FieldId(FieldIDs.LighterPrimaryColor);
themeConfiguration.SharedField(x => x.PrimaryColor).FieldId(FieldIDs.PrimaryColor);
themeConfiguration.SharedField(x => x.SecondaryColor).FieldId(FieldIDs.SecondaryColor);
And each individual color is mapped like this:
var colorConfiguration = loader.Add<Core.Models.Color>();
colorConfiguration.TemplateId(TemplateIDs.ColorTemplate);
colorConfiguration.Field(x => x.ColorHex).FieldId(FieldIDs.ColorHex);
colorConfiguration.Field(x => x.CssClass).FieldId(FieldIDs.CssClass);
colorConfiguration.Field(x => x.TextColor).FieldId(FieldIDs.TextColor);