在Azure函数中通过CSOM从SharePoint Online访问TermCollection时获取NULL术语 [英] Getting NULL terms when accesing TermCollection from SharePoint Online via CSOM in an Azure Function

查看:82
本文介绍了在Azure函数中通过CSOM从SharePoint Online访问TermCollection时获取NULL术语的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Azure函数公开REST API,该API使用CSOM和C#从SharePoint Online中的特定术语集中返回术语.

我绝对可以从控制台应用程序和Azure API应用程序调用完全相同的CSOM代码,并且它可以循环浏览这些术语并成功输出到控制台或HTTP响应.

但是,当从Azure Function主机调用以下代码时,当遍历TermCollectionIEnumerable<Term>时,它总是查找NULL术语对象的集合(我已经尝试通过在TermSet.GetAllTerms()上使用ClientContext.LoadQuery,以及仅通过TermSet.Terms属性加载TermCollection).

一旦迭代器在foreach中击中一个词(我也曾尝试过使用LINQ Select),它就会认为该项目为NULL,因此对其调用属性会抛出NullReferenceException.我无法从控制台应用程序或API应用程序调用相同的代码来重现该行为-它仅能按预期工作并检索每个Term对象.

为什么从不同的主机调用SAME CODE时会发生这种情况? 为什么会在Azure Functions主机中而不是在控制台应用程序或Azure API应用程序中发生这种情况?

从Azure Function主机调用时有什么区别?

我真的很想使用Azure Functions来获得使用定价的好处,因此我不必将其托管在App Service中.

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;
        }
    }
}

Project.json:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.SharePointOnline.CSOM": "16.1.6112.1200"
      }
    }
  }
}

这是使用Azure Functions CLI调试时的本地调试器的屏幕截图(您可以看到它确实在集合中找到10个项目,但所有项目均为空):

解决方案

不是解决方案,而是增加了对话-我能够使用PnP-PowerShell(2017年2月)进行测试.条款已添加.

SPO,CSOM和PnP-PowerShell.

将PnP-PowerShell安装到PowerShell函数:

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 from an Azure API app and it is able to loop through the terms and output to console or the HTTP response 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 or from the API app calling into the same code - it just works as expected there and retrieves each Term object.

Why is this happening when SAME CODE is invoked from different hosts?? Why would this happen in the Azure Functions host, but not in Console app or the Azure API app?

What is the difference when invoked from an Azure Function host??

I would really like to use Azure Functions for the consumption pricing benefits, so I don't have to host this in an App Service.

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;
        }
    }
}

Project.json:

{
  "frameworks": {
    "net46":{
      "dependencies": {
        "Microsoft.SharePointOnline.CSOM": "16.1.6112.1200"
      }
    }
  }
}

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):

解决方案

Not the solution, but adding to the conversation - I was able to test with PnP-PowerShell (2017-Feb). Terms were just added.

SPO, CSOM and PnP-PowerShell.

Installing PnP-PowerShell to a PowerShell function:

这篇关于在Azure函数中通过CSOM从SharePoint Online访问TermCollection时获取NULL术语的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆