代码之家  ›  专栏  ›  技术社区  ›  Mark

检测ClientObject属性是否已检索/初始化的正确方法

  •  15
  • Mark  · 技术社区  · 10 年前

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

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

    例如:

    Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException

    集合尚未初始化。它不是请求或 请求尚未执行。

    例外

    如果这些财产已经初始化/检索,那么在访问这些属性之前,是否有适当的方法进行检查?没有Try/Catch方法。我不喜欢那个。

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

    我已经检查了

    IsObjectPropertyInstantiated

    IsPropertyAvailable

    方法,但它们实际上没有帮助。 IsPropertyAvaiable 例如,只检查标量财产,不会给出结果 Web.Lists IsObjectPropertyInstantiated 返回true Web列表 虽然 Web列表 未初始化。

    4 回复  |  直到 10 年前
        1
  •  28
  •   Vadim Gremyachev    10 年前

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

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

    测验

    测试用例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.IsPropertyAvailable ClientObject.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))
         {
             //...
         }
    } 
    
        2
  •  1
  •   Thomas Jørgensen    10 年前

    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
  •  1
  •   Ilkka Priha    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;
        }
    }
    
        4
  •  0
  •   Jesús Pazo    6 年前

    使用扩展的想法很好,但只适用于列表。扩展可以在“对象”和“标量”财产之间进行选择。我认为这样做会比扩展更好:

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