Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Jesse Andrews 191 posts 716 karma points c-trib
    Mar 09, 2019 @ 01:30
    Jesse Andrews
    0

    Why is UmbracoContext.Current becoming null when calling static helper from inside UmbracoApiController?

    Umbraco Version: 7.12.3

    I have a few static methods that I use to convert IPublishedContent to something that can be returned as json, but for some reason, one of the methods loses the umbracoContext. I know they start out with it, as ToFeedData doesn't ever throw an error (it's called from beneath an UmbracoApiController). See the below code

    public static Dictionary<string, object> ToFeedData(this IPublishedContent content) {
        var data = new Dictionary<string, object> {
            { "id", content.Id },
            { "name", content.Name },
            { "url", content.Url },
            { "urlName", content.UrlName },
            { "createDate", content.CreateDate },
            { "updateDate", content.UpdateDate }
        };
        foreach (var prop in content.Properties) {
            if (prop.HasValue && prop.PropertyTypeAlias != "seoSettings") {
                data.Add(prop.PropertyTypeAlias, ConvertType(prop.Value));
            }
        }
        return data;
    }
    
    public static object ConvertType(Object value) {
        if (value == null) {
            return null;
        }
    
        if (value is JObject) {
            return null;
        }
    
        if (value is IEnumerable<IPublishedContent>) {
            var item = (IEnumerable<IPublishedContent>)value;
            return item.Where(x => x != null).Select(val => val.ToJson());
        }
        if (value is IPublishedContent) {
            var node = (IPublishedContent)value;
            return node.ToJson();
        }
        return value;
    }
    
    public static object ToJson(this IPublishedContent content) {
        if (content is Image) {
            if (UmbracoContext.Current == null) {
                return null;
            }
            return content.ToImageModel();
        }
        else {
            var data = new JObject();
            data["id"] = content.Id;
            data["name"] = content.Name;
            --> data["url"] = content.Url;
            data["urlName"] = content.UrlName;
            data["createDate"] = content.CreateDate;
            data["updateDate"] = content.UpdateDate;
    
            foreach (var prop in content.Properties) {
                if (prop.HasValue && prop.PropertyTypeAlias != "seoSettings") {
                    --> var result = ConvertType(prop.Value);
                    if (result != null) {
                        var alias = prop.PropertyTypeAlias;
                        var type = result.GetType();
                        if (type.IsPrimitive || type == typeof(string)) {
                            data[prop.PropertyTypeAlias] = (dynamic)result;
                        }
                        else if (result is IEnumerable<dynamic>) {
                            data[prop.PropertyTypeAlias] = JArray.FromObject(result);
                        }
                        else {
                            data[prop.PropertyTypeAlias] = JObject.FromObject(result);
                        }
                    }
                }
            }
            return data;
        }
    }
    

    The ToFeedData method is what is the entry point for this and it works fine. However, when it calls ToJson, the UmbracoContext isn't always passed down, which causes content.Url to throw an error about the missing context.

    I added arrows at the two points where I encountered errors. The first arrow is where a null reference error sometimes happens complaining that UmbracoContext.Current is null. The second also complains about a null value, and I'm pretty sure the reason is the same. It's triggering the TextValueConverter, which calls ParseInternalLinks, which encounters the same issue generating urls as the first error.

    This didn't used to be an issue. I believe it happened around the time I added hostnames to the root nodes (the site has multiple root nodes). I tried removing them, but unfortunately it didn't help.

    Any ideas on how to resolve this? I know it's not because of nested content, as the node that throws the error is tied to an actual node.

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Mar 09, 2019 @ 12:54
    Søren Gregersen
    0

    Just pass the context, instead of relying on it the way you are now.

    You need to control your dependencies.

    Are you by any chance running this is in a thread?

  • Jesse Andrews 191 posts 716 karma points c-trib
    Mar 11, 2019 @ 20:20
    Jesse Andrews
    0

    I found the cause of the issue. The problem occurred when I removed all allowed templates from one of the doc types that was being run through this logic. After republishing the affected nodes, their template is still assigned to the old value. Since that template is no longer allowed for that doc type, it caused errors when trying to get the url and the id check I have in place failed to prevent this because the template wasn't set to 0.

    As for why I did this, it made it so that the affected nodes throw a 404 when visited, as they aren't navigable pages. I've filed a bug report here about this issue. I reenabled the template on the doc type as a workaround in the meantime.

  • Jesse Andrews 191 posts 716 karma points c-trib
    Mar 11, 2019 @ 22:32
    Jesse Andrews
    0

    Actually turns out that wasn't the cause, though it is an issue. Made that change and it loaded successfully the first time I tried, but it failed on later attempts.

    I do have the api collecting the data asynchronously, so it likely is running on another thread. I'm a little unsure on how the UmbracoContext should be passed in, as UmbracoContext.Current is readonly. However, I'm pretty sure that's not the problem, as the context exists immediately before the ToJson method is called.

    I think the problem may have been the following line

    return item.Where(x => x != null).Select(val => val.ToJson());
    

    I converted that to

    var result = new JArray();
    foreach (var entry in item.Where(x => x != null)) {
        result.Add(entry.ToJson());
    }
    return result;
    

    and it is reliably working again. Not sure why the linq clause would impact the context though.

  • Søren Gregersen 441 posts 1884 karma points MVP 2x c-trib
    Mar 12, 2019 @ 03:13
    Søren Gregersen
    0

    The difference between the two solutions is simple: the linq returns a statement that has not been executed, and the other one returns a prepared list.

    The .Select(...) is only run when you actually invoke it, either by calling .ToList() / .ToArray() or similar, og when you start iterating it.

Please Sign in or register to post replies

Write your reply to:

Draft