【问题标题】:Getting NULL terms when accesing TermCollection from SharePoint Online via CSOM in an Azure Function在 Azure 函数中通过 CSOM 从 SharePoint Online 访问 TermCollection 时获取 NULL 术语
【发布时间】:2017-02-17 06:53:37
【问题描述】:

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

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

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

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

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

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

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

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

项目.json:

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

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

【问题讨论】:

  • 你能分享你的 project.json 吗?
  • { "frameworks": { "net46":{ "dependencies": { "Microsoft.SharePointOnline.CSOM": "16.1.6112.1200" } } } }
  • 我已经启动了 Fiddler,并比较了控制台应用程序客户端调用 SPO 与函数客户端调用 SPO 的响应。两者都返回了预期的数据。似乎问题出在 JSON 反序列化中?
  • 这也是我的怀疑。这是否与您在自定义程序集中的参考相匹配?
  • 我们可能因为私有程序集使用的加载上下文而遇到问题。只是要尝试一下...您可以尝试将该程序集移动到另一个文件夹(例如根目录下的 sharedbin)并使用该新路径(在本例中为#r“..\sharedbin\CsomTaxonomyHelper.dll”)引用它吗?

标签: azure sharepoint sharepoint-online azure-functions


【解决方案1】:

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

SPO、CSOM 和 PnP-PowerShell。

将 PnP-PowerShell 安装到 PowerShell 函数:

【讨论】:

  • 如何将 PnP PS 模块添加到 Az Functions 中? (我假设不是 nuget pkg,对吧?)
  • 哈哈,这是一个@ling-toh 问题。将 DLL 从 C:\Users\john.liu\AppData\Local\Apps\SharePointPnPPowerShellOnline\Modules\SharePointPnPPowerShellOnline 拖放到函数下的模块子文件夹中。更多注释:johnliu.net/blog/2016/11/… 也。大量的 PnP Dll 将花费更多的存储空间。可能是 0.05 美元。但测试的速度更快。
  • 我能够像您一样创建并成功运行 PowerShell 版本,它按预期返回了我的 MMD 条款。从 C# 脚本宿主运行时,内部 JSON 序列化肯定有问题。
  • 我需要调用 GetAllTerms 真是太可惜了,因为我有嵌套的术语,不希望长时间等待递归调用来加载我的术语,否则我会使用这个 Powershell 版本。
  • PnP-PowerShell 返回与您的 C# 版本相同的 CSOM 对象。你也可以把它们混合起来。 PnP-PowerShell 只是一个包装器。 github.com/SharePoint/PnP-PowerShell/blob/master/Commands/…
猜你喜欢
  • 1970-01-01
  • 2017-04-04
  • 2017-05-31
  • 1970-01-01
  • 1970-01-01
  • 2019-10-23
  • 1970-01-01
  • 2019-04-12
  • 1970-01-01
相关资源
最近更新 更多