【问题标题】:Proper way to detect if a ClientObject property is already retrieved/initialized检测 ClientObject 属性是否已被检索/初始化的正确方法
【发布时间】:2014-09-05 06:45:01
【问题描述】:

如果您使用 SharePoint 中的客户端对象模型并访问尚未初始化或已由

Context.Load(property); 
Context.ExecuteQuery();

你会得到例如:

Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException

集合尚未初始化。它没有被请求或 请求尚未执行。

例外。

是否有任何适当的方法可以在访问这些属性之前检查它们是否已经初始化/检索?没有 Try/Catch 方法。我不喜欢那个。

我想在抛出异常之前检查并处理它。

我已经检查过了

IsObjectPropertyInstantiated

IsPropertyAvailable

方法,但它们并没有真正的帮助。 IsPropertyAvaiable 只检查标量属性,不会给出结果,例如 Web.ListsIsObjectPropertyInstantiatedWeb.Lists 返回 true,尽管 Web.Lists 未初始化。

【问题讨论】:

    标签: c# sharepoint csom


    【解决方案1】:

    我会说你的问题在某种程度上已经包含了正确的答案。

    为了确定是否加载了客户端对象属性,可以使用以下方法:

    测试

    测试用例 1:仅加载标量属性

    ctx.Load(ctx.Web, w => w.Title);
    ctx.ExecuteQuery();
    //Results:
    ctx.Web.IsObjectPropertyInstantiated("Lists")  False
    ctx.Web.IsPropertyAvailable("Title")    True
    

    测试用例 2:仅加载复合属性

    ctx.Load(ctx.Web, w => w.Lists);
    ctx.ExecuteQuery();
    //Results:
    ctx.Web.IsObjectPropertyInstantiated("Lists")  True
    ctx.Web.IsPropertyAvailable("Title")    False
    

    测试用例 3:同时加载标量和复合属性

    ctx.Load(ctx.Web, w=>w.Lists,w=>w.Title);
    ctx.ExecuteQuery();
    //Results
    ctx.Web.IsObjectPropertyInstantiated("Lists")  True
    ctx.Web.IsPropertyAvailable("Title")    True
    


    如何动态判断客户端对象属性是否加载?

    由于ClientObject.IsPropertyAvailableClientObject.IsObjectPropertyInstantiated 方法期望将属性名称指定为字符串值,这可能导致拼写错误,我通常更喜欢以下extension method

    public static class ClientObjectExtensions
    {
        /// <summary>
        /// Determines whether Client Object property is loaded
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="clientObject"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static bool IsPropertyAvailableOrInstantiated<T>(this T clientObject, Expression<Func<T, object>> property)
            where T : ClientObject
        {
            var expression = (MemberExpression)property.Body;
            var propName = expression.Member.Name;
            var isCollection = typeof(ClientObjectCollection).IsAssignableFrom(property.Body.Type);
            return isCollection ? clientObject.IsObjectPropertyInstantiated(propName) : clientObject.IsPropertyAvailable(propName);
        }
    }
    

    用法

    using (var ctx = new ClientContext(webUri))
    {
    
         ctx.Load(ctx.Web, w => w.Lists, w => w.Title);
         ctx.ExecuteQuery();
    
    
         if (ctx.Web.IsPropertyAvailableOrInstantiated(w => w.Title))
         {
             //...
         }
    
         if (ctx.Web.IsPropertyAvailableOrInstantiated(w => w.Lists))
         {
             //...
         }
    } 
    

    【讨论】:

    • 问题提到即使 Lists 没有真正初始化, IsObjectPropertyInstantiated 也会返回 true。你检查了吗?
    • 是的,至少在未请求 Lists 属性时它返回 false(测试用例 1)。针对 SharePoint 2013 测试
    • 这是一个不错的小扩展方法,效果很好!
    • OfficeDev PnP 现在包含 EnsurePropertyEnsureProperties 方法。
    • 启动c# 6也可以使用nameof关键字
    【解决方案2】:

    Vadim Gremyachev 提供的测试仅涵盖一半的场景 - 您使用 ctx.Load。但是当您使用 ctx.LoadQuery 时,结果会发生变化:

    var query = from lst in ctx.Web.Lists where lst.Title == "SomeList" select lst;
    var lists = ctx.LoadQuery(query);
    ctx.ExecuteQuery();
    ctx.Web.IsObjectPropertyInstantiated("Lists") -> True
    ctx.Web.Lists.ServerObjectIsNull -> False
    ctx.Web.Lists.Count -> CollectionNotInitializedException
    

    因此,一旦对集合调用 LoadQuery,您将无法再查看该集合是否实际可用。

    在这种情况下,唯一的方法是检测异常发生。

    【讨论】:

      【解决方案3】:

      使用扩展的想法很棒,但仅适用于列表。扩展可以在“对象”和“标量”属性之间进行选择。我认为这种方式比扩展更好:

      public static bool IsPropertyAvailableOrInstantiated<T>(this T clientObject, Expression<Func<T, object>> property)
          where T : ClientObject
      {
          var expression = (MemberExpression)property.Body;
          var propName = expression.Member.Name;
          var isObject = typeof(ClientObject).IsAssignableFrom(property.Body.Type); // test with ClientObject instead of ClientObjectList
          return isObject ? clientObject.IsObjectPropertyInstantiated(propName) : clientObject.IsPropertyAvailable(propName);
      }
      
      

      【讨论】:

        【解决方案4】:

        好的,这变得越来越复杂,尤其是在 SharePoint Online 中,即使没有引发异常,Load 和 Execute 方法的结果也可能不完整。但是,下面是我从这个线程和其他线程中收集的内容,这些线程组合到 LoadAndExecute 方法中,该方法可以是 ClientContext 类的子类扩展,也可以转换为静态扩展类。对于新的客户端对象,对象及其属性在一个操作中加载,但每个属性的结果会单独检查。对于现有的客户端对象,仅在单独的操作中加载缺少的属性,这可能会不必要地消耗网络资源。因此,该方法不仅会检查哪些属性未初始化,还会尝试检索丢失的属性。另外,还有一个主题是通过覆盖ClientContext的Execute方法来避免被限制,但这里不包括:

        /// <summary>
        /// An extended ClientContext to avoid getting throttled.
        /// </summary>
        public partial class OnlineContext : ClientContext
        {
            /// <inheritdoc />
            public OnlineContext(string webFullUrl, int retryCount = 0, int delay = 0)
                : base(webFullUrl)
            {
                RetryCount = retryCount;
                Delay = delay;
            }
        
            /// <summary>
            /// The retry count.
            /// </summary>
            public int RetryCount { get; set; }
        
            /// <summary>
            /// The delay between attempts in seconds.
            /// </summary>
            public int Delay { get; set; }
        
            /// <summary>
            /// Loads and executes the specified client object properties.
            /// </summary>
            /// <typeparam name="T">the object type.</typeparam>
            /// <param name="clientObject">the object.</param>
            /// <param name="properties">the properties.</param>
            /// <returns>true if all available, false otherwise.</returns>
            public bool LoadAndExecute<T>(T clientObject, params Expression<Func<T, object>>[] properties)
                where T : ClientObject
            {
                int retryAttempts = 0;
                int backoffInterval = Math.Max(Delay, 1);
        
                bool retry;
                bool available;
                do
                {
                    if (clientObject is ClientObjectCollection)
                    {
                        // Note that Server Object can be null for collections!
                        ClientObjectCollection coc = (ClientObjectCollection) (ClientObject) clientObject;
                        if (!coc.ServerObjectIsNull.HasValue || !coc.ServerObjectIsNull.Value)
                        {
                            available = coc.AreItemsAvailable;
                        }
                        else
                        {
                            available = false;
                            break;
                        }
                    }
                    else if (clientObject.ServerObjectIsNull.HasValue)
                    {
                        available = !clientObject.ServerObjectIsNull.Value;
                        break;
                    }
                    else
                    {
                        available = false;
                    }
        
                    if (!available && retryAttempts++ <= RetryCount)
                    {
                        if (retryAttempts > 1)
                        {
                            Thread.Sleep(backoffInterval * 1000);
                            backoffInterval *= 2;
                        }
        
                        Load(clientObject, properties);
                        ExecuteQuery();
                        retry = true;
                    }
                    else
                    {
                        retry = false;
                    }
                } while (retry);
        
                if (available)
                {
                    if (properties != null && properties.Length > 0)
                    {
                        foreach (Expression<Func<T, object>> property in properties)
                        {
                            if (!LoadAndExecuteProperty(clientObject, property, retryAttempts > 0))
                            {
                                available = false;
                            }
                        }
                    }
                }
                return available;
            }
        
            /// <summary>
            /// Loads and executes the specified client object property.
            /// </summary>
            /// <typeparam name="T">the object type.</typeparam>
            /// <param name="clientObject">the object.</param>
            /// <param name="property">the property.</param>
            /// <param name="loaded">true, if the client object was already loaded and executed at least once.</param>
            /// <returns>true if available, false otherwise.</returns>
            private bool LoadAndExecuteProperty<T>(T clientObject, Expression<Func<T, object>> property, bool loaded = false)
                where T : ClientObject
            {
                string propertyName;
                bool isObject;
                bool isCollection;
                Func<T, object> func;
                Expression expression = property.Body;
                if (expression is MemberExpression)
                {
                    // Member expression, check its type to select correct property test.
                    propertyName = ((MemberExpression) expression).Member.Name;
                    isObject = typeof(ClientObject).IsAssignableFrom(property.Body.Type);
                    isCollection = isObject
                        ? typeof(ClientObjectCollection).IsAssignableFrom(property.Body.Type)
                        : false;
                    func = isObject ? property.Compile() : null;
                }
                else if (!loaded)
                {
                    // Unary expression or alike, test by invoking its function.
                    propertyName = null;
                    isObject = false;
                    isCollection = false;
                    func = property.Compile();
                }
                else
                {
                    // Unary expression and alike should be available if just loaded.
                    return true;
                }
        
                int retryAttempts = 0;
                int backoffInterval = Math.Max(Delay, 1);
        
                bool retry;
                bool available;
                do
                {
                    if (isObject)
                    {
                        if (clientObject.IsObjectPropertyInstantiated(propertyName))
                        {
                            ClientObject co = (ClientObject) func.Invoke(clientObject);
                            if (isCollection)
                            {
                                ClientObjectCollection coc = (ClientObjectCollection) co;
                                if (!coc.ServerObjectIsNull.HasValue || !coc.ServerObjectIsNull.Value)
                                {
                                    available = coc.AreItemsAvailable;
                                }
                                else
                                {
                                    available = false;
                                    break;
                                }
                            }
                            else if (co.ServerObjectIsNull.HasValue)
                            {
                                available = !co.ServerObjectIsNull.Value;
                                break;
                            }
                            else
                            {
                                available = false;
                            }
                        }
                        else
                        {
                            available = false;
                        }
                    }
                    else if (propertyName != null)
                    {
                        available = clientObject.IsPropertyAvailable(propertyName);
                    }
                    else if (func != null)
                    {
                        try
                        {
                            func.Invoke(clientObject);
                            available = true;
                        }
                        catch (PropertyOrFieldNotInitializedException)
                        {
                            available = false;
                        }
                    }
                    else
                    {
                        available = true; // ?
                    }
        
                    if (!available && retryAttempts++ <= RetryCount)
                    {
                        if (retryAttempts > 1)
                        {
                            Thread.Sleep(backoffInterval * 1000);
                            backoffInterval *= 2;
                        }
        
                        Load(clientObject, property);
                        ExecuteQuery();
                        retry = true;
                    }
                    else
                    {
                        retry = false;
                    }
                } while (retry);
                return available;
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-10-07
          • 2014-08-19
          • 1970-01-01
          • 1970-01-01
          • 2017-07-20
          • 2011-10-12
          • 2015-05-23
          • 2019-01-25
          • 1970-01-01
          相关资源
          最近更新 更多