Я пытаюсь открыть API REST с помощью Azure Functions, который возвращает термины с определенного термина в SharePoint Online с использованием CSOM и C#.Получение NULL-терминов при использовании TermCollection из SharePoint Online через CSOM в функции Azure
Я могу определенно вызывать этот точный код CSOM из консольного приложения и из приложения Azure API, и он может циклически выполнять термины и выводить на консоль или HTTP-ответ.
Однако, когда код ниже вызывается из Azure функции хоста, он ВСЕГДА найти коллекцию долгосрочных объектов NULL, когда зацикливание через TermCollection
или IEnumerable<Term>
(я попытался с помощью ClientContext.LoadQuery
на TermSet.GetAllTerms()
, а также просто загрузив TermCollection
через свойство TermSet.Terms
).
Как только итератор обращается к термину в foreach (который я также пробовал просто как LINQ Select), он считает, что элемент имеет значение NULL, поэтому вызывающие свойства на нем вызывают NullReferenceException
. Я не могу воспроизвести поведение из консольного приложения или из приложения API-приложения в тот же код - он просто работает так, как ожидалось, и извлекает каждый объект Term
.
Почему это происходит, когда SAME CODE вызывается из разных хостов? Почему это происходит на хосте Azure Functions, но не в приложении Console или в приложении Azure API?
В чем разница при вызове с узла Azure Function?
Я бы очень хотел использовать Azure Functions для преимуществ ценообразования на потребление, поэтому мне не нужно размещать это в службе App.
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 и помещаются в папке 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;
}
}
}
Project.json:
{
"frameworks": {
"net46":{
"dependencies": {
"Microsoft.SharePointOnline.CSOM": "16.1.6112.1200"
}
}
}
}
Вот скриншот локального отладчика, при использовании Azure функции CLI для отладки это (вы можете увидеть, что он нашел 10 пунктов в коллекции, но все элементы равны нулю):
Не могли бы вы поделиться своим project.json? –
{ "рамки": { "net46": { "зависимости": { "Microsoft.SharePointOnline.CSOM": "16.1.6112.1200 " } } } } –
Я активировал Fiddler и сравнивал ответы клиента консольного приложения, вызывающего SPO, и клиента функции, вызывающего SPO. BOTH возвратил ожидаемые данные. Похоже, проблема в JSON-десериализация? –