0

I am trying to expose a REST API using Azure Functions which returns terms from a specific termset in SharePoint Online using CSOM and C#.

I can definitely invoke this exact same CSOM code from a console app and it is able to loop through the terms and output to console successfully.

However, when the code below is invoked from the Azure Function host, it ALWAYS find a collection of NULL term objects, when looping through the TermCollection or the IEnumerable<Term> (I’ve tried by using ClientContext.LoadQuery on TermSet.GetAllTerms(), as well as by just loading the TermCollection via the TermSet.Terms property).

As soon as the iterator hits a term in the foreach (which I’ve also tried as just a LINQ Select), it thinks that the item is NULL, so calling properties on it throws the NullReferenceException. I cannot reproduce the behavior from the console app calling into the same code - it just works as expected there and retrieves each Term object.

Why would this happen in the Azure Functions host, but not in Console app? How can I fix this??

UPDATE: Someone marked this question as a Duplicate of "what is an NRE". I know what and how NREs occur. The question here is why is this happening when SAME CODE is invoked from different hosts?? I know the exact line where the NRE is thrown, and what object is throwing it - but it should not be, as this code works as is from a console app. What is the difference when invoked from an Azure Function host??

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Taxonomy;

namespace CsomTaxonomyHelper
{
    public class TermSearch
    {
        private readonly ClientContext ctx;
        public TermSearch(ClientContext context)
        {
            if (context == null)
                throw new ArgumentNullException(nameof(context));

            ctx = context;
        }

        public IEnumerable<TermViewModel> GetTerms(Guid termSetId)
        {
            var taxonomySession = TaxonomySession.GetTaxonomySession(ctx);
            var termStore = taxonomySession.GetDefaultSiteCollectionTermStore();
            var termSet = termStore.GetTermSet(termSetId);

            //get flat list of terms, so we don't make recursive calls to SPO            
            var allTerms = ctx.LoadQuery(termSet.GetAllTerms().IncludeWithDefaultProperties());
            ctx.ExecuteQuery();

            return ToViewModel(allTerms);
        }

        static IEnumerable<TermViewModel> ToViewModel(IEnumerable<Term> allTerms)
        {
            var results = allTerms.Select(term => new TermViewModel
            {
                Id = term.Id, //BOOM! <-- within the context of an Azure Function the "allTerms" IEnumerable is a list of nulls
                Name = term.Name,
                ParentId = TryGetParentId(term)

            });

            return results;
        }

        static Guid? TryGetParentId(Term term)
        {
            try
            {
                if (term.Parent.IsPropertyAvailable("Id"))
                    return term.Parent.Id;
            }
            catch (ServerObjectNullReferenceException) { }
            return null;
        }
    }

    public class PasswordString
    {
        public SecureString SecurePassword { get; private set; }
        public PasswordString(string password)
        {
            SecurePassword = new SecureString();
            foreach (char c in password.ToCharArray())
            {
                SecurePassword.AppendChar(c);
            }
            SecurePassword.MakeReadOnly();
        }
    }
}

Here's the "run.csx" function, invoking the code above which has been compiled into a DLL and placed in the Bin folder of the Azure Function:

#r "CsomTaxonomyHelper.dll"
#r "Newtonsoft.Json"

using System.Net;
using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.Taxonomy;
using CsomTaxonomyHelper;
using Newtonsoft.Json;

static TraceWriter _log = null;
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log)
{
    _log = log;
    _log.Info("C# HTTP trigger function processed a request. Getting mmd terms from SPO...");


    var terms = GetFocusAreas();
    var result = JsonConvert.SerializeObject(terms);

    return req.CreateResponse(HttpStatusCode.OK, result);
}

static IEnumerable<TermViewModel> GetFocusAreas()
{
    string spSiteUrl = System.Environment.GetEnvironmentVariable("SPOSiteUrl", EnvironmentVariableTarget.Process);
    string userName = System.Environment.GetEnvironmentVariable("SPOUserName", EnvironmentVariableTarget.Process);
    string password = System.Environment.GetEnvironmentVariable("SPOPassword", EnvironmentVariableTarget.Process);

    var securePwd = new PasswordString(password).SecurePassword;

    using (var ctx = new ClientContext(spSiteUrl))
    {
        ctx.Credentials = new SharePointOnlineCredentials(userName, securePwd);
        ctx.ExecuteQuery();

        _log.Info("Logged into SPO service.");

        var search = new TermSearch(ctx);
        try
        {
            var result = search.GetTerms(new Guid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"));
            return result;
        }
        catch (Exception ex)
        {
            _log.Error(ex.Message, ex);
            throw;
        }
    }
}

Here's the screenshot of the local debugger, when using the Azure Functions CLI to debug this (you can see that it did find 10 items in the collection, but all items are null):

local debugger with Azure Function CLI

Thiago Silva
  • 14,183
  • 3
  • 36
  • 46
  • @Peter Duniho - this is not a duplication of "What is an NRE". Please re-read this question in its entirety. – Thiago Silva Feb 13 '17 at 18:34
  • Have you tried remote debugging? – Andy T Feb 13 '17 at 19:31
  • @AndrésNava-.NET yes, I am using the Azure Function CLI and see the same behavior locally. It's very bizarre how the same code in a class library behaves differently when invoked from the AF vs. a console app – Thiago Silva Feb 13 '17 at 20:53
  • Until you can present your question in a more specific way than "why am I getting a `NullReferenceException`?", it is very much a duplicate of the marked question. If you want help, you need to get beyond that. Post a good [mcve] that reliably reproduces the problem, and explain _in detail_ what you've done to debug the problem, what you've found, and what you still need help with. Simply knowing that there's a `null` reference you didn't expect isn't good enough. – Peter Duniho Feb 13 '17 at 21:10
  • @PeterDuniho I understand and appreciate why you think this seems like a dupe - but my question is not "Why am I getting a NRE", it's "Why does this same code yield 2 different results within different hosts, NRE in AzureFunction and valid results in console app." I would really appreciate if you remove the "Duplicate" mark for this question as I think there's a bug in the Azure Function host when executing SharePoint CSOM code and this mark will just bury this question as people tend to dismiss dupes. – Thiago Silva Feb 13 '17 at 21:16
  • @PeterDuniho I've modified the title and added further code and clarification - please reconsider removing the duplicate mark. thanks! – Thiago Silva Feb 13 '17 at 21:32
  • @PeterDuniho That's the point - if I could explain why the null elements, I would not be asking here. The SharePoint CSOM code works in console app, and collection returns with non-null Term objects. But, under the Azure Function host, the SharePoint Online server execution seems to fail. I have explained what I've done to debug - I run it in a local emulator of Azure Functions (the CLI) which reproduces the issue. Also, I run it locally in a console app, which gives me the data as expected. The code above is complete and verifiable - but you must have access to a SharePoint Online tenant. – Thiago Silva Feb 13 '17 at 22:44
  • @PeterDuniho I also just realized that when I use the URL from this question on Twitter, it pulls the title and description of the issue you linked as being duplicate to. Not cool. I've got a legitimate question, unrelated to the one you marked as dupe, and it's impacting the visibility of this question. – Thiago Silva Feb 16 '17 at 17:37
  • 1
    Possible duplicate of [Getting NULL terms when accesing TermCollection from SharePoint Online via CSOM in an Azure Function](http://stackoverflow.com/questions/42281084/getting-null-terms-when-accesing-termcollection-from-sharepoint-online-via-csom) – Thiago Silva Feb 17 '17 at 00:51

0 Answers0