/// <summary> /// find all <typeparamref name="TContact"/> by <paramref name="query"/> if <paramref name="query"/> is not null, /// else find all <typeparamref name="TContact"/>s. /// </summary> /// <param name="query"></param> /// <returns></returns> public async Task <IReadOnlyList <TContact> > FindAll([AllowNull] ContactQueryFilter?query = default) { if (Logger.IsEnabled(LogLevel.Trace)) { Logger.LogTrace($"findAll({JsonConvert.SerializeObject(query)})"); } try { var contactIdList = await Puppet.ContactSearch(query); var contactList = contactIdList.Select(id => Load(id)) .ToList(); const int BATCH_SIZE = 16; var batchIndex = 0; var invalid = new ConcurrentDictionary <string, bool>(); while (batchIndex * BATCH_SIZE < contactList.Count) { await Task.WhenAll(contactList.Skip(batchIndex *BATCH_SIZE) .Take(BATCH_SIZE) .Select(async contact => { try { await contact.Ready(); } catch (Exception exception) { invalid.TryAdd(contact.Id, true); Logger.LogError(exception, "findAll() contact.ready() failed."); } })); batchIndex++; } //return contactList.Where(c => invalid.ContainsKey(c.Id)) // .ToImmutableList(); return(contactList.ToImmutableList()); } catch (Exception exception) { Logger.LogError(exception, "this.puppet.contactFindAll() rejected"); return(Array.Empty <TContact>()); } }