/// <summary> /// THE REAL PROPER CHAINING ENDPOINT! Will perform ONE chain request (after linking with previous chains) /// </summary> /// <param name="data"></param> /// <param name="baggage"></param> /// <typeparam name="S"></typeparam> /// <typeparam name="V"></typeparam> /// <returns></returns> public async Task ChainAsync <S, V>(ChainRequest <S, V> data, List <List <IIdView> > previousChains) where V : IIdView where S : IConstrainedSearcher { Dictionary <string, PropertyInfo> properties = null; Type type = typeof(V); //My poor design has led to this... if (type == typeof(UserViewFull)) { type = typeof(UserViewBasic); } var baseProperties = GetProperties(type); //Before doing ANYTHING, IMMEDIATELY convert fields to actual properties. It's easy if they pass us null: they want everything. if (data.fields == null) { properties = baseProperties.ToDictionary(x => x.Name, x => x); } else { var lowerFields = data.fields.Select(x => x.ToLower()); properties = baseProperties.Where(x => lowerFields.Contains(x.Name.ToLower())).ToDictionary(x => x.Name, x => x); if (properties.Count != data.fields.Count) { throw new BadRequestException($"Unknown fields in list: {string.Join(",", data.fields)}"); } } //Parse the chains, get the ids. WARN: THIS IS DESTRUCTIVE TO DATA.BASESEARCH!!! foreach (var c in data.chains) { LinkToSearch(c, previousChains, data.baseSearch); } var myResults = await data.retriever(data.baseSearch); previousChains.Add(myResults.Cast <IIdView>().ToList()); //Only add ones that aren't in the list foreach (var v in myResults) { if (!data.mergeList.Any(x => x.id == v.id)) { var result = new TaggedChainResult() { id = v.id, result = new ExpandoObject() }; foreach (var p in properties) { result.result.TryAdd(p.Key, p.Value.GetValue(v)); } lock (data.mergeLock) { data.mergeList.Add(result); } } } }