PiranhaCMS / piranha.core

Piranha CMS is the friendly editor-focused CMS for .NET that can be used both as an integrated CMS or as a headless API.

Home Page:http://piranhacms.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Deserialization issue in AliasService when using distributed cache

hedronn opened this issue · comments

I am seeing the following exception raised when upgrading from v10.4 to v11.0

It appears to be an issue with Newtonsoft Deserialize of an enumerable of a specific type, I have tried clearing my cache and creating a new empty page object in Piranha CMS but still see the same issue (see details below). The project works using v10.4, but fails afer upgrade to v11.0 any thoughts?!?

[Exception]

Newtonsoft.Json.JsonSerializationException
HResult=0x80131500
Message=Cannot create and populate list type System.Linq.Enumerable+SelectListIterator`2[Piranha.Models.Alias,Piranha.Services.AliasService+AliasUrlCacheEntry]. Path '$values', line 1, position 172.
Source=Newtonsoft.Json
StackTrace:
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateNewList(JsonReader reader, JsonArrayContract contract, Boolean& createdFromNonDefaultCreator) in /_/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs:line 1223

[Newtonsoft.Json.Serialization.JsonSerializerInternalReader]

// some types like non-generic IEnumerable can be serialized but not deserialized
if (!contract.CanDeserialize)
{
throw JsonSerializationException.Create(reader, "Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.CreatedType));
}

[Type]

System.Linq.Enumerable+SelectListIterator`2[[Piranha.Models.Alias, Piranha, Version=11.0.0.0, Culture=neutral, PublicKeyToken=null],[Piranha.Services.AliasService+AliasUrlCacheEntry, Piranha, Version=11.0.0.0, Culture=neutral, PublicKeyToken=null]], System.Linq, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

From the error message it appears the a list that is a result of a linq query (.Where(x => ...)) is being serialized directly without calling .ToList() before assigning the value. Is this error triggered internally from Piranha or is it from custom code in your project?

EDIT

By quickly looking at the AliasService we don't appear to be caching aliases in a collection but rather one by one, so I don't think we would be serializing a list of aliases internally.

Thanks for the prompt reply Håkan, I will investigate further and report back, but as I mentioned its the same project / custom code, working under v10.4 but not after upgrade to v11.0, that is what made me think this was as a result of a change to Piranha v11.0 updates.

From what I can tell looking at the Call Stack the exception is raised in Piranha.Core when the _cache.Get<IEnumerable> call in AliasService calls the DistributedCache class Get method fails due to the JsonConvert.DeserializeObject throwing an exception (see code below) when attempting to return the aliasUrls.

I don't think this is a cache issue as this code works in v10.4 of Piranha but fails in v11.0 for my project.

Piranha.Services.AliasService

/// <summary>
/// Gets the aliases for the specified site.
/// </summary>
private async Task<IEnumerable<AliasUrlCacheEntry>> GetAliasUrls(Guid siteId)
{
    if (_cache != null)
    {
        var aliasUrls = _cache.Get<IEnumerable<AliasUrlCacheEntry>>($"Piranha_AliasUrls_{siteId}");

        if (aliasUrls == null)
        {
            var aliases = await _repo.GetAll(siteId).ConfigureAwait(false);
            aliasUrls = aliases.Select(x => new AliasUrlCacheEntry
            {
                Id = x.Id,
                AliasUrl = x.AliasUrl
            });

            _cache.Set($"Piranha_AliasUrls_{siteId}", aliasUrls);
        }
        return aliasUrls;
    }
    return null;
}

Piranha.Cache.DistributedCache

/// <inheritdoc />
public T Get<T>(string key)
{
    var json = _cache.GetString(key);

    if (!string.IsNullOrEmpty(json))
    {
        return JsonConvert.DeserializeObject<T>(json, _jsonSettings);
    }
    return default(T);
}

From the stack I'm guessing the error is here:

aliasUrls = aliases.Select(x => new AliasUrlCacheEntry
{
   Id = x.Id,
   AliasUrl = x.AliasUrl
});

According to the exception changing it to the following would probably fix the error:

aliasUrls = aliases.Select(x => new AliasUrlCacheEntry
{
   Id = x.Id,
   AliasUrl = x.AliasUrl
}).ToList();

I'm assuming the error comes from an update in Newtonsoft since we updated to the latest version, but since we run integration tests with both memory and distributed cache I'll have to check why this error wasn't captured by the tests.

I'm assuming the error comes from an update in Newtonsoft since we updated to the latest version, but since we run integration tests with both memory and distributed cache I'll have to check why this error wasn't captured by the tests.

Thanks Håkan for taking the time to look into this issue, I look forward to the fix in v11.x