6

In a legacy project, there's code that retrieves items like this:

var context = SitecoreContext.GetFromHttpContext();
context.GetItem<Item>(itemId, Language.Current);

For several types of items, this results in a NullReferenceException with the following stack trace:

Glass.Mapper.AbstractService.RunConfigurationPipeline(AbstractTypeCreationContext abstractTypeCreationContext)
Glass.Mapper.AbstractService.InstantiateObject(AbstractTypeCreationContext abstractTypeCreationContext)
Glass.Mapper.Sc.SitecoreService.CreateType(Type type, Item item, Boolean isLazy, Boolean inferType, Dictionary`2 parameters, Object[] constructorParameters)
Glass.Mapper.Sc.SitecoreService.CreateType(Item item, Boolean isLazy, Boolean inferType, Object[] constructorParameters)
Glass.Mapper.Sc.SitecoreService.CreateType(Item item, Boolean isLazy, Boolean inferType)
... soluction code here ...

According to the registered Glass issues I have found, this happens when caching is enabled and referenced item collections are loaded lazily:

https://github.com/mikeedwards83/Glass.Mapper/issues/169 https://github.com/mikeedwards83/Glass.Mapper/issues/247

We use TDS code generation to create Glass models.

Caching is enabled globally in Glass.Mapper.Sc.config:

<setting name="Glass.Mapper.Sc.Caching.Enabled" value="true"/>

We cannot disable caching, as it improves performance. But I think it wouldn't affect us much if we disabled lazy loading.

It looks like lazy loading is enabled by default for all models, unless specified otherwise with Glass model attributes.

Is is possible to disable lazy loading globally? Or is there a better way to solve the errors we're getting?

Glass version is 4.0.5.54.

  • 1
    For what it's worth, I've seen a couple of examples where disabling Glass's lazy loading to profoundly slow down a Sitecore implementation. Let me see if I can find how we did it (and then we promptly reverted that change)... – G Killian Nov 10 '16 at 15:37
  • @GKillian In your case, did you also disable lazy loading because of issues with caching? – Dmytro Shevchenko Nov 10 '16 at 15:58
  • 1
    I think initially it was to avoid stale data during development. I'm checking my notes, but looks like we had it disabled based on Glass.Mapper.Configuration.Attributes.ChildrenAttribute.IsLazy and other API attributes (but not a config setting ootb with Glass) – G Killian Nov 10 '16 at 16:10
5

No, there's not a setting to disable lazy loading globally. However, it is possible to disable lazy loading on all of your models' properties with some custom code.

Eager-Loading Attribute Configuration Loader

This is a custom attribute configuration loader that will iterate over all the attribute configurations in your assemblies and disable lazy loading for any of your model properties that support lazy loading.

The IsLazy property is not implemented on a common AbstractPropertyConfiguration class, but rather separately on several different implementations (e.g., ChildrenConfiguration, LinkedConfiguration), so we have to resort to reflection to force IsLazy to false on the properties that support it.

public class EagerLoadingSitecoreAttributeConfigurationLoader : IConfigurationLoader
{
  private const string IsLazyPropertyName = "IsLazy";
  private readonly SitecoreAttributeConfigurationLoader _attributeConfigLoader;

  public IEnumerable<string> AllowedNamespaces 
  { 
    get { return _attributeConfigLoader.AllowedNamespaces; }
    set { _attributeConfigLoader.AllowedNamespaces = value; }
  }

  public EagerLoadingSitecoreAttributeConfigurationLoader(params string[] assemblies)
  {
    _attributeConfigLoader = new SitecoreAttributeConfigurationLoader(assemblies);
  }

  public IEnumerable<AbstractTypeConfiguration> Load()
  {
    var configurations = _attributeConfigLoader.Load().ToArray();
    ForceEagerLoading(configurations);
    return configurations;
  }

  private static void ForceEagerLoading(IEnumerable<AbstractTypeConfiguration> configurations)
  {
    foreach (var propertyConfig in configurations.SelectMany(c => c.Properties))
    {
      var isLazyProperties = propertyConfig.GetType().GetProperties().Where(IsIsLazyProperty);
      foreach (var isLazyProperty in isLazyProperties)
      {
        isLazyProperty.SetValue(propertyConfig, false);
      }
    }
  }

  private static bool IsIsLazyProperty(PropertyInfo propertyInfo)
  {
    return propertyInfo.CanWrite
           && propertyInfo.PropertyType == typeof(bool)
           && string.Equals(propertyInfo.Name, IsLazyPropertyName, StringComparison.InvariantCultureIgnoreCase);
  }
}

GlassMapperScCustom.cs

Update the GlassLoaders method in GlassMapperScCustom to use the new EagerLoadingSitecoreAttributeConfigurationLoader instead of SitecoreAttributeConfigurationLoader.

public static class GlassMapperScCustom
{
  ...

  public static IConfigurationLoader[] GlassLoaders()
  {
    return new IConfigurationLoader[]
    {
        new EagerLoadingSitecoreAttributeConfigurationLoader("CompanyName.Assembly1"),
        new EagerLoadingSitecoreAttributeConfigurationLoader("CompanyName.Assembly2"),
        new EagerLoadingSitecoreAttributeConfigurationLoader("CompanyName.Assembly3")
    };
  }

  ...
}

Notes

This will not disable lazy loading from the SitecoreService or SitecoreContext. Anywhere that the SitecoreService or SitecoreContext are being used in your code to create or retrieve items with isLazy set to true will still use lazy loading. For that level of control you will have to update to Glass Mapper 4.2.0.184 or greater and implement your own SitecoreContext as far as I know.

Also, this code has not been tested in production so I make no claims about what it'll do to performance :). You can use this to see if it resolves your errors.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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