1

Recently I've been exploring Glass Mapper for Sitecore and stumbled upon a very strange issue.

PROBLEM

Imagine you have an item of MyModel and it contains a few Droplink fields that point to other items. The MyModel item exists only in one language (so are the linked items), i.e. it is a helper item that is used in dropdowns and has only shared fields, so it makes no sense to keep a version of it in every language.

Now, the Glass Mapper team recommends using VersionCountDisabler like this:

using(new VersionCountDisabler()){

    var model =  sitecoreService.GetItem<MyModel>("/sitecore/content/home");

}

But in my project I've noticed that if I don't access any property of model while inside the using statement and the code reaches the closing curly bracket (and the instance of the VersionCountDisabler get disposed of) then all the properties in my model get set back to null. If I, however, access any property while inside the using statement, I will be able to access my model properties even when the VersionCountDisabler is disposed of.

Apparently the proxy object is killed when the VersionCountDisabler is being disposed of.

DESIRED BEHAVIOR

In my world Glass Mapper should keep the field values regardless of me accessing them inside the VersionCountDisabler

2 Answers 2

5

I believe linked items by default are loaded lazily, which is probably what is causing this.

If you first access the property outside the VersionCountDisabler then the items are loaded without it.

An alternative solution to your own answer could be to try and use SitecoreFieldSettings.DontLoadLazily on the properties in question. The items should then all be loaded and mapped inside the VersionCountDisabler.

// Attribute configuration
[SitecoreField(Setting = SitecoreFieldSettings.DontLoadLazily)]
public IList<MyLinkedItem> LinkedItems { get; set; }

// Fluent configuration
Map(x => x.Field(y => y.LinkedItems ).Setting(SitecoreFieldSettings.DontLoadLazily));
2
  • Yes, disabling Lazy Loading on droplink fields seems to have done the trick too, though I wish Glass Mapper had a built-in functionality to Lazy load fields with version count disabled. I am going to test both approaches in the project I am currently working to see which one works better.
    – VadimG
    Commented Jul 13, 2017 at 9:21
  • Yea, I've been looking for similar functionality before but haven't gotten around to creating a solution yet. Commented Jul 13, 2017 at 10:52
0

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 theCopy()` 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);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.