2017-02-16 1 views
0

Я пытаюсь открыть 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 пунктов в коллекции, но все элементы равны нулю):

local debugger with Azure Function CLI

+0

Не могли бы вы поделиться своим project.json? –

+0

{ "рамки": { "net46": { "зависимости": { "Microsoft.SharePointOnline.CSOM": "16.1.6112.1200 " } } } } –

+0

Я активировал Fiddler и сравнивал ответы клиента консольного приложения, вызывающего SPO, и клиента функции, вызывающего SPO. BOTH возвратил ожидаемые данные. Похоже, проблема в JSON-десериализация? –

ответ

0

enter image description here

Не решение, но добавление к разговору - я был в состоянии проверить с PnP-PowerShell (2017-февраль). Термины были добавлены.

SPO, CSOM и PnP-PowerShell.

Установка PnP-PowerShell для функции PowerShell:

Drag and drop from windows to modules subfolder

+0

Как вы добавляете модуль PnP PS в функции Az? (Я предполагаю, что не nuget pkg, не так ли?) –

+0

ха-ха, это вопрос @ ling-toh. Перетащите DLL из C: \ Users \ john.liu \ AppData \ Local \ Apps \ SharePointPnPPowerShellOnline \ Modules \ SharePointPnPPowerShellOnline в подпапку модулей под этой функцией. Другие примечания здесь: http://johnliu.net/blog/2016/11/build-your-pnp-site-provisioning-with-powershell-in-azure-functions-and-run-it-from-flow тоже. много PnP Dll будут стоить больше места. Может быть 0,05 доллара. Но это просто быстрее, чтобы проверить вещи. –

+0

Мне удалось создать и успешно запустить версию PowerShell, как и вы, и она вернула мои условия MMD, как ожидалось. Во внутренней сериализации JSON во время работы с хостом сценария C# должно быть что-то неправильное. –

Смежные вопросы