Search code examples
c#linuxmononancydotliquid

Inconsistent Behavior in Mono 3.2 with DotLiquid Collections on Nancy Web Solution


I'm seeing some strangely inconsistent results in my application under specific circumstances when rendering out a liquid template. For the record, I'm on Ubuntu 12.10, Mono 3.2.3 and in the latest version of Dotliquid (1.7). I've made a couple of small overrides to Dotliquid which I'll outline below and the reason for them:

In the DotLiquidViewEngine, I've inserted the following:

if (model is ResponseModel)
{
    hashedModel = new Hash((model as ResponseModel).ToHash());
}
else
{
    hashedModel = Hash.FromAnonymousObject(new
                  {
                      Model = new DynamicDrop(model),
                      ViewBag = new DynamicDrop(renderContext.Context.ViewBag)
                  });
}

The point of this slight change is so that I don't have to type {{ model.myobject.property }} and I can use {{ myobject.property }} instead.

The ResponseModel object is a Dictionary. The part that starts to deviate a bit from the happy path is that I've created an object that inherits from DotLiquid.Drop, and also implements IDictionary. This way I can access a list of objects in 3 different ways:

{{ mylist.list_item_key.property }}

{{ mylist["list_item_key"].property }}

{% foreach list in mylist %}
    {% if list.handle == 'list_item_key' %}
        {{ list.property }}
    {% endif %}
{% endfor %}

(I'll paste this generic collection code below.)

The problem I'm seeing is this: the code I have provided works every single time in a Windows environment. In a Linux hosting environment running the latest version of Mono, this code works sometimes, and other times it doesn't.

The only pattern that I can find is that once Apache is restarted, whatever happens on the first page request (whether the lists render correctly or not), this behavior will occur on every subsequent page request until I've restarted Apache again. When it fails, it is only the first two approaches listed above that fail, the third approach always works no matter what. When I see a failure, this is the error I see:

Liquid error: Array index is out of range. 

I get the same inconsistent results in Mono regardless of running Ubuntu or CentOS. I've tried executing the code in debug vs release mode. I've even tried compiling through Visual Studio and Xamarin to see if it would help. Same result no matter what.

The only other pieces of information that may be relevant are that the solution runs on Nancy and is using StructureMap for the IoC. These are both on the most recent versions out of Nuget.

I'm rather stuck on this, so any insight is very appreciated. Below is the code from the generic collection that implements the Drop:

public class GenericCollectionDrop : Drop, IDictionary<string, object>
{
    private Dictionary<string, object> _collection = null;

    public GenericCollectionDrop()
    {
        _collection = new Dictionary<string, object>();
    }

    public override object BeforeMethod(string method)
    {
        if (this.ContainsKey(method))
            return this[method];
        return base.BeforeMethod(method);
    }

    public void Add(string key, object value)
    {
        _collection.Add(key, value);
    }

    public bool ContainsKey(string key)
    {
        return _collection.ContainsKey(key);
    }

    public ICollection<string> Keys
    {
        get { return _collection.Keys; }
    }

    public bool Remove(string key)
    {
        return _collection.Remove(key);
    }

    public bool TryGetValue(string key, out object value)
    {
        return _collection.TryGetValue(key, out value);
    }

    public ICollection<object> Values
    {
        get { return _collection.Values; }
    }

    public object this[string key]
    {
        get
        {
            return _collection[key];
        }
        set
        {
            _collection[key] = value;
        }
    }

    public void Add(KeyValuePair<string, object> item)
    {
        _collection.Add(item.Key, item.Value);
    }

    public void Clear()
    {
        _collection.Clear();
    }

    public bool Contains(KeyValuePair<string, object> item)
    {
        return _collection.Contains(item);
    }

    public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
    {
        throw new NotImplementedException();
    }

    public int Count
    {
        get { return _collection.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(KeyValuePair<string, object> item)
    {
        return _collection.Remove(item.Key);
    }

    public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
    {
        return _collection.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _collection.Values.GetEnumerator();
    }
}

I have also tried substituting the class above, with the same results, with the solution provided in this post: Dictionaries and DotLiquid


Solution

  • This is a very specific question regarding a few open-source components that aren't widely adopted yet, and on a platform that most people would say isn't suitable for a production environment. I doubt many people will come searching for an answer to this same situation, but I have a solution in case anybody does:

    The version of DotLiquid I'm running is 1.7. The notes on version 1.8 (still in beta) looked promising and I figured I could work another solution to achieve the results around the safe interface models. However, simply replacing DotLiquid 1.7 with 1.8 beta seems to have resolved the issue without having to make any code changes on my end.

    Personally, in my opinion the only thing worse than not understanding a problem is not understanding why a particular problem is fixed so perhaps in the future I'll dig through the source and see what changed underneath. For now, upgrading the version, even to beta, has eliminated the issue in Linux/Apache/Mono entirely and the above solution works beautifully.