我正在尝试使用Azure Functions公开REST API,它使用CSOM和C#从SharePoint Online中的特定术语集返回术语。
我绝对可以从控制台应用程序中调用这个完全相同的CSOM代码,并且能够成功遍历条款并输出到控制台。
但是,当从Azure功能主机调用以下代码时,总是在循环遍历TermCollection
或IEnumerable<Term>
时找到NULL术语对象的集合(我尝试在ClientContext.LoadQuery
上使用TermSet.GetAllTerms()
,并通过TermCollection
属性加载TermSet.Terms
。
一旦迭代器遇到foreach中的一个术语(我也尝试过只是一个LINQ Select),它认为该项为NULL,因此调用它上面的属性会抛出NullReferenceException
。我无法从控制台应用程序调用相同的代码重现行为 - 它只是按预期工作并检索每个Term
对象。
为什么会在Azure Functions主机中发生这种情况,但在控制台应用中却不会发生?我该如何解决这个问题?
更新:有人将此问题标记为“什么是NRE”的副本。我知道NRE的发生方式和方式。这里的问题是为什么当从不同的主机调用SAME CODE时会发生这种情况?我知道抛出NRE的确切行,以及抛出它的对象 - 但不应该这样,因为这段代码可以从控制台应用程序中运行。从Azure Function主机调用时有什么区别?
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();
}
}
}
这是“run.csx”函数,调用上面的代码编译成DLL并放在Azure函数的Bin文件夹中:
#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;
}
}
}
以下是本地调试器的屏幕截图,当使用Azure Functions CLI对其进行调试时(您可以看到它确实在集合中找到了10个项目,但所有项目都为空):