检测ClientObject属性是否已检索/初始化的正确方法
本文关键字:初始化 方法 检索 ClientObject 属性 是否 检测 | 更新日期: 2023-09-27 18:20:54
如果您使用SharePoint中的客户端对象模型,并访问尚未初始化或已检索到的属性
Context.Load(property);
Context.ExecuteQuery();
例如:
Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException
或
集合尚未初始化。它不是请求或该请求尚未被执行。
例外情况。
在访问这些属性之前,如果它们已经初始化/检索,是否有任何正确的方法进行检查?没有Try/Catch方法。我不喜欢那个。
我想在抛出异常之前进行检查并处理它。
我已经检查了
IsObjectProperty实例化
IsPropertyAvailable
方法,但它们并没有真正的帮助。IsPropertyAvaiable
只检查标量属性,不会给出结果,例如Web.Lists
和IsObjectPropertyInstantiated
对Web.Lists
返回true,尽管Web.Lists
没有初始化。
我想说你的问题在某种程度上已经包含了正确的答案。
为了确定是否加载了客户端对象属性,可以使用以下方法:
- ClientObject.IsPropertyAvailable方法指示已检索或设置指定的标量属性
- ClientObject.IsObjectPropertyInstantated方法指示是否实例化了客户端对象的指定属性
测试
测试用例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.IsObjectPropertyInstantated方法希望将属性名称指定为字符串值,这可能会导致键入错误,因此我通常更喜欢以下扩展方法:
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))
{
//...
}
}
Vadim Gremyachev提供的测试只涵盖了一半的场景,即使用ctx的场景。负载但当你使用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,就无法再查看该集合是否真的可用。
在这种情况下,唯一的方法是检测异常是否发生。
好吧,这变得越来越复杂,尤其是在SharePoint Online中,加载和执行方法的结果可能不完整,即使没有抛出的异常。然而,下面是我从这个和其他线程中收集到的内容,这些线程组合到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;
}
}
使用扩展的想法很好,但只适用于列表。扩展可以在"对象"answers"标量"属性之间进行选择。我认为这种方式会比延期更好:
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);
}