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

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

问题描述

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

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#.

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

但是,当从 Azure Function 主机调用下面的代码时,它总是在循环遍历 TermCollectionIEnumerable<Term>(我尝试在 TermSet.GetAllTerms() 上使用 ClientContext.LoadQuery,以及仅加载 TermCollection 通过 TermSet.Terms 属性).

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

一旦迭代器在 foreach 中遇到一个术语(我也尝试过仅作为 LINQ 选择),它就会认为该项目为 NULL,因此在其上调用属性会引发 NullReferenceException.我无法从控制台应用程序或调用相同代码的 API 应用程序重现该行为 - 它只是在那里按预期工作并检索每个 Term 对象.

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.

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

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?

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

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

我真的很想使用 Azure Functions 来获得消费定价优势,因此我不必在应用服务中托管它.

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

这里是run.csx"函数,调用上面已经编译成DLL并放在Azure函数的Bin文件夹中的代码:

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

项目.json:

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

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

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

推荐答案

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

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

SPO、CSOM 和 PnP-PowerShell.

SPO, CSOM and PnP-PowerShell.

将 PnP-PowerShell 安装到 PowerShell 函数:

Installing PnP-PowerShell to a PowerShell function:

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

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